diff options
author | Bilal Akhtar <bilalakhtar@ubuntu.com> | 2011-05-03 10:34:47 +0530 |
---|---|---|
committer | Bilal Akhtar <bilalakhtar@ubuntu.com> | 2011-05-03 10:34:47 +0530 |
commit | 95ef69053691c5c731d0a6538de118ea52a3cd0a (patch) | |
tree | 60a79db75770323a8af7783831aad06f6aef0a01 /src | |
parent | 2debbc92898682dfe0c81f1ce4999175887ec922 (diff) |
Imported Upstream version 2.5.0
Diffstat (limited to 'src')
183 files changed, 6837 insertions, 17519 deletions
diff --git a/src/audacious/Makefile b/src/audacious/Makefile index ccca7e5..8beefdc 100644 --- a/src/audacious/Makefile +++ b/src/audacious/Makefile @@ -1,8 +1,6 @@ include ../../extra.mk -SUBDIRS = ${INTL_OBJECTIVE} - -PROG = audacious2${PROG_SUFFIX} +PROG = audacious${PROG_SUFFIX} SRCS = audconfig.c \ chardet.c \ configdb.c \ @@ -13,19 +11,23 @@ SRCS = audconfig.c \ equalizer_preset.c \ fft.c \ folder-add.c \ - general.c \ + general.c \ interface.c \ main.c \ + mpris-signals.c \ output.c \ playback.c \ - playlist_container.c \ + playlist-files.c \ playlist-new.c \ playlist-utils.c \ pluginenum.c \ plugin-registry.c \ + plugin-init.c \ + plugin-view.c \ probe.c \ probe-buffer.c \ signals.c \ + smclient.c \ ui_plugin_menu.c \ ui_preferences.c \ util.c \ @@ -50,6 +52,8 @@ INCLUDES = api.h \ debug.h \ drct.h \ drct-api.h \ + glib-compat.h \ + gtk-compat.h \ i18n.h \ interface.h \ misc.h \ @@ -63,6 +67,7 @@ INCLUDES = api.h \ types.h DATA = images/about-logo.png \ + images/album.png \ images/appearance.png \ images/audacious_eq.xpm \ images/audacious_player.xpm \ @@ -73,7 +78,6 @@ DATA = images/about-logo.png \ images/menu_playlist.png \ images/menu_plugin.png \ images/menu_queue_toggle.png \ - images/playback.png \ images/playlist.png \ images/plugins.png \ images/replay_gain.png @@ -106,13 +110,13 @@ CPPFLAGS += -std=gnu99 \ ${ARCH_DEFINES} \ ${DBUS_CFLAGS} \ ${REGEX_CFLAGS} \ + ${PTHREAD_CFLAGS} \ ${LIBMCS_CFLAGS} \ ${SIMD_CFLAGS} \ -D_AUDACIOUS_CORE \ ${EGGSM_CFLAGS} \ ${LIBGUESS_CFLAGS} \ - -I.. -I../.. \ - -I./intl + -I.. -I../.. LIBS += ${LDADD} \ -lm \ @@ -128,6 +132,7 @@ LIBS += ${LDADD} \ ${MOWGLI_LIBS} \ ${LIBMCS_LIBS} \ ${REGEX_LIBS} \ + ${PTHREAD_LIBS} \ ${LIBGUESS_LIBS} LDFLAGS += ${PROG_IMPLIB_LDFLAGS} ${AUDLDFLAGS} @@ -150,9 +155,6 @@ dbus-client-bindings.h: ${DBUS_BINDINGS_SOURCES} ${DBUS_BINDING_TOOL} --mode=glib-client --prefix=audacious_rc objects.xml > $@ install-extra: - if test -h "${DESTDIR}${bindir}/audacious" ; then ${RM} "${DESTDIR}${bindir}/audacious" ; fi - ${LN_S} audacious2 "${DESTDIR}${bindir}/audacious" - if test x"${PROG_IMPLIB_NEEDED}" = x"yes"; then \ for i in ${PROG}; do \ i="lib$$i.a"; \ @@ -166,8 +168,6 @@ install-extra: fi uninstall-extra: - if test -h "${DESTDIR}${bindir}/audacious" ; then ${RM} "${DESTDIR}${bindir}/audacious" ; fi - if test x"${PROG_IMPLIB_NEEDED}" = x"yes"; then \ for i in ${PROG}; do \ i="lib$$i.a"; \ diff --git a/src/audacious/audconfig.c b/src/audacious/audconfig.c index 15f868d..3556c37 100644 --- a/src/audacious/audconfig.c +++ b/src/audacious/audconfig.c @@ -26,20 +26,9 @@ #include <glib.h> #include <libaudcore/hook.h> -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - #include "audconfig.h" #include "configdb.h" -#include "effect.h" -#include "general.h" -#include "output.h" #include "playback.h" -#include "pluginenum.h" -#include "plugins.h" -#include "util.h" -#include "visualization.h" AudConfig cfg = { .shuffle = FALSE, @@ -50,7 +39,11 @@ AudConfig cfg = { .equalizer_visible = FALSE, .player_visible = TRUE, .show_numbers_in_pl = TRUE, + .leading_zero = TRUE, .no_playlist_advance = FALSE, + .advance_on_delete = FALSE, + .clear_playlist = TRUE, + .open_to_temporary = FALSE, .stopaftersong = FALSE, .close_dialog_open = TRUE, .equalizer_preamp = 0.0, @@ -87,11 +80,6 @@ AudConfig cfg = { .replay_gain_preamp = 0, .default_gain = 0, .sw_volume_left = 100, .sw_volume_right = 100, - .clear_playlist = TRUE, - .output_path = NULL, - .output_number = -1, - .iface_path = NULL, - .iface_number = -1, /* libaudgui stuff */ .no_confirm_playlist_delete = FALSE, @@ -125,7 +113,11 @@ typedef struct aud_cfg_strent_t { static aud_cfg_boolent aud_boolents[] = { {"show_numbers_in_pl", &cfg.show_numbers_in_pl, TRUE}, + {"leading_zero", & cfg.leading_zero, TRUE}, {"no_playlist_advance", &cfg.no_playlist_advance, TRUE}, + {"advance_on_delete", & cfg.advance_on_delete, TRUE}, + {"clear_playlist", & cfg.clear_playlist, TRUE}, + {"open_to_temporary", & cfg.open_to_temporary, TRUE}, {"player_visible", &cfg.player_visible, TRUE}, {"shuffle", &cfg.shuffle, TRUE}, {"repeat", &cfg.repeat, TRUE}, @@ -147,7 +139,6 @@ static aud_cfg_boolent aud_boolents[] = { {"enable_clipping_prevention", &cfg.enable_clipping_prevention, TRUE}, {"replay_gain_track", &cfg.replay_gain_track, TRUE}, {"replay_gain_album", &cfg.replay_gain_album, TRUE}, - {"clear_playlist", &cfg.clear_playlist, TRUE}, {"no_confirm_playlist_delete", &cfg.no_confirm_playlist_delete, TRUE}, {"playlist_manager_close_on_activate", & cfg.playlist_manager_close_on_activate, TRUE}, @@ -166,8 +157,6 @@ static aud_cfg_nument aud_numents[] = { {"output_bit_depth", &cfg.output_bit_depth, TRUE}, {"sw_volume_left", & cfg.sw_volume_left, TRUE}, {"sw_volume_right", & cfg.sw_volume_right, TRUE}, - {"output_number", & cfg.output_number, TRUE}, - {"iface_number", & cfg.iface_number, TRUE}, {"playlist_manager_x", & cfg.playlist_manager_x, TRUE}, {"playlist_manager_y", & cfg.playlist_manager_y, TRUE}, {"playlist_manager_width", & cfg.playlist_manager_width, TRUE}, @@ -186,25 +175,10 @@ static aud_cfg_strent aud_strents[] = { {"chardet_fallback", &cfg.chardet_fallback, TRUE}, {"cover_name_include", &cfg.cover_name_include, TRUE}, {"cover_name_exclude", &cfg.cover_name_exclude, TRUE}, - {"output_path", & cfg.output_path, TRUE}, - {"iface_path", & cfg.iface_path, TRUE}, }; static gint ncfgsent = G_N_ELEMENTS(aud_strents); -void -aud_config_free(void) -{ - gint i; - for (i = 0; i < ncfgsent; ++i) { - if ( *(aud_strents[i].se_vloc) != NULL ) - { - g_free( *(aud_strents[i].se_vloc) ); - *(aud_strents[i].se_vloc) = NULL; - } - } -} - void aud_config_chardet_update(void) { if (cfg.chardet_fallback_s != NULL) @@ -219,7 +193,9 @@ aud_config_load(void) mcs_handle_t *db; gint i, length; - db = cfg_db_open(); + if (! (db = cfg_db_open ())) + return; + for (i = 0; i < ncfgbent; ++i) { cfg_db_get_bool(db, NULL, aud_boolents[i].be_vname, @@ -276,26 +252,12 @@ aud_config_load(void) aud_config_chardet_update(); if (!cfg.cover_name_include) - cfg.cover_name_include = g_strdup(""); + cfg.cover_name_include = g_strdup("album,folder"); if (!cfg.cover_name_exclude) cfg.cover_name_exclude = g_strdup("back"); } -static void save_output_path (void) -{ - const gchar * path = NULL; - gint type, number = -1; - - if (current_output_plugin != NULL) - plugin_get_path (plugin_by_header (current_output_plugin), & path, - & type, & number); - - g_free (cfg.output_path); - cfg.output_path = (path != NULL) ? g_strdup (path) : NULL; - cfg.output_number = number; -} - void aud_config_save(void) { @@ -311,9 +273,8 @@ aud_config_save(void) cfg.resume_playback_on_startup_time = playback_get_playing () ? playback_get_time () : 0; - save_output_path (); - - db = cfg_db_open(); + if (! (db = cfg_db_open ())) + return; for (i = 0; i < ncfgbent; ++i) if (aud_boolents[i].be_wrt) diff --git a/src/audacious/audconfig.h b/src/audacious/audconfig.h index 0995bbb..cf38773 100644 --- a/src/audacious/audconfig.h +++ b/src/audacious/audconfig.h @@ -38,8 +38,9 @@ struct _AudConfig { gboolean shuffle, repeat; gboolean equalizer_autoload, equalizer_active; gboolean playlist_visible, equalizer_visible, player_visible; - gboolean show_numbers_in_pl; - gboolean no_playlist_advance; + gboolean show_numbers_in_pl, leading_zero; + gboolean no_playlist_advance, advance_on_delete, clear_playlist, + open_to_temporary; gboolean stopaftersong; gboolean close_dialog_open; gfloat equalizer_preamp, equalizer_bands[AUD_EQUALIZER_NBANDS]; @@ -50,7 +51,6 @@ struct _AudConfig { gint titlestring_preset; gchar *gentitle_format; gboolean resume_playback_on_startup; - gint unused, unused2; /* for compatibility with v2.3 binary API */ gint resume_state; gint resume_playback_on_startup_time; gchar *chardet_detector; @@ -76,11 +76,6 @@ struct _AudConfig { gfloat replay_gain_preamp; gfloat default_gain; gint sw_volume_left, sw_volume_right; - gboolean clear_playlist; - gchar * output_path; - gint output_number; - gchar * iface_path; - gint iface_number; /* libaudgui stuff */ gboolean no_confirm_playlist_delete; @@ -97,7 +92,6 @@ typedef struct _AudConfig AudConfig; extern AudConfig cfg; extern AudConfig aud_default_config; -void aud_config_free(void); void aud_config_load(void); void aud_config_save(void); diff --git a/src/audacious/chardet.c b/src/audacious/chardet.c index 7723b32..9010db2 100644 --- a/src/audacious/chardet.c +++ b/src/audacious/chardet.c @@ -21,18 +21,31 @@ #include <libaudcore/audstrings.h> #include "audconfig.h" -#include "chardet.h" #include "config.h" #include "i18n.h" -#include "main.h" #include "debug.h" #ifdef USE_CHARDET # include <libguess.h> #endif -gchar * -cd_str_to_utf8(const gchar * str) +static gchar * cd_chardet_to_utf8 (const gchar * str, gssize len, + gsize * arg_bytes_read, gsize * arg_bytes_write, GError ** error); + +static gchar * str_to_utf8_fallback (const gchar * str) +{ + gchar * out = g_strconcat (str, _(" (invalid UTF-8)"), NULL); + + for (gchar * c = out; * c; c ++) + { + if (* c & 0x80) + * c = '?'; + } + + return out; +} + +static gchar * cd_str_to_utf8 (const gchar * str) { gchar *out_str; @@ -78,9 +91,8 @@ cd_str_to_utf8(const gchar * str) return str_to_utf8_fallback(str); } -gchar * -cd_chardet_to_utf8(const gchar * str, gssize len, gsize * arg_bytes_read, - gsize * arg_bytes_write, GError ** error) +static gchar * cd_chardet_to_utf8 (const gchar * str, gssize len, + gsize * arg_bytes_read, gsize * arg_bytes_write, GError ** error) { if (error) * error = NULL; @@ -178,9 +190,7 @@ fallback: return NULL; /* If we have no idea, return NULL. */ } - -void chardet_init(void) +void chardet_init (void) { - str_to_utf8 = cd_str_to_utf8; - chardet_to_utf8 = cd_chardet_to_utf8; + str_set_utf8_impl (cd_str_to_utf8, cd_chardet_to_utf8); } diff --git a/src/audacious/compatibility.h b/src/audacious/compatibility.h deleted file mode 100644 index 6ea287c..0000000 --- a/src/audacious/compatibility.h +++ /dev/null @@ -1,26 +0,0 @@ -#if ! GLIB_CHECK_VERSION (2, 14, 0) -#define G_QUEUE_INIT {NULL, NULL, 0} -#define g_queue_clear(q) do {g_list_free ((q)->head); (q)->head = NULL; (q)->tail = NULL; (q)->length = 0;} while (0) -#define g_timeout_add_seconds(s, f, d) g_timeout_add (1000 * (s), (f), (d)) -#endif - -#ifdef GTK_CHECK_VERSION /* GTK headers included? */ - -#if ! GTK_CHECK_VERSION (2, 10, 0) -#define GDK_WINDOW_TYPE_HINT_TOOLTIP GDK_WINDOW_TYPE_HINT_MENU -#endif - -#if ! GTK_CHECK_VERSION (2, 12, 0) -#define gtk_widget_set_tooltip_text(...) -#endif - -#if ! GTK_CHECK_VERSION (2, 14, 0) -#define gtk_widget_get_window(w) ((w)->window) -#endif - -#if ! GTK_CHECK_VERSION (2, 18, 0) -#define gtk_widget_set_can_default(w, t) do {if (t) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT); else GTK_WIDGET_UNSET_FLAGS ((w), GTK_CAN_DEFAULT);} while (0) -#define gtk_widget_set_can_focus(w, t) do {if (t) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_FOCUS); else GTK_WIDGET_UNSET_FLAGS ((w), GTK_CAN_FOCUS);} while (0) -#endif - -#endif diff --git a/src/audacious/configdb.c b/src/audacious/configdb.c index da78137..dd9e68d 100644 --- a/src/audacious/configdb.c +++ b/src/audacious/configdb.c @@ -46,8 +46,16 @@ cfg_db_open() } if (! config_handle) + { config_handle = mcs_new (RCFILE_DEFAULT_SECTION_NAME); + if (! config_handle) + { + fprintf (stderr, "MCS failure. Configuration will not be saved.\n"); + return NULL; + } + } + config_refcount ++; return config_handle; } @@ -58,7 +66,7 @@ cfg_db_open() */ void cfg_db_close (mcs_handle_t * handle) { - g_return_if_fail (handle == config_handle); + g_return_if_fail (handle && handle == config_handle); g_return_if_fail (config_refcount > 0); config_refcount --; } @@ -88,6 +96,8 @@ cfg_db_get_string(mcs_handle_t * db, const gchar * key, gchar ** value) { + g_return_val_if_fail (db && db == config_handle, FALSE); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -117,6 +127,8 @@ gboolean cfg_db_get_int(mcs_handle_t * db, const gchar * section, const gchar * key, gint * value) { + g_return_val_if_fail (db && db == config_handle, FALSE); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -138,6 +150,8 @@ cfg_db_get_bool(mcs_handle_t * db, const gchar * key, gboolean * value) { + g_return_val_if_fail (db && db == config_handle, FALSE); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -160,6 +174,8 @@ cfg_db_get_float(mcs_handle_t * db, const gchar * key, gfloat * value) { + g_return_val_if_fail (db && db == config_handle, FALSE); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -182,6 +198,8 @@ cfg_db_get_double(mcs_handle_t * db, const gchar * key, gdouble * value) { + g_return_val_if_fail (db && db == config_handle, FALSE); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -203,6 +221,8 @@ cfg_db_set_string(mcs_handle_t * db, const gchar * key, const gchar * value) { + g_return_if_fail (db && db == config_handle); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -227,6 +247,8 @@ cfg_db_set_int(mcs_handle_t * db, const gchar * key, gint value) { + g_return_if_fail (db && db == config_handle); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -248,6 +270,8 @@ cfg_db_set_bool(mcs_handle_t * db, const gchar * key, gboolean value) { + g_return_if_fail (db && db == config_handle); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -269,6 +293,8 @@ cfg_db_set_float(mcs_handle_t * db, const gchar * key, gfloat value) { + g_return_if_fail (db && db == config_handle); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -290,6 +316,8 @@ cfg_db_set_double(mcs_handle_t * db, const gchar * key, gdouble value) { + g_return_if_fail (db && db == config_handle); + if (!section) section = RCFILE_DEFAULT_SECTION_NAME; @@ -308,7 +336,7 @@ cfg_db_unset_key(mcs_handle_t * db, const gchar * section, const gchar * key) { - g_return_if_fail(db != NULL); + g_return_if_fail (db && db == config_handle); g_return_if_fail(key != NULL); if (!section) diff --git a/src/audacious/credits.c b/src/audacious/credits.c index 74fbe34..436c1d0 100644 --- a/src/audacious/credits.c +++ b/src/audacious/credits.c @@ -28,9 +28,8 @@ #include "misc.h" static const gchar *audacious_brief = - N_("<big><b>Audacious %s</b></big>\n" - "An audio player for many platforms.\n" - "Copyright (C) 2005-2011 Audacious Development Team"); + "<big><b>Audacious %s</b></big>\n" + "Copyright (C) 2005-2011 Audacious Team"; static const gchar *credit_text[] = { N_("Core developers:"), @@ -105,6 +104,7 @@ static const gchar *credit_text[] = { "Alex Maclean", "Mikael Magnusson", "Rodrigo Martins de Matos Ventura", + "Mihai Maruseac", "Diego Pettenò", "Mike Ryan", "Michael Schwendt", @@ -112,11 +112,13 @@ static const gchar *credit_text[] = { "Kirill Shendrikowski", "Kazuki Shimura", "Valentine Sinitsyn", + "Will Storey", "Johan Tavelin", "Christoph J. Thompson", "Bret Towe", "Peter Wagner", "John Wehle", + "Ben Wolfson", "Tim Yamin", "Ivan N. Zlatev", NULL, @@ -251,6 +253,9 @@ static const gchar *translators_text[] = { N_("Korean:"), "DongCheon Park", NULL, + N_("Latvian:"), + "Einars Sprugis", + NULL, N_("Lithuanian:"), "Paul Daukas", "Rimas Kudelis", @@ -291,6 +296,7 @@ static const gchar *translators_text[] = { "Jeki Sinneo Leinos", "Francisco Javier F. Serrador", "Gustavo D. Vranjes", + "Jorge Andrés", NULL, N_("Swedish:"), "Martin Persenius", diff --git a/src/audacious/dbus-service.h b/src/audacious/dbus-service.h index 581556a..31c947d 100644 --- a/src/audacious/dbus-service.h +++ b/src/audacious/dbus-service.h @@ -59,6 +59,8 @@ typedef enum { MPRIS_STATUS_STOP } PlaybackStatus; +extern MprisPlayer * mpris; + // MPRIS / gboolean mpris_root_identity(MprisRoot *obj, gchar **identity, GError **error); diff --git a/src/audacious/dbus.c b/src/audacious/dbus.c index 5e71a97..a4549d7 100644 --- a/src/audacious/dbus.c +++ b/src/audacious/dbus.c @@ -37,7 +37,6 @@ #include "debug.h" #include "drct.h" #include "equalizer.h" -#include "main.h" #include "playback.h" #include "playlist.h" #include "interface.h" @@ -90,6 +89,8 @@ static DBusGConnection *dbus_conn = NULL; static guint signals[LAST_SIG] = { 0 }; static guint tracklist_signals[LAST_TRACKLIST_SIG] = { 0 }; +MprisPlayer * mpris = NULL; + static GThread *main_thread; static GMutex *info_mutex; static GCond *info_cond; @@ -772,38 +773,13 @@ gboolean mpris_player_get_caps(MprisPlayer * obj, gint * capabilities, GError ** gboolean mpris_player_volume_set(MprisPlayer * obj, gint vol, GError ** error) { - gint vl, vr, v; - - // get the current volume so we can maintain the balance - input_get_volume(&vl, &vr); - - // sanity check - vl = CLAMP(vl, 0, 100); - vr = CLAMP(vr, 0, 100); - v = CLAMP(vol, 0, 100); - - if (vl > vr) - { - input_set_volume(v, (gint) rint(((gdouble) vr / vl) * v)); - } - else if (vl < vr) - { - input_set_volume((gint) rint(((gdouble) vl / vr) * v), v); - } - else - { - input_set_volume(v, v); - } + drct_set_volume_main (vol); return TRUE; } gboolean mpris_player_volume_get(MprisPlayer * obj, gint * vol, GError ** error) { - gint vl, vr; - input_get_volume(&vl, &vr); - // vl and vr may be different depending on the balance; the true volume is - // the maximum of vl or vr. - *vol = MAX(vl, vr); + drct_get_volume_main (vol); return TRUE; } @@ -1095,32 +1071,19 @@ gboolean audacious_rc_seek(RemoteObject * obj, guint pos, GError * *error) gboolean audacious_rc_volume(RemoteObject * obj, gint * vl, gint * vr, GError ** error) { - input_get_volume(vl, vr); + drct_get_volume (vl, vr); return TRUE; } gboolean audacious_rc_set_volume(RemoteObject * obj, gint vl, gint vr, GError ** error) { - if (vl > 100) - vl = 100; - if (vr > 100) - vr = 100; - input_set_volume(vl, vr); + drct_set_volume (vl, vr); return TRUE; } gboolean audacious_rc_balance(RemoteObject * obj, gint * balance, GError ** error) { - gint vl, vr; - input_get_volume(&vl, &vr); - if (vl < 0 || vr < 0) - *balance = 0; - else if (vl > vr) - *balance = -100 + ((vr * 100) / vl); - else if (vr > vl) - *balance = 100 - ((vl * 100) / vr); - else - *balance = 0; + drct_get_volume_balance (balance); return TRUE; } diff --git a/src/audacious/drct-api.h b/src/audacious/drct-api.h index 41058f4..8b78301 100644 --- a/src/audacious/drct-api.h +++ b/src/audacious/drct-api.h @@ -1,6 +1,6 @@ /* * drct-api.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * This file is part of Audacious. * @@ -75,6 +75,7 @@ AUD_FUNC1 (void, drct_pl_open_temp, const gchar *, filename) AUD_FUNC1 (void, drct_pl_open_temp_list, GList *, list) AUD_FUNC1 (void, drct_pl_delete, gint, entry) +AUD_FUNC0 (void, drct_pl_delete_selected) AUD_FUNC0 (void, drct_pl_clear) /* --- PLAYLIST QUEUE CONTROL --- */ @@ -86,3 +87,6 @@ AUD_FUNC1 (gint, drct_pq_get_queue_position, gint, entry) AUD_FUNC1 (void, drct_pq_add, gint, entry) AUD_FUNC1 (void, drct_pq_remove, gint, entry) AUD_FUNC0 (void, drct_pq_clear) + +/* New in 2.5-alpha2 */ +AUD_FUNC0 (gboolean, drct_get_ready) diff --git a/src/audacious/drct.c b/src/audacious/drct.c index 95ba3e3..764295f 100644 --- a/src/audacious/drct.c +++ b/src/audacious/drct.c @@ -1,6 +1,6 @@ /* * drct.c - * Copyright 2009-2010 John Lindgren + * Copyright 2009-2011 John Lindgren * * This file is part of Audacious. * @@ -24,11 +24,10 @@ #include <libaudcore/vfs.h> #include "audconfig.h" -#include "compatibility.h" #include "config.h" #include "drct.h" +#include "glib-compat.h" #include "i18n.h" -#include "main.h" #include "playback.h" #include "playlist.h" @@ -36,7 +35,7 @@ void drct_quit (void) { - aud_quit (); + hook_call ("quit", NULL); } /* --- PLAYBACK CONTROL --- */ @@ -71,6 +70,11 @@ gboolean drct_get_playing (void) return playback_get_playing (); } +gboolean drct_get_ready (void) +{ + return playback_get_ready (); +} + gboolean drct_get_paused (void) { return playback_get_paused (); @@ -105,14 +109,14 @@ void drct_seek (gint time) void drct_get_volume (gint * left, gint * right) { - input_get_volume (left, right); + playback_get_volume (left, right); * left = CLAMP (* left, 0, 100); * right = CLAMP (* right, 0, 100); } void drct_set_volume (gint left, gint right) { - input_set_volume (CLAMP (left, 0, 100), CLAMP (right, 0, 100)); + playback_set_volume (CLAMP (left, 0, 100), CLAMP (right, 0, 100)); } void drct_get_volume_main (gint * volume) @@ -244,8 +248,35 @@ gint drct_pl_get_time (gint pos) return playlist_entry_get_length (playlist_get_active (), pos, FALSE); } -static void add_list (GList * list, gint at, gboolean play) +static void activate_temp (void) +{ + gint playlists = playlist_count (); + const gchar * title = _("Temporary Playlist"); + + for (gint playlist = 0; playlist < playlists; playlist ++) + { + if (! strcmp (playlist_get_title (playlist), title)) + { + playlist_set_active (playlist); + return; + } + } + + if (! playlist_entry_count (playlist_get_active ())) + playlist_set_title (playlist_get_active (), title); + else + { + playlist_insert (playlists); + playlist_set_title (playlists, title); + playlist_set_active (playlists); + } +} + +static void add_list (GList * list, gint at, gboolean to_temp, gboolean play) { + if (to_temp) + activate_temp (); + gint playlist = playlist_get_active (); if (play) @@ -301,56 +332,37 @@ static void add_list (GList * list, gint at, gboolean play) void drct_pl_add (const gchar * filename, gint at) { GList * list = g_list_prepend (NULL, (void *) filename); - add_list (list, at, FALSE); + add_list (list, at, FALSE, FALSE); g_list_free (list); } void drct_pl_add_list (GList * list, gint at) { - add_list (list, at, FALSE); + add_list (list, at, FALSE, FALSE); } void drct_pl_open (const gchar * filename) { GList * list = g_list_prepend (NULL, (void *) filename); - add_list (list, -1, TRUE); + add_list (list, -1, cfg.open_to_temporary, TRUE); g_list_free (list); } void drct_pl_open_list (GList * list) { - add_list (list, -1, TRUE); -} - -static void activate_temp (void) -{ - gint playlists = playlist_count (); - const gchar * title = _("Temporary Playlist"); - - for (gint playlist = 0; playlist < playlists; playlist ++) - { - if (! strcmp (playlist_get_title (playlist), title)) - { - playlist_set_active (playlist); - return; - } - } - - playlist_insert (playlists); - playlist_set_title (playlists, title); - playlist_set_active (playlists); + add_list (list, -1, cfg.open_to_temporary, TRUE); } void drct_pl_open_temp (const gchar * filename) { - activate_temp (); - drct_pl_open (filename); + GList * list = g_list_prepend (NULL, (void *) filename); + add_list (list, -1, TRUE, TRUE); + g_list_free (list); } void drct_pl_open_temp_list (GList * list) { - activate_temp (); - drct_pl_open_list (list); + add_list (list, -1, TRUE, TRUE); } void drct_pl_delete (gint entry) @@ -358,6 +370,34 @@ void drct_pl_delete (gint entry) playlist_entry_delete (playlist_get_active (), entry, 1); } +/* Advancing to the next song when the current one is deleted is tricky. First, + * we delete all the selected songs except the current one. We can then advance + * to a new song without worrying about picking one that is also selected. + * Finally, we can delete the former current song without stopping playback. */ + +void drct_pl_delete_selected (void) +{ + gint list = playlist_get_active (); + gint pos = playlist_get_position (list); + + if (cfg.advance_on_delete && ! cfg.no_playlist_advance + && playback_get_playing () && list == playlist_get_playing () + && pos >= 0 && playlist_entry_get_selected (list, pos)) + { + playlist_entry_set_selected (list, pos, FALSE); + playlist_delete_selected (list); + pos = playlist_get_position (list); /* it may have moved */ + + if (playlist_next_song (list, cfg.repeat) + && playlist_get_position (list) != pos) + playback_play (0, FALSE); + + playlist_entry_delete (list, pos, 1); + } + else + playlist_delete_selected (list); +} + void drct_pl_clear (void) { gint playlist = playlist_get_active (); diff --git a/src/audacious/effect.c b/src/audacious/effect.c index 1998e7a..f407099 100644 --- a/src/audacious/effect.c +++ b/src/audacious/effect.c @@ -23,7 +23,6 @@ #include "debug.h" #include "effect.h" -#include "output.h" #include "playback.h" #include "plugin.h" #include "plugins.h" @@ -35,7 +34,8 @@ typedef struct { gboolean remove_flag; } RunningEffect; -static GList * running_effects = NULL; +static GStaticMutex mutex = G_STATIC_MUTEX_INIT; +static GList * running_effects = NULL; /* (RunningEffect *) */ static gint input_channels, input_rate; typedef struct { @@ -63,6 +63,8 @@ static gboolean effect_start_cb (PluginHandle * plugin, EffectStartState * state void effect_start (gint * channels, gint * rate) { + g_static_mutex_lock (& mutex); + AUDDBG ("Starting effects.\n"); g_list_foreach (running_effects, (GFunc) g_free, NULL); g_list_free (running_effects); @@ -75,10 +77,11 @@ void effect_start (gint * channels, gint * rate) plugin_for_enabled (PLUGIN_TYPE_EFFECT, (PluginForEachFunc) effect_start_cb, & state); running_effects = g_list_reverse (running_effects); + + g_static_mutex_unlock (& mutex); } -typedef struct -{ +typedef struct { gfloat * * data; gint * samples; } EffectProcessState; @@ -100,36 +103,56 @@ static void effect_process_cb (RunningEffect * effect, EffectProcessState * void effect_process (gfloat * * data, gint * samples) { + g_static_mutex_lock (& mutex); + EffectProcessState state = {data, samples}; g_list_foreach (running_effects, (GFunc) effect_process_cb, & state); + + g_static_mutex_unlock (& mutex); } void effect_flush (void) { + g_static_mutex_lock (& mutex); + for (GList * node = running_effects; node != NULL; node = node->next) ((RunningEffect *) node->data)->header->flush (); + + g_static_mutex_unlock (& mutex); } void effect_finish (gfloat * * data, gint * samples) { + g_static_mutex_lock (& mutex); + for (GList * node = running_effects; node != NULL; node = node->next) ((RunningEffect *) node->data)->header->finish (data, samples); + + g_static_mutex_unlock (& mutex); } gint effect_decoder_to_output_time (gint time) { + g_static_mutex_lock (& mutex); + for (GList * node = running_effects; node != NULL; node = node->next) time = ((RunningEffect *) node->data)->header->decoder_to_output_time (time); + + g_static_mutex_unlock (& mutex); return time; } gint effect_output_to_decoder_time (gint time) { + g_static_mutex_lock (& mutex); + for (GList * node = g_list_last (running_effects); node != NULL; node = node->prev) time = ((RunningEffect *) node->data)->header->output_to_decoder_time (time); + + g_static_mutex_unlock (& mutex); return time; } @@ -193,26 +216,48 @@ static void effect_remove (PluginHandle * plugin) ((RunningEffect *) node->data)->remove_flag = TRUE; } -void effect_plugin_enable (PluginHandle * plugin, gboolean enable) +static void effect_enable (PluginHandle * plugin, EffectPlugin * ep, gboolean + enable) { - plugin_set_enabled (plugin, enable); + if (ep->preserves_format) + { + g_static_mutex_lock (& mutex); + + if (enable) + effect_insert (plugin, ep); + else + effect_remove (plugin); + g_static_mutex_unlock (& mutex); + } + else + { + AUDDBG ("Reset to add/remove %s.\n", plugin_get_name (plugin)); + gint time = playback_get_time (); + gboolean paused = playback_get_paused (); + playback_stop (); + playback_play (time, paused); + } +} + +gboolean effect_plugin_start (PluginHandle * plugin) +{ if (playback_get_playing ()) { - EffectPlugin * header = plugin_get_header (plugin); - g_return_if_fail (header != NULL); - - if (header->preserves_format) - { - if (enable) - effect_insert (plugin, header); - else - effect_remove (plugin); - } - else - { - AUDDBG ("Reset to add/remove %s.\n", plugin_get_name (plugin)); - set_current_output_plugin (current_output_plugin); - } + EffectPlugin * ep = plugin_get_header (plugin); + g_return_val_if_fail (ep != NULL, FALSE); + effect_enable (plugin, ep, TRUE); + } + + return TRUE; +} + +void effect_plugin_stop (PluginHandle * plugin) +{ + if (playback_get_playing ()) + { + EffectPlugin * ep = plugin_get_header (plugin); + g_return_if_fail (ep != NULL); + effect_enable (plugin, ep, FALSE); } } diff --git a/src/audacious/effect.h b/src/audacious/effect.h index 701c55b..14afce4 100644 --- a/src/audacious/effect.h +++ b/src/audacious/effect.h @@ -25,6 +25,8 @@ #include <glib.h> +#include "types.h" + void effect_start (gint * channels, gint * rate); void effect_process (gfloat * * data, gint * samples); void effect_flush (void); @@ -32,4 +34,7 @@ void effect_finish (gfloat * * data, gint * samples); gint effect_decoder_to_output_time (gint time); gint effect_output_to_decoder_time (gint time); +gboolean effect_plugin_start (PluginHandle * plugin); +void effect_plugin_stop (PluginHandle * plugin); + #endif diff --git a/src/audacious/equalizer_preset.c b/src/audacious/equalizer_preset.c index 52605b3..34a564b 100644 --- a/src/audacious/equalizer_preset.c +++ b/src/audacious/equalizer_preset.c @@ -23,7 +23,6 @@ #include "debug.h" #include "i18n.h" #include "interface.h" -#include "main.h" #include "misc.h" static EqualizerPreset * equalizer_preset_new (const gchar * name) @@ -43,13 +42,14 @@ equalizer_read_presets(const gchar *basename) gint i, p = 0; EqualizerPreset *preset; - filename = g_build_filename(aud_paths[BMP_PATH_USER_DIR], basename, NULL); + filename = g_build_filename (get_path (AUD_PATH_USER_DIR), basename, NULL); rcfile = g_key_file_new(); if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, &error)) { g_free(filename); - filename = g_build_filename(DATA_DIR, basename, NULL); + filename = g_build_filename (get_path (AUD_PATH_DATA_DIR), basename, + NULL); error = NULL; if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, &error)) @@ -131,8 +131,7 @@ gboolean equalizer_write_preset_file (GList * list, const gchar * basename) } } - - filename = g_build_filename(aud_paths[BMP_PATH_USER_DIR], basename, NULL); + filename = g_build_filename (get_path (AUD_PATH_USER_DIR), basename, NULL); data = g_key_file_to_data(rcfile, &len, &error); gboolean success = g_file_set_contents (filename, data, len, & error); diff --git a/src/audacious/folder-add.c b/src/audacious/folder-add.c index 48ce73d..4435872 100644 --- a/src/audacious/folder-add.c +++ b/src/audacious/folder-add.c @@ -1,6 +1,6 @@ /* * folder-add.c - * Copyright 2009 John Lindgren + * Copyright 2009-2011 John Lindgren * * This file is part of Audacious. * @@ -20,31 +20,39 @@ */ #include <dirent.h> -#include <glib.h> -#include <gtk/gtk.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> +#include <gtk/gtk.h> + #include <libaudcore/audstrings.h> #include "audconfig.h" #include "config.h" +#include "glib-compat.h" #include "i18n.h" #include "misc.h" #include "playback.h" #include "playlist.h" -static GList * add_queue = NULL; +typedef struct { + gchar * filename; + PluginHandle * decoder; +} AddFile; + +typedef struct { + gint playlist_id, at; + GQueue folders; // (gchar *) + gboolean play; +} AddGroup; + +static GQueue add_queue = G_QUEUE_INIT; // (AddGroup *) static gint add_source = 0; -static gint add_playlist, add_at; -static gboolean add_play; static GtkWidget * add_window = NULL, * add_progress_path, * add_progress_count; static void show_progress (const gchar * path, gint count) { - gchar scratch[128]; - if (add_window == NULL) { GtkWidget * vbox; @@ -76,6 +84,8 @@ static void show_progress (const gchar * path, gint count) } gtk_label_set_text (GTK_LABEL(add_progress_path), path); + + gchar scratch[128]; snprintf (scratch, sizeof scratch, dngettext (PACKAGE, "%d file found", "%d files found", count), count); gtk_label_set_text (GTK_LABEL(add_progress_count), scratch); @@ -86,34 +96,49 @@ static void show_done (void) gtk_widget_destroy (add_window); } +static gint sort_cb (const AddFile * a, const AddFile * b) +{ + return string_compare_encoded (a->filename, b->filename); +} + static gboolean add_cb (void * unused) { - static GList * stack = NULL; - static struct index * index; - gint count; + static GList * stack = NULL; // (gchar *) and (DIR *) alternately + static GList * big_list = NULL, * little_list = NULL; // (AddFile *) - if (stack == NULL) + if (! stack) { - stack = g_list_prepend (stack, add_queue->data); - index = index_new (); + AddGroup * group = g_queue_peek_head (& add_queue); + g_return_val_if_fail (group, FALSE); + gchar * folder = g_queue_pop_head (& group->folders); + g_return_val_if_fail (folder, FALSE); + stack = g_list_prepend (NULL, folder); } - show_progress ((gchar *) stack->data, index_count (index)); + show_progress (stack->data, g_list_length (big_list) + g_list_length + (little_list)); - for (count = 0; count < 30; count ++) + for (gint count = 0; count < 30; count ++) { struct stat info; - struct dirent * entry; - if (stat (stack->data, & info) == 0) + /* top of stack is (gchar *) */ + + if (! stat (stack->data, & info)) { if (S_ISREG (info.st_mode)) { gchar * filename = filename_to_uri (stack->data); + PluginHandle * decoder = (filename == NULL) ? NULL : + file_find_decoder (filename, TRUE); - if (filename != NULL && file_find_decoder (filename, TRUE) != - NULL) - index_append (index, filename); + if (decoder) + { + AddFile * file = g_slice_new (AddFile); + file->filename = filename; + file->decoder = decoder; + little_list = g_list_prepend (little_list, file); + } else g_free (filename); } @@ -121,7 +146,7 @@ static gboolean add_cb (void * unused) { DIR * folder = opendir (stack->data); - if (folder != NULL) + if (folder) { stack = g_list_prepend (stack, folder); goto READ; @@ -133,18 +158,20 @@ static gboolean add_cb (void * unused) stack = g_list_delete_link (stack, stack); READ: - if (stack == NULL) + if (! stack) break; - entry = readdir (stack->data); + /* top of stack is (DIR *) */ + + struct dirent * entry = readdir (stack->data); - if (entry != NULL) + if (entry) { if (entry->d_name[0] == '.') goto READ; - stack = g_list_prepend (stack, g_strdup_printf ("%s/%s", (gchar *) - stack->next->data, entry->d_name)); + stack = g_list_prepend (stack, g_strdup_printf ("%s" + G_DIR_SEPARATOR_S "%s", (gchar *) stack->next->data, entry->d_name)); } else { @@ -156,64 +183,114 @@ static gboolean add_cb (void * unused) } } - if (stack == NULL) - { - index_sort (index, (gint (*) (const void *, const void *)) - string_compare_encoded); + if (stack) + return TRUE; /* not done yet */ - count = playlist_count (); + AddGroup * group = g_queue_peek_head (& add_queue); + g_return_val_if_fail (group, FALSE); - if (add_playlist > count - 1) - add_playlist = count - 1; + little_list = g_list_sort (little_list, (GCompareFunc) sort_cb); + big_list = g_list_concat (big_list, little_list); + little_list = NULL; - count = playlist_entry_count (add_playlist); + if (! g_queue_is_empty (& group->folders)) + return TRUE; /* not done yet */ - if (add_at < 0 || add_at > count) - add_at = count; + gint playlist = playlist_by_unique_id (group->playlist_id); - playlist_entry_insert_batch (add_playlist, add_at, index, NULL); - - if (add_play && playlist_entry_count (add_playlist) > count) + if (playlist < 0) /* ouch! playlist deleted */ + { + for (GList * node = big_list; node; node = node->next) { - playlist_set_playing (add_playlist); - if (! cfg.shuffle) - playlist_set_position (add_playlist, add_at); - playback_play (0, FALSE); - add_play = FALSE; + AddFile * file = node->data; + g_free (file->filename); + g_slice_free (AddFile, file); } - add_at += playlist_entry_count (add_playlist) - count; + g_list_free (big_list); + big_list = NULL; + goto ADDED; + } - add_queue = g_list_delete_link (add_queue, add_queue); + struct index * filenames = index_new (); + struct index * decoders = index_new (); - if (add_queue == NULL) - { - show_done (); - add_source = 0; - return FALSE; - } + for (GList * node = big_list; node; node = node->next) + { + AddFile * file = node->data; + index_append (filenames, file->filename); + index_append (decoders, file->decoder); + g_slice_free (AddFile, file); } - return TRUE; + g_list_free (big_list); + big_list = NULL; + + gint count = playlist_entry_count (playlist); + if (group->at < 0 || group->at > count) + group->at = count; + + playlist_entry_insert_batch_with_decoders (playlist, group->at, + filenames, decoders, NULL); + + if (group->play && playlist_entry_count (playlist) > count) + { + playlist_set_playing (playlist); + if (! cfg.shuffle) + playlist_set_position (playlist, group->at); + playback_play (0, FALSE); + } + +ADDED: + g_slice_free (AddGroup, group); + g_queue_pop_head (& add_queue); + + if (! g_queue_is_empty (& add_queue)) + return TRUE; /* not done yet */ + + show_done (); + add_source = 0; + return FALSE; } void playlist_insert_folder (gint playlist, gint at, const gchar * folder, gboolean play) { - gchar * unix_name = uri_to_filename (folder); - - add_playlist = playlist; - add_at = at; - add_play |= play; + gint playlist_id = playlist_get_unique_id (playlist); + g_return_if_fail (playlist_id >= 0); - if (unix_name == NULL) - return; + gchar * unix_name = uri_to_filename (folder); + g_return_if_fail (unix_name); if (unix_name[strlen (unix_name) - 1] == '/') unix_name[strlen (unix_name) - 1] = 0; - add_queue = g_list_append (add_queue, unix_name); + AddGroup * group = NULL; + + for (GList * node = g_queue_peek_head_link (& add_queue); node; node = + node->next) + { + AddGroup * test = node->data; + if (test->playlist_id == playlist_id && test->at == at) + { + group = test; + break; + } + } + + if (! group) + { + group = g_slice_new (AddGroup); + group->playlist_id = playlist_id; + group->at = at; + g_queue_init (& group->folders); + group->play = FALSE; + g_queue_push_tail (& add_queue, group); + } + + g_queue_push_tail (& group->folders, unix_name); + group->play |= play; - if (add_source == 0) + if (! add_source) add_source = g_idle_add (add_cb, NULL); } diff --git a/src/audacious/general.c b/src/audacious/general.c index 1c1e05c..7c8e60a 100644 --- a/src/audacious/general.c +++ b/src/audacious/general.c @@ -1,6 +1,6 @@ /* * general.c - * Copyright 2010 John Lindgren + * Copyright 2011 John Lindgren * * This file is part of Audacious. * @@ -19,45 +19,78 @@ * using our public API to be a derived work. */ -#include <glib.h> +#include <gtk/gtk.h> #include "debug.h" #include "general.h" +#include "interface.h" #include "plugin.h" #include "plugins.h" +#include "ui_preferences.h" +typedef struct { + PluginHandle * plugin; + GeneralPlugin * gp; + GtkWidget * widget; +} LoadedGeneral; + +static gint running = FALSE; static GList * loaded_general_plugins = NULL; +static gint general_find_cb (LoadedGeneral * general, PluginHandle * plugin) +{ + return (general->plugin == plugin) ? 0 : -1; +} + static void general_load (PluginHandle * plugin) { - GList * node = g_list_find (loaded_general_plugins, plugin); + GList * node = g_list_find_custom (loaded_general_plugins, plugin, + (GCompareFunc) general_find_cb); if (node != NULL) return; AUDDBG ("Loading %s.\n", plugin_get_name (plugin)); - GeneralPlugin * header = plugin_get_header (plugin); - g_return_if_fail (header != NULL); - - if (header->init != NULL) - header->init (); - - loaded_general_plugins = g_list_prepend (loaded_general_plugins, plugin); + GeneralPlugin * gp = plugin_get_header (plugin); + g_return_if_fail (gp != NULL); + + LoadedGeneral * general = g_slice_new (LoadedGeneral); + general->plugin = plugin; + general->gp = gp; + general->widget = NULL; + + if (gp->get_widget != NULL) + general->widget = gp->get_widget (); + + if (general->widget != NULL) + { + AUDDBG ("Adding %s to interface.\n", plugin_get_name (plugin)); + g_signal_connect (general->widget, "destroy", (GCallback) + gtk_widget_destroyed, & general->widget); + interface_add_plugin_widget (plugin, general->widget); + } + + loaded_general_plugins = g_list_prepend (loaded_general_plugins, general); } static void general_unload (PluginHandle * plugin) { - GList * node = g_list_find (loaded_general_plugins, plugin); + GList * node = g_list_find_custom (loaded_general_plugins, plugin, + (GCompareFunc) general_find_cb); if (node == NULL) return; AUDDBG ("Unloading %s.\n", plugin_get_name (plugin)); - GeneralPlugin * header = plugin_get_header (plugin); - g_return_if_fail (header != NULL); - + LoadedGeneral * general = node->data; loaded_general_plugins = g_list_delete_link (loaded_general_plugins, node); - if (header->cleanup != NULL) - header->cleanup (); + if (general->widget != NULL) + { + AUDDBG ("Removing %s from interface.\n", plugin_get_name (plugin)); + interface_remove_plugin_widget (plugin, general->widget); + g_return_if_fail (general->widget == NULL); /* not destroyed? */ + } + + g_slice_free (LoadedGeneral, general); } static gboolean general_init_cb (PluginHandle * plugin) @@ -68,21 +101,64 @@ static gboolean general_init_cb (PluginHandle * plugin) void general_init (void) { + g_return_if_fail (! running); + running = TRUE; + plugin_for_enabled (PLUGIN_TYPE_GENERAL, (PluginForEachFunc) general_init_cb, NULL); } +static void general_cleanup_cb (LoadedGeneral * general) +{ + general_unload (general->plugin); +} + void general_cleanup (void) { - g_list_foreach (loaded_general_plugins, (GFunc) general_unload, NULL); + g_return_if_fail (running); + running = FALSE; + + g_list_foreach (loaded_general_plugins, (GFunc) general_cleanup_cb, NULL); } -void general_plugin_enable (PluginHandle * plugin, gboolean enable) +gboolean general_plugin_start (PluginHandle * plugin) { - plugin_set_enabled (plugin, enable); + GeneralPlugin * gp = plugin_get_header (plugin); + g_return_val_if_fail (gp != NULL, FALSE); + + if (gp->init != NULL && ! gp->init ()) + return FALSE; - if (enable) + if (running) general_load (plugin); - else + + return TRUE; +} + +void general_plugin_stop (PluginHandle * plugin) +{ + GeneralPlugin * gp = plugin_get_header (plugin); + g_return_if_fail (gp != NULL); + + if (running) general_unload (plugin); + + if (gp->settings != NULL) + plugin_preferences_cleanup (gp->settings); + if (gp->cleanup != NULL) + gp->cleanup (); +} + +PluginHandle * general_plugin_by_widget (/* GtkWidget * */ void * widget) +{ + g_return_val_if_fail (widget, NULL); + + for (GList * node = loaded_general_plugins; node; node = node->next) + { + LoadedGeneral * general = node->data; + if (general->widget == widget) + return general->plugin; + } + + return NULL; } diff --git a/src/audacious/general.h b/src/audacious/general.h index cf29eb7..b96dbff 100644 --- a/src/audacious/general.h +++ b/src/audacious/general.h @@ -1,6 +1,6 @@ /* * general.h - * Copyright 2010 John Lindgren + * Copyright 2011 John Lindgren * * This file is part of Audacious. * @@ -22,7 +22,16 @@ #ifndef AUDACIOUS_GENERAL_H #define AUDACIOUS_GENERAL_H +#include <glib.h> + +#include "plugins.h" + void general_init (void); void general_cleanup (void); +gboolean general_plugin_start (PluginHandle * plugin); +void general_plugin_stop (PluginHandle * plugin); + +PluginHandle * general_plugin_by_widget (/* GtkWidget * */ void * widget); + #endif diff --git a/src/audacious/glib-compat.h b/src/audacious/glib-compat.h new file mode 100755 index 0000000..be639d9 --- /dev/null +++ b/src/audacious/glib-compat.h @@ -0,0 +1,26 @@ +/* Compatibility macros to make supporting multiple GLib versions easier. + * Public domain. */ + +#ifndef AUD_GLIB_COMPAT_H +#define AUD_GLIB_COMPAT_H + +#if ! GLIB_CHECK_VERSION (2, 14, 0) + +static inline void g_queue_init (GQueue * q) +{ + q->head = q->tail = NULL; + q->length = 0; +} + +static inline void g_queue_clear (GQueue * q) +{ + g_list_free (q->head); + q->head = q->tail = NULL; + q->length = 0; +} + +#define G_QUEUE_INIT {NULL, NULL, 0} +#define g_timeout_add_seconds(s, f, d) g_timeout_add (1000 * (s), (f), (d)) +#endif + +#endif /* AUD_GLIB_COMPAT_H */ diff --git a/src/audacious/gtk-compat.h b/src/audacious/gtk-compat.h new file mode 100644 index 0000000..91d8d20 --- /dev/null +++ b/src/audacious/gtk-compat.h @@ -0,0 +1,89 @@ +/* Compatibility macros to make supporting multiple GTK versions easier. + * Public domain. */ + +#ifndef AUD_GTK_COMPAT_H +#define AUD_GTK_COMPAT_H + +#include <string.h> + +#if defined GDK_KEY_Tab && ! defined GDK_Tab +#include <gdk/gdkkeysyms-compat.h> +#endif + +#if ! GTK_CHECK_VERSION (2, 10, 0) +#define GDK_WINDOW_TYPE_HINT_TOOLTIP GDK_WINDOW_TYPE_HINT_MENU +#define gtk_label_set_line_wrap_mode(...) +#endif + +#if ! GTK_CHECK_VERSION (2, 12, 0) + +static inline void gtk_tree_view_convert_widget_to_bin_window_coords + (GtkTreeView * tree, gint wx, gint wy, gint * bx, gint * by) +{ + gint bx0, by0; + gdk_window_get_position (gtk_tree_view_get_bin_window (tree), & bx0, & by0); + * bx = wx - bx0; + * by = wy - by0; +} + +#define gtk_widget_set_tooltip_text(...) +#endif + +#if ! GTK_CHECK_VERSION (2, 14, 0) +#define gtk_adjustment_get_page_size(a) ((a)->page_size) +#define gtk_adjustment_get_upper(a) ((a)->upper) +#define gtk_dialog_get_action_area(d) ((d)->action_area) +#define gtk_dialog_get_content_area(d) ((d)->vbox) +#define gtk_selection_data_get_data(s) ((s)->data) +#define gtk_selection_data_get_length(s) ((s)->length) +#define gtk_widget_get_window(w) ((w)->window) +#endif + +#if ! GTK_CHECK_VERSION (2, 18, 0) + +static inline void gtk_widget_set_can_default (GtkWidget * w, gboolean b) +{ + if (b) + GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); + else + GTK_WIDGET_UNSET_FLAGS (w, GTK_CAN_DEFAULT); +} + +static inline void gtk_widget_set_can_focus (GtkWidget * w, gboolean b) +{ + if (b) + GTK_WIDGET_SET_FLAGS (w, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS (w, GTK_CAN_FOCUS); +} + +#define gtk_widget_get_allocation(w, a) memcpy ((a), & (w)->allocation, sizeof (GtkAllocation)) +#define gtk_widget_get_sensitive GTK_WIDGET_SENSITIVE +#define gtk_widget_get_visible GTK_WIDGET_VISIBLE +#define gtk_widget_is_toplevel GTK_WIDGET_TOPLEVEL +#endif + +#if ! GTK_CHECK_VERSION (2, 20, 0) +#define gtk_widget_is_drawable GTK_WIDGET_DRAWABLE +#endif + +#if ! GTK_CHECK_VERSION (3, 0, 0) + +static inline void gdk_window_get_geometry_compat (GdkWindow * win, gint * x, + gint * y, gint * w, gint * h) +{ + gdk_window_get_geometry (win, x, y, w, h, NULL); +} + +#define GtkComboBoxText GtkComboBox +#define gdk_window_get_geometry gdk_window_get_geometry_compat +#define gtk_combo_box_text_new gtk_combo_box_new_text +#define gtk_combo_box_text_new_with_entry gtk_combo_box_entry_new_text +#define gtk_combo_box_text_append_text gtk_combo_box_append_text +#endif + +#if GTK_CHECK_VERSION (3, 0, 0) +#define gtk_range_set_update_policy(...) +#endif + +#endif /* AUD_GTK_COMPAT_H */ diff --git a/src/audacious/images/about-logo.png b/src/audacious/images/about-logo.png Binary files differindex f3dc1f1..32fb69f 100644 --- a/src/audacious/images/about-logo.png +++ b/src/audacious/images/about-logo.png diff --git a/src/audacious/images/album.png b/src/audacious/images/album.png Binary files differnew file mode 100644 index 0000000..decbcdb --- /dev/null +++ b/src/audacious/images/album.png diff --git a/src/audacious/images/appearance.png b/src/audacious/images/appearance.png Binary files differindex baf5f8a..b06f9b6 100644 --- a/src/audacious/images/appearance.png +++ b/src/audacious/images/appearance.png diff --git a/src/audacious/images/audio.png b/src/audacious/images/audio.png Binary files differindex 8d3ecb5..4cbf294 100644 --- a/src/audacious/images/audio.png +++ b/src/audacious/images/audio.png diff --git a/src/audacious/images/connectivity.png b/src/audacious/images/connectivity.png Binary files differindex 23dfd56..d6be3b8 100644 --- a/src/audacious/images/connectivity.png +++ b/src/audacious/images/connectivity.png diff --git a/src/audacious/images/menu_playlist.png b/src/audacious/images/menu_playlist.png Binary files differindex fd877cb..a96f899 100644 --- a/src/audacious/images/menu_playlist.png +++ b/src/audacious/images/menu_playlist.png diff --git a/src/audacious/images/menu_plugin.png b/src/audacious/images/menu_plugin.png Binary files differindex 0d49f9d..507ff2a 100644 --- a/src/audacious/images/menu_plugin.png +++ b/src/audacious/images/menu_plugin.png diff --git a/src/audacious/images/menu_queue_toggle.png b/src/audacious/images/menu_queue_toggle.png Binary files differindex 2ecbd15..d2cfa4e 100644 --- a/src/audacious/images/menu_queue_toggle.png +++ b/src/audacious/images/menu_queue_toggle.png diff --git a/src/audacious/images/play.png b/src/audacious/images/play.png Binary files differdeleted file mode 100644 index 7fc2172..0000000 --- a/src/audacious/images/play.png +++ /dev/null diff --git a/src/audacious/images/playback.png b/src/audacious/images/playback.png Binary files differdeleted file mode 100644 index bcc2ecc..0000000 --- a/src/audacious/images/playback.png +++ /dev/null diff --git a/src/audacious/images/playlist.png b/src/audacious/images/playlist.png Binary files differindex 00c51ba..787a1e0 100644 --- a/src/audacious/images/playlist.png +++ b/src/audacious/images/playlist.png diff --git a/src/audacious/images/plugins.png b/src/audacious/images/plugins.png Binary files differindex f8b8180..47d3e42 100644 --- a/src/audacious/images/plugins.png +++ b/src/audacious/images/plugins.png diff --git a/src/audacious/images/replay_gain.png b/src/audacious/images/replay_gain.png Binary files differindex 75ab421..4fce8c3 100644 --- a/src/audacious/images/replay_gain.png +++ b/src/audacious/images/replay_gain.png diff --git a/src/audacious/interface.c b/src/audacious/interface.c index 65aca42..e43a33b 100644 --- a/src/audacious/interface.c +++ b/src/audacious/interface.c @@ -2,6 +2,7 @@ * Audacious2 * Copyright (c) 2008 William Pitcock <nenolod@dereferenced.org> * Copyright (c) 2008-2009 Tomasz MoÅ„ <desowin@gmail.com> + * Copyright (c) 2010 John Lindgren <john.lindgren@tds.net> * * 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 @@ -27,14 +28,17 @@ #include "audconfig.h" #include "config.h" #include "debug.h" +#include "general.h" #include "i18n.h" #include "interface.h" +#include "misc.h" #include "plugins.h" #include "ui_preferences.h" +#include "visualization.h" -static Interface *current_interface = NULL; +static Iface *current_interface = NULL; -static InterfaceOps interface_ops = { +static IfaceOps interface_ops = { .create_prefs_window = create_prefs_window, .show_prefs_window = show_prefs_window, .hide_prefs_window = hide_prefs_window, @@ -42,48 +46,11 @@ static InterfaceOps interface_ops = { .prefswin_page_new = prefswin_page_new, }; -static InterfaceCbs interface_cbs = { NULL }; - -static gboolean interface_search_cb (PluginHandle * plugin, PluginHandle * * - pluginp) -{ - * pluginp = plugin; - return FALSE; -} - -PluginHandle * interface_get_default (void) -{ - PluginHandle * plugin = NULL; - - if (cfg.iface_path == NULL || (plugin = plugin_by_path (cfg.iface_path, - PLUGIN_TYPE_IFACE, cfg.iface_number)) == NULL || ! plugin_get_enabled - (plugin)) - { - AUDDBG ("Searching for an interface.\n"); - plugin_for_enabled (PLUGIN_TYPE_IFACE, (PluginForEachFunc) - interface_search_cb, & plugin); - if (plugin == NULL) - return NULL; - - interface_set_default (plugin); - } - - return plugin; -} - -void interface_set_default (PluginHandle * plugin) -{ - const gchar * path; - gint type; - - g_free (cfg.iface_path); - plugin_get_path (plugin, & path, & type, & cfg.iface_number); - cfg.iface_path = g_strdup (path); -} +static IfaceCbs interface_cbs = { NULL }; gboolean interface_load (PluginHandle * plugin) { - Interface * i = plugin_get_header (plugin); + Iface * i = (Iface *) plugin_get_header (plugin); g_return_val_if_fail (i != NULL, FALSE); current_interface = i; @@ -189,7 +156,7 @@ interface_show_about_window(gboolean show) static gboolean delete_cb (GtkWidget * window, GdkEvent * event, PluginHandle * plugin) { - vis_plugin_enable (plugin, FALSE); + plugin_enable (plugin, FALSE); return TRUE; } @@ -216,6 +183,24 @@ void interface_remove_plugin_widget (PluginHandle * plugin, GtkWidget * widget) } void +interface_install_toolbar(void *widget) +{ + if (interface_cbs.install_toolbar != NULL) + interface_cbs.install_toolbar(widget); + else + AUDDBG ("Interface didn't register install_toolbar function.\n"); +} + +void +interface_uninstall_toolbar(void *widget) +{ + if (interface_cbs.uninstall_toolbar != NULL) + interface_cbs.uninstall_toolbar(widget); + else + AUDDBG ("Interface didn't register uninstall_toolbar function.\n"); +} + +void interface_toggle_shuffle(void) { if (interface_cbs.toggle_shuffle != NULL) @@ -244,7 +229,7 @@ typedef enum { HOOK_ABOUTWIN_SHOW, HOOK_TOGGLE_SHUFFLE, HOOK_TOGGLE_REPEAT, -} InterfaceHookID; +} IfaceHookID; void interface_hook_handler(gpointer hook_data, gpointer user_data) @@ -287,10 +272,10 @@ interface_hook_handler(gpointer hook_data, gpointer user_data) typedef struct { const gchar *name; - InterfaceHookID id; -} InterfaceHooks; + IfaceHookID id; +} IfaceHooks; -static InterfaceHooks hooks[] = { +static IfaceHooks hooks[] = { {"prefswin show", HOOK_PREFSWIN_SHOW}, {"filebrowser show", HOOK_FILEBROWSER_SHOW}, {"filebrowser hide", HOOK_FILEBROWSER_HIDE}, @@ -314,3 +299,57 @@ register_interface_hooks(void) } +static gboolean probe_cb (PluginHandle * p, PluginHandle * * pp) +{ + * pp = p; + return FALSE; +} + +PluginHandle * iface_plugin_probe (void) +{ + PluginHandle * p = NULL; + plugin_for_each (PLUGIN_TYPE_IFACE, (PluginForEachFunc) probe_cb, & p); + return p; +} + +static PluginHandle * current_plugin = NULL; + +PluginHandle * iface_plugin_get_current (void) +{ + return current_plugin; +} + +gboolean iface_plugin_set_current (PluginHandle * plugin) +{ + if (current_plugin != NULL) + { + AUDDBG ("Unloading plugin widgets.\n"); + general_cleanup (); + + AUDDBG ("Unloading visualizers.\n"); + vis_cleanup (); + + AUDDBG ("Unloading %s.\n", plugin_get_name (current_plugin)); + interface_unload (); + + current_plugin = NULL; + } + + if (plugin != NULL) + { + AUDDBG ("Loading %s.\n", plugin_get_name (plugin)); + + if (! interface_load (plugin)) + return FALSE; + + current_plugin = plugin; + + AUDDBG ("Loading visualizers.\n"); + vis_init (); + + AUDDBG ("Loading plugin widgets.\n"); + general_init (); + } + + return TRUE; +} diff --git a/src/audacious/interface.h b/src/audacious/interface.h index e03df24..4ce4c5b 100644 --- a/src/audacious/interface.h +++ b/src/audacious/interface.h @@ -42,7 +42,7 @@ typedef struct { const gchar * imgurl); */ gint (* prefswin_page_new) (void * container, const gchar * name, const gchar * imgurl); -} InterfaceOps; +} IfaceOps; typedef struct { void (*show_prefs_window)(gboolean show); @@ -60,15 +60,18 @@ typedef struct { void * (* run_gtk_plugin) (void * parent, const gchar * name); /* GtkWidget * (* stop_gtk_plugin) (GtkWidget * parent); */ void * (* stop_gtk_plugin) (void * parent); -} InterfaceCbs; -struct _Interface { + void (*install_toolbar)(void * button); + void (*uninstall_toolbar)(void * button); +} IfaceCbs; + +struct _Iface { gchar *id; /* simple ID like 'skinned' */ gchar *desc; /* description like 'Skinned Interface' */ - gboolean (*init)(InterfaceCbs *cbs); /* init UI */ + gboolean (*init)(IfaceCbs *cbs); /* init UI */ gboolean (*fini)(void); /* shutdown UI */ - InterfaceOps *ops; + IfaceOps *ops; }; #ifdef _AUDACIOUS_CORE @@ -76,8 +79,6 @@ struct _Interface { #include <gtk/gtk.h> #include <audacious/plugins.h> -PluginHandle * interface_get_default (void); -void interface_set_default (PluginHandle * plugin); gboolean interface_load (PluginHandle * plugin); void interface_unload (void); @@ -97,5 +98,9 @@ void interface_toggle_repeat(void); void register_interface_hooks(void); +PluginHandle * iface_plugin_probe (void); +PluginHandle * iface_plugin_get_current (void); +gboolean iface_plugin_set_current (PluginHandle * plugin); + #endif #endif diff --git a/src/audacious/intl/ChangeLog b/src/audacious/intl/ChangeLog deleted file mode 100644 index eed2d21..0000000 --- a/src/audacious/intl/ChangeLog +++ /dev/null @@ -1,4 +0,0 @@ -2003-05-22 GNU <bug-gnu-gettext@gnu.org> - - * Version 0.12.1 released. - diff --git a/src/audacious/intl/Makefile b/src/audacious/intl/Makefile deleted file mode 100644 index 44217e1..0000000 --- a/src/audacious/intl/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -STATIC_LIB_NOINST = libintl.a -SRCS = bindtextdom.c \ - dcgettext.c \ - dgettext.c \ - gettext.c \ - finddomain.c \ - loadmsgcat.c \ - localealias.c \ - textdomain.c \ - l10nflist.c \ - explodename.c \ - dcigettext.c \ - dcngettext.c \ - dngettext.c \ - ngettext.c \ - plural.c \ - plural-exp.c \ - localcharset.c \ - relocatable.c \ - localename.c \ - log.c \ - osdep.c \ - os2compat.c \ - intl-compat.c - -include ../../../buildsys.mk -include ../../../extra.mk - -localedir = ${datadir)}locale -gettextsrcdir = ${datadir}/gettext/intl -aliaspath = ${localedir} - -DEFS += -DLOCALEDIR=\"${localedir}\" -DLOCALE_ALIAS_PATH=\"${aliaspath}\" \ - -DLIBDIR=\"${libdir}\" -DIN_LIBINTL \ - -DENABLE_RELOCATABLE=1 -DIN_LIBRARY -DINSTALLDIR=\"${libdir}\" -DINSTALLPREFIX=\"${libdir}\" -DNO_XMALLOC \ - -Dset_relocation_prefix=libintl_set_relocation_prefix \ - -Drelocate=libintl_relocate \ - -DDEPENDS_ON_LIBICONV=1 - -CPPFLAGS += -I../../.. -I../.. -I. ${DEFS} diff --git a/src/audacious/intl/VERSION b/src/audacious/intl/VERSION deleted file mode 100644 index 1303183..0000000 --- a/src/audacious/intl/VERSION +++ /dev/null @@ -1 +0,0 @@ -GNU gettext library from gettext-0.12.1 diff --git a/src/audacious/intl/bindtextdom.c b/src/audacious/intl/bindtextdom.c deleted file mode 100644 index ff0ec62..0000000 --- a/src/audacious/intl/bindtextdom.c +++ /dev/null @@ -1,374 +0,0 @@ -/* Implementation of the bindtextdomain(3) function - Copyright (C) 1995-1998, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif -#include "gettextP.h" - -#ifdef _LIBC -/* We have to handle multi-threaded applications. */ -# include <bits/libc-lock.h> -#else -/* Provide dummy implementation if this is outside glibc. */ -# define __libc_rwlock_define(CLASS, NAME) -# define __libc_rwlock_wrlock(NAME) -# define __libc_rwlock_unlock(NAME) -#endif - -/* The internal variables in the standalone libintl.a must have different - names than the internal variables in GNU libc, otherwise programs - using libintl.a cannot be linked statically. */ -#if !defined _LIBC -# define _nl_default_dirname libintl_nl_default_dirname -# define _nl_domain_bindings libintl_nl_domain_bindings -#endif - -/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>. */ -#ifndef offsetof -# define offsetof(type,ident) ((size_t)&(((type*)0)->ident)) -#endif - -/* @@ end of prolog @@ */ - -/* Contains the default location of the message catalogs. */ -extern const char _nl_default_dirname[]; -#ifdef _LIBC -extern const char _nl_default_dirname_internal[] attribute_hidden; -#else -# define INTUSE(name) name -#endif - -/* List with bindings of specific domains. */ -extern struct binding *_nl_domain_bindings; - -/* Lock variable to protect the global data in the gettext implementation. */ -__libc_rwlock_define (extern, _nl_state_lock attribute_hidden) - - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define BINDTEXTDOMAIN __bindtextdomain -# define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset -# ifndef strdup -# define strdup(str) __strdup (str) -# endif -#else -# define BINDTEXTDOMAIN libintl_bindtextdomain -# define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset -#endif - -/* Prototypes for local functions. */ -static void set_binding_values PARAMS ((const char *domainname, - const char **dirnamep, - const char **codesetp)); - -/* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP - to be used for the DOMAINNAME message catalog. - If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not - modified, only the current value is returned. - If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither - modified nor returned. */ -static void -set_binding_values (domainname, dirnamep, codesetp) - const char *domainname; - const char **dirnamep; - const char **codesetp; -{ - struct binding *binding; - int modified; - - /* Some sanity checks. */ - if (domainname == NULL || domainname[0] == '\0') - { - if (dirnamep) - *dirnamep = NULL; - if (codesetp) - *codesetp = NULL; - return; - } - - __libc_rwlock_wrlock (_nl_state_lock); - - modified = 0; - - for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) - { - int compare = strcmp (domainname, binding->domainname); - if (compare == 0) - /* We found it! */ - break; - if (compare < 0) - { - /* It is not in the list. */ - binding = NULL; - break; - } - } - - if (binding != NULL) - { - if (dirnamep) - { - const char *dirname = *dirnamep; - - if (dirname == NULL) - /* The current binding has be to returned. */ - *dirnamep = binding->dirname; - else - { - /* The domain is already bound. If the new value and the old - one are equal we simply do nothing. Otherwise replace the - old binding. */ - char *result = binding->dirname; - if (strcmp (dirname, result) != 0) - { - if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0) - result = (char *) INTUSE(_nl_default_dirname); - else - { -#if defined _LIBC || defined HAVE_STRDUP - result = strdup (dirname); -#else - size_t len = strlen (dirname) + 1; - result = (char *) malloc (len); - if (__builtin_expect (result != NULL, 1)) - memcpy (result, dirname, len); -#endif - } - - if (__builtin_expect (result != NULL, 1)) - { - if (binding->dirname != INTUSE(_nl_default_dirname)) - free (binding->dirname); - - binding->dirname = result; - modified = 1; - } - } - *dirnamep = result; - } - } - - if (codesetp) - { - const char *codeset = *codesetp; - - if (codeset == NULL) - /* The current binding has be to returned. */ - *codesetp = binding->codeset; - else - { - /* The domain is already bound. If the new value and the old - one are equal we simply do nothing. Otherwise replace the - old binding. */ - char *result = binding->codeset; - if (result == NULL || strcmp (codeset, result) != 0) - { -#if defined _LIBC || defined HAVE_STRDUP - result = strdup (codeset); -#else - size_t len = strlen (codeset) + 1; - result = (char *) malloc (len); - if (__builtin_expect (result != NULL, 1)) - memcpy (result, codeset, len); -#endif - - if (__builtin_expect (result != NULL, 1)) - { - if (binding->codeset != NULL) - free (binding->codeset); - - binding->codeset = result; - binding->codeset_cntr++; - modified = 1; - } - } - *codesetp = result; - } - } - } - else if ((dirnamep == NULL || *dirnamep == NULL) - && (codesetp == NULL || *codesetp == NULL)) - { - /* Simply return the default values. */ - if (dirnamep) - *dirnamep = INTUSE(_nl_default_dirname); - if (codesetp) - *codesetp = NULL; - } - else - { - /* We have to create a new binding. */ - size_t len = strlen (domainname) + 1; - struct binding *new_binding = - (struct binding *) malloc (offsetof (struct binding, domainname) + len); - - if (__builtin_expect (new_binding == NULL, 0)) - goto failed; - - memcpy (new_binding->domainname, domainname, len); - - if (dirnamep) - { - const char *dirname = *dirnamep; - - if (dirname == NULL) - /* The default value. */ - dirname = INTUSE(_nl_default_dirname); - else - { - if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0) - dirname = INTUSE(_nl_default_dirname); - else - { - char *result; -#if defined _LIBC || defined HAVE_STRDUP - result = strdup (dirname); - if (__builtin_expect (result == NULL, 0)) - goto failed_dirname; -#else - size_t len = strlen (dirname) + 1; - result = (char *) malloc (len); - if (__builtin_expect (result == NULL, 0)) - goto failed_dirname; - memcpy (result, dirname, len); -#endif - dirname = result; - } - } - *dirnamep = dirname; - new_binding->dirname = (char *) dirname; - } - else - /* The default value. */ - new_binding->dirname = (char *) INTUSE(_nl_default_dirname); - - new_binding->codeset_cntr = 0; - - if (codesetp) - { - const char *codeset = *codesetp; - - if (codeset != NULL) - { - char *result; - -#if defined _LIBC || defined HAVE_STRDUP - result = strdup (codeset); - if (__builtin_expect (result == NULL, 0)) - goto failed_codeset; -#else - size_t len = strlen (codeset) + 1; - result = (char *) malloc (len); - if (__builtin_expect (result == NULL, 0)) - goto failed_codeset; - memcpy (result, codeset, len); -#endif - codeset = result; - new_binding->codeset_cntr++; - } - *codesetp = codeset; - new_binding->codeset = (char *) codeset; - } - else - new_binding->codeset = NULL; - - /* Now enqueue it. */ - if (_nl_domain_bindings == NULL - || strcmp (domainname, _nl_domain_bindings->domainname) < 0) - { - new_binding->next = _nl_domain_bindings; - _nl_domain_bindings = new_binding; - } - else - { - binding = _nl_domain_bindings; - while (binding->next != NULL - && strcmp (domainname, binding->next->domainname) > 0) - binding = binding->next; - - new_binding->next = binding->next; - binding->next = new_binding; - } - - modified = 1; - - /* Here we deal with memory allocation failures. */ - if (0) - { - failed_codeset: - if (new_binding->dirname != INTUSE(_nl_default_dirname)) - free (new_binding->dirname); - failed_dirname: - free (new_binding); - failed: - if (dirnamep) - *dirnamep = NULL; - if (codesetp) - *codesetp = NULL; - } - } - - /* If we modified any binding, we flush the caches. */ - if (modified) - ++_nl_msg_cat_cntr; - - __libc_rwlock_unlock (_nl_state_lock); -} - -/* Specify that the DOMAINNAME message catalog will be found - in DIRNAME rather than in the system locale data base. */ -char * -BINDTEXTDOMAIN (domainname, dirname) - const char *domainname; - const char *dirname; -{ - set_binding_values (domainname, &dirname, NULL); - return (char *) dirname; -} - -/* Specify the character encoding in which the messages from the - DOMAINNAME message catalog will be returned. */ -char * -BIND_TEXTDOMAIN_CODESET (domainname, codeset) - const char *domainname; - const char *codeset; -{ - set_binding_values (domainname, NULL, &codeset); - return (char *) codeset; -} - -#ifdef _LIBC -/* Aliases for function names in GNU C Library. */ -weak_alias (__bindtextdomain, bindtextdomain); -weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset); -#endif diff --git a/src/audacious/intl/config.charset b/src/audacious/intl/config.charset deleted file mode 100644 index 11b3b5f..0000000 --- a/src/audacious/intl/config.charset +++ /dev/null @@ -1,467 +0,0 @@ -#! /bin/sh -# Output a system dependent table of character encoding aliases. -# -# Copyright (C) 2000-2003 Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU Library General Public License as published -# by the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# Library General Public License for more details. -# -# You should have received a copy of the GNU Library General Public -# License along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. -# -# The table consists of lines of the form -# ALIAS CANONICAL -# -# ALIAS is the (system dependent) result of "nl_langinfo (CODESET)". -# ALIAS is compared in a case sensitive way. -# -# CANONICAL is the GNU canonical name for this character encoding. -# It must be an encoding supported by libiconv. Support by GNU libc is -# also desirable. CANONICAL is case insensitive. Usually an upper case -# MIME charset name is preferred. -# The current list of GNU canonical charset names is as follows. -# -# name used by which systems a MIME name? -# ASCII, ANSI_X3.4-1968 glibc solaris freebsd -# ISO-8859-1 glibc aix hpux irix osf solaris freebsd yes -# ISO-8859-2 glibc aix hpux irix osf solaris freebsd yes -# ISO-8859-3 glibc solaris yes -# ISO-8859-4 osf solaris freebsd yes -# ISO-8859-5 glibc aix hpux irix osf solaris freebsd yes -# ISO-8859-6 glibc aix hpux solaris yes -# ISO-8859-7 glibc aix hpux irix osf solaris yes -# ISO-8859-8 glibc aix hpux osf solaris yes -# ISO-8859-9 glibc aix hpux irix osf solaris yes -# ISO-8859-13 glibc -# ISO-8859-14 glibc -# ISO-8859-15 glibc aix osf solaris freebsd -# KOI8-R glibc solaris freebsd yes -# KOI8-U glibc freebsd yes -# KOI8-T glibc -# CP437 dos -# CP775 dos -# CP850 aix osf dos -# CP852 dos -# CP855 dos -# CP856 aix -# CP857 dos -# CP861 dos -# CP862 dos -# CP864 dos -# CP865 dos -# CP866 freebsd dos -# CP869 dos -# CP874 woe32 dos -# CP922 aix -# CP932 aix woe32 dos -# CP943 aix -# CP949 osf woe32 dos -# CP950 woe32 dos -# CP1046 aix -# CP1124 aix -# CP1125 dos -# CP1129 aix -# CP1250 woe32 -# CP1251 glibc solaris woe32 -# CP1252 aix woe32 -# CP1253 woe32 -# CP1254 woe32 -# CP1255 glibc woe32 -# CP1256 woe32 -# CP1257 woe32 -# GB2312 glibc aix hpux irix solaris freebsd yes -# EUC-JP glibc aix hpux irix osf solaris freebsd yes -# EUC-KR glibc aix hpux irix osf solaris freebsd yes -# EUC-TW glibc aix hpux irix osf solaris -# BIG5 glibc aix hpux osf solaris freebsd yes -# BIG5-HKSCS glibc solaris -# GBK glibc aix osf solaris woe32 dos -# GB18030 glibc solaris -# SHIFT_JIS hpux osf solaris freebsd yes -# JOHAB glibc solaris woe32 -# TIS-620 glibc aix hpux osf solaris -# VISCII glibc yes -# TCVN5712-1 glibc -# GEORGIAN-PS glibc -# HP-ROMAN8 hpux -# HP-ARABIC8 hpux -# HP-GREEK8 hpux -# HP-HEBREW8 hpux -# HP-TURKISH8 hpux -# HP-KANA8 hpux -# DEC-KANJI osf -# DEC-HANYU osf -# UTF-8 glibc aix hpux osf solaris yes -# -# Note: Names which are not marked as being a MIME name should not be used in -# Internet protocols for information interchange (mail, news, etc.). -# -# Note: ASCII and ANSI_X3.4-1968 are synonymous canonical names. Applications -# must understand both names and treat them as equivalent. -# -# The first argument passed to this file is the canonical host specification, -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM - -host="$1" -os=`echo "$host" | sed -e 's/^[^-]*-[^-]*-\(.*\)$/\1/'` -echo "# This file contains a table of character encoding aliases," -echo "# suitable for operating system '${os}'." -echo "# It was automatically generated from config.charset." -# List of references, updated during installation: -echo "# Packages using this file: " -case "$os" in - linux* | *-gnu*) - # With glibc-2.1 or newer, we don't need any canonicalization, - # because glibc has iconv and both glibc and libiconv support all - # GNU canonical names directly. Therefore, the Makefile does not - # need to install the alias file at all. - # The following applies only to glibc-2.0.x and older libcs. - echo "ISO_646.IRV:1983 ASCII" - ;; - aix*) - echo "ISO8859-1 ISO-8859-1" - echo "ISO8859-2 ISO-8859-2" - echo "ISO8859-5 ISO-8859-5" - echo "ISO8859-6 ISO-8859-6" - echo "ISO8859-7 ISO-8859-7" - echo "ISO8859-8 ISO-8859-8" - echo "ISO8859-9 ISO-8859-9" - echo "ISO8859-15 ISO-8859-15" - echo "IBM-850 CP850" - echo "IBM-856 CP856" - echo "IBM-921 ISO-8859-13" - echo "IBM-922 CP922" - echo "IBM-932 CP932" - echo "IBM-943 CP943" - echo "IBM-1046 CP1046" - echo "IBM-1124 CP1124" - echo "IBM-1129 CP1129" - echo "IBM-1252 CP1252" - echo "IBM-eucCN GB2312" - echo "IBM-eucJP EUC-JP" - echo "IBM-eucKR EUC-KR" - echo "IBM-eucTW EUC-TW" - echo "big5 BIG5" - echo "GBK GBK" - echo "TIS-620 TIS-620" - echo "UTF-8 UTF-8" - ;; - hpux*) - echo "iso88591 ISO-8859-1" - echo "iso88592 ISO-8859-2" - echo "iso88595 ISO-8859-5" - echo "iso88596 ISO-8859-6" - echo "iso88597 ISO-8859-7" - echo "iso88598 ISO-8859-8" - echo "iso88599 ISO-8859-9" - echo "iso885915 ISO-8859-15" - echo "roman8 HP-ROMAN8" - echo "arabic8 HP-ARABIC8" - echo "greek8 HP-GREEK8" - echo "hebrew8 HP-HEBREW8" - echo "turkish8 HP-TURKISH8" - echo "kana8 HP-KANA8" - echo "tis620 TIS-620" - echo "big5 BIG5" - echo "eucJP EUC-JP" - echo "eucKR EUC-KR" - echo "eucTW EUC-TW" - echo "hp15CN GB2312" - #echo "ccdc ?" # what is this? - echo "SJIS SHIFT_JIS" - echo "utf8 UTF-8" - ;; - irix*) - echo "ISO8859-1 ISO-8859-1" - echo "ISO8859-2 ISO-8859-2" - echo "ISO8859-5 ISO-8859-5" - echo "ISO8859-7 ISO-8859-7" - echo "ISO8859-9 ISO-8859-9" - echo "eucCN GB2312" - echo "eucJP EUC-JP" - echo "eucKR EUC-KR" - echo "eucTW EUC-TW" - ;; - osf*) - echo "ISO8859-1 ISO-8859-1" - echo "ISO8859-2 ISO-8859-2" - echo "ISO8859-4 ISO-8859-4" - echo "ISO8859-5 ISO-8859-5" - echo "ISO8859-7 ISO-8859-7" - echo "ISO8859-8 ISO-8859-8" - echo "ISO8859-9 ISO-8859-9" - echo "ISO8859-15 ISO-8859-15" - echo "cp850 CP850" - echo "big5 BIG5" - echo "dechanyu DEC-HANYU" - echo "dechanzi GB2312" - echo "deckanji DEC-KANJI" - echo "deckorean EUC-KR" - echo "eucJP EUC-JP" - echo "eucKR EUC-KR" - echo "eucTW EUC-TW" - echo "GBK GBK" - echo "KSC5601 CP949" - echo "sdeckanji EUC-JP" - echo "SJIS SHIFT_JIS" - echo "TACTIS TIS-620" - echo "UTF-8 UTF-8" - ;; - solaris*) - echo "646 ASCII" - echo "ISO8859-1 ISO-8859-1" - echo "ISO8859-2 ISO-8859-2" - echo "ISO8859-3 ISO-8859-3" - echo "ISO8859-4 ISO-8859-4" - echo "ISO8859-5 ISO-8859-5" - echo "ISO8859-6 ISO-8859-6" - echo "ISO8859-7 ISO-8859-7" - echo "ISO8859-8 ISO-8859-8" - echo "ISO8859-9 ISO-8859-9" - echo "ISO8859-15 ISO-8859-15" - echo "koi8-r KOI8-R" - echo "ansi-1251 CP1251" - echo "BIG5 BIG5" - echo "Big5-HKSCS BIG5-HKSCS" - echo "gb2312 GB2312" - echo "GBK GBK" - echo "GB18030 GB18030" - echo "cns11643 EUC-TW" - echo "5601 EUC-KR" - echo "ko_KR.johap92 JOHAB" - echo "eucJP EUC-JP" - echo "PCK SHIFT_JIS" - echo "TIS620.2533 TIS-620" - #echo "sun_eu_greek ?" # what is this? - echo "UTF-8 UTF-8" - ;; - freebsd* | os2*) - # FreeBSD 4.2 doesn't have nl_langinfo(CODESET); therefore - # localcharset.c falls back to using the full locale name - # from the environment variables. - # Likewise for OS/2. OS/2 has XFree86 just like FreeBSD. Just - # reuse FreeBSD's locale data for OS/2. - echo "C ASCII" - echo "US-ASCII ASCII" - for l in la_LN lt_LN; do - echo "$l.ASCII ASCII" - done - for l in da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES \ - fi_FI fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT la_LN \ - lt_LN nl_BE nl_NL no_NO pt_PT sv_SE; do - echo "$l.ISO_8859-1 ISO-8859-1" - echo "$l.DIS_8859-15 ISO-8859-15" - done - for l in cs_CZ hr_HR hu_HU la_LN lt_LN pl_PL sl_SI; do - echo "$l.ISO_8859-2 ISO-8859-2" - done - for l in la_LN lt_LT; do - echo "$l.ISO_8859-4 ISO-8859-4" - done - for l in ru_RU ru_SU; do - echo "$l.KOI8-R KOI8-R" - echo "$l.ISO_8859-5 ISO-8859-5" - echo "$l.CP866 CP866" - done - echo "uk_UA.KOI8-U KOI8-U" - echo "zh_TW.BIG5 BIG5" - echo "zh_TW.Big5 BIG5" - echo "zh_CN.EUC GB2312" - echo "ja_JP.EUC EUC-JP" - echo "ja_JP.SJIS SHIFT_JIS" - echo "ja_JP.Shift_JIS SHIFT_JIS" - echo "ko_KR.EUC EUC-KR" - ;; - netbsd*) - echo "646 ASCII" - echo "ISO8859-1 ISO-8859-1" - echo "ISO8859-2 ISO-8859-2" - echo "ISO8859-4 ISO-8859-4" - echo "ISO8859-5 ISO-8859-5" - echo "ISO8859-15 ISO-8859-15" - echo "eucCN GB2312" - echo "eucJP EUC-JP" - echo "eucKR EUC-KR" - echo "eucTW EUC-TW" - echo "BIG5 BIG5" - echo "SJIS SHIFT_JIS" - ;; - beos*) - # BeOS has a single locale, and it has UTF-8 encoding. - echo "* UTF-8" - ;; - msdosdjgpp*) - # DJGPP 2.03 doesn't have nl_langinfo(CODESET); therefore - # localcharset.c falls back to using the full locale name - # from the environment variables. - echo "#" - echo "# The encodings given here may not all be correct." - echo "# If you find that the encoding given for your language and" - echo "# country is not the one your DOS machine actually uses, just" - echo "# correct it in this file, and send a mail to" - echo "# Juan Manuel Guerrero <st001906@hrz1.hrz.tu-darmstadt.de>" - echo "# and Bruno Haible <bruno@clisp.org>." - echo "#" - echo "C ASCII" - # ISO-8859-1 languages - echo "ca CP850" - echo "ca_ES CP850" - echo "da CP865" # not CP850 ?? - echo "da_DK CP865" # not CP850 ?? - echo "de CP850" - echo "de_AT CP850" - echo "de_CH CP850" - echo "de_DE CP850" - echo "en CP850" - echo "en_AU CP850" # not CP437 ?? - echo "en_CA CP850" - echo "en_GB CP850" - echo "en_NZ CP437" - echo "en_US CP437" - echo "en_ZA CP850" # not CP437 ?? - echo "es CP850" - echo "es_AR CP850" - echo "es_BO CP850" - echo "es_CL CP850" - echo "es_CO CP850" - echo "es_CR CP850" - echo "es_CU CP850" - echo "es_DO CP850" - echo "es_EC CP850" - echo "es_ES CP850" - echo "es_GT CP850" - echo "es_HN CP850" - echo "es_MX CP850" - echo "es_NI CP850" - echo "es_PA CP850" - echo "es_PY CP850" - echo "es_PE CP850" - echo "es_SV CP850" - echo "es_UY CP850" - echo "es_VE CP850" - echo "et CP850" - echo "et_EE CP850" - echo "eu CP850" - echo "eu_ES CP850" - echo "fi CP850" - echo "fi_FI CP850" - echo "fr CP850" - echo "fr_BE CP850" - echo "fr_CA CP850" - echo "fr_CH CP850" - echo "fr_FR CP850" - echo "ga CP850" - echo "ga_IE CP850" - echo "gd CP850" - echo "gd_GB CP850" - echo "gl CP850" - echo "gl_ES CP850" - echo "id CP850" # not CP437 ?? - echo "id_ID CP850" # not CP437 ?? - echo "is CP861" # not CP850 ?? - echo "is_IS CP861" # not CP850 ?? - echo "it CP850" - echo "it_CH CP850" - echo "it_IT CP850" - echo "lt CP775" - echo "lt_LT CP775" - echo "lv CP775" - echo "lv_LV CP775" - echo "nb CP865" # not CP850 ?? - echo "nb_NO CP865" # not CP850 ?? - echo "nl CP850" - echo "nl_BE CP850" - echo "nl_NL CP850" - echo "nn CP865" # not CP850 ?? - echo "nn_NO CP865" # not CP850 ?? - echo "no CP865" # not CP850 ?? - echo "no_NO CP865" # not CP850 ?? - echo "pt CP850" - echo "pt_BR CP850" - echo "pt_PT CP850" - echo "sv CP850" - echo "sv_SE CP850" - # ISO-8859-2 languages - echo "cs CP852" - echo "cs_CZ CP852" - echo "hr CP852" - echo "hr_HR CP852" - echo "hu CP852" - echo "hu_HU CP852" - echo "pl CP852" - echo "pl_PL CP852" - echo "ro CP852" - echo "ro_RO CP852" - echo "sk CP852" - echo "sk_SK CP852" - echo "sl CP852" - echo "sl_SI CP852" - echo "sq CP852" - echo "sq_AL CP852" - echo "sr CP852" # CP852 or CP866 or CP855 ?? - echo "sr_YU CP852" # CP852 or CP866 or CP855 ?? - # ISO-8859-3 languages - echo "mt CP850" - echo "mt_MT CP850" - # ISO-8859-5 languages - echo "be CP866" - echo "be_BE CP866" - echo "bg CP866" # not CP855 ?? - echo "bg_BG CP866" # not CP855 ?? - echo "mk CP866" # not CP855 ?? - echo "mk_MK CP866" # not CP855 ?? - echo "ru CP866" - echo "ru_RU CP866" - echo "uk CP1125" - echo "uk_UA CP1125" - # ISO-8859-6 languages - echo "ar CP864" - echo "ar_AE CP864" - echo "ar_DZ CP864" - echo "ar_EG CP864" - echo "ar_IQ CP864" - echo "ar_IR CP864" - echo "ar_JO CP864" - echo "ar_KW CP864" - echo "ar_MA CP864" - echo "ar_OM CP864" - echo "ar_QA CP864" - echo "ar_SA CP864" - echo "ar_SY CP864" - # ISO-8859-7 languages - echo "el CP869" - echo "el_GR CP869" - # ISO-8859-8 languages - echo "he CP862" - echo "he_IL CP862" - # ISO-8859-9 languages - echo "tr CP857" - echo "tr_TR CP857" - # Japanese - echo "ja CP932" - echo "ja_JP CP932" - # Chinese - echo "zh_CN GBK" - echo "zh_TW CP950" # not CP938 ?? - # Korean - echo "kr CP949" # not CP934 ?? - echo "kr_KR CP949" # not CP934 ?? - # Thai - echo "th CP874" - echo "th_TH CP874" - # Other - echo "eo CP850" - echo "eo_EO CP850" - ;; -esac diff --git a/src/audacious/intl/dcgettext.c b/src/audacious/intl/dcgettext.c deleted file mode 100644 index 253d4ec..0000000 --- a/src/audacious/intl/dcgettext.c +++ /dev/null @@ -1,59 +0,0 @@ -/* Implementation of the dcgettext(3) function. - Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include "gettextP.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif - -/* @@ end of prolog @@ */ - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define DCGETTEXT __dcgettext -# define DCIGETTEXT __dcigettext -#else -# define DCGETTEXT libintl_dcgettext -# define DCIGETTEXT libintl_dcigettext -#endif - -/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY - locale. */ -char * -DCGETTEXT (domainname, msgid, category) - const char *domainname; - const char *msgid; - int category; -{ - return DCIGETTEXT (domainname, msgid, NULL, 0, 0, category); -} - -#ifdef _LIBC -/* Alias for function name in GNU C Library. */ -INTDEF(__dcgettext) -weak_alias (__dcgettext, dcgettext); -#endif diff --git a/src/audacious/intl/dcigettext.c b/src/audacious/intl/dcigettext.c deleted file mode 100644 index 418a4c2..0000000 --- a/src/audacious/intl/dcigettext.c +++ /dev/null @@ -1,1238 +0,0 @@ -/* Implementation of the internal dcigettext function. - Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Tell glibc's <string.h> to provide a prototype for mempcpy(). - This must come before <config.h> because <config.h> may include - <features.h>, and once <features.h> has been included, it's too late. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <sys/types.h> - -#ifdef __GNUC__ -# define alloca __builtin_alloca -# define HAVE_ALLOCA 1 -#else -# ifdef _MSC_VER -# include <malloc.h> -# define alloca _alloca -# else -# if defined HAVE_ALLOCA_H || defined _LIBC -# include <alloca.h> -# else -# ifdef _AIX - #pragma alloca -# else -# ifndef alloca -char *alloca (); -# endif -# endif -# endif -# endif -#endif - -#include <errno.h> -#ifndef errno -extern int errno; -#endif -#ifndef __set_errno -# define __set_errno(val) errno = (val) -#endif - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#if defined HAVE_UNISTD_H || defined _LIBC -# include <unistd.h> -#endif - -#include <locale.h> - -#ifdef _LIBC - /* Guess whether integer division by zero raises signal SIGFPE. - Set to 1 only if you know for sure. In case of doubt, set to 0. */ -# if defined __alpha__ || defined __arm__ || defined __i386__ \ - || defined __m68k__ || defined __s390__ -# define INTDIV0_RAISES_SIGFPE 1 -# else -# define INTDIV0_RAISES_SIGFPE 0 -# endif -#endif -#if !INTDIV0_RAISES_SIGFPE -# include <signal.h> -#endif - -#if defined HAVE_SYS_PARAM_H || defined _LIBC -# include <sys/param.h> -#endif - -#include "gettextP.h" -#include "plural-exp.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif -#include "hash-string.h" - -/* Thread safetyness. */ -#ifdef _LIBC -# include <bits/libc-lock.h> -#else -/* Provide dummy implementation if this is outside glibc. */ -# define __libc_lock_define_initialized(CLASS, NAME) -# define __libc_lock_lock(NAME) -# define __libc_lock_unlock(NAME) -# define __libc_rwlock_define_initialized(CLASS, NAME) -# define __libc_rwlock_rdlock(NAME) -# define __libc_rwlock_unlock(NAME) -#endif - -/* Alignment of types. */ -#if defined __GNUC__ && __GNUC__ >= 2 -# define alignof(TYPE) __alignof__ (TYPE) -#else -# define alignof(TYPE) \ - ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2) -#endif - -/* The internal variables in the standalone libintl.a must have different - names than the internal variables in GNU libc, otherwise programs - using libintl.a cannot be linked statically. */ -#if !defined _LIBC -# define _nl_default_default_domain libintl_nl_default_default_domain -# define _nl_current_default_domain libintl_nl_current_default_domain -# define _nl_default_dirname libintl_nl_default_dirname -# define _nl_domain_bindings libintl_nl_domain_bindings -#endif - -/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>. */ -#ifndef offsetof -# define offsetof(type,ident) ((size_t)&(((type*)0)->ident)) -#endif - -/* @@ end of prolog @@ */ - -#ifdef _LIBC -/* Rename the non ANSI C functions. This is required by the standard - because some ANSI C functions will require linking with this object - file and the name space must not be polluted. */ -# define getcwd __getcwd -# ifndef stpcpy -# define stpcpy __stpcpy -# endif -# define tfind __tfind -#else -# if !defined HAVE_GETCWD -char *getwd (); -# define getcwd(buf, max) getwd (buf) -# else -char *getcwd (); -# endif -# ifndef HAVE_STPCPY -static char *stpcpy PARAMS ((char *dest, const char *src)); -# endif -# ifndef HAVE_MEMPCPY -static void *mempcpy PARAMS ((void *dest, const void *src, size_t n)); -# endif -#endif - -/* Amount to increase buffer size by in each try. */ -#define PATH_INCR 32 - -/* The following is from pathmax.h. */ -/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define - PATH_MAX but might cause redefinition warnings when sys/param.h is - later included (as on MORE/BSD 4.3). */ -#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__) -# include <limits.h> -#endif - -#ifndef _POSIX_PATH_MAX -# define _POSIX_PATH_MAX 255 -#endif - -#if !defined PATH_MAX && defined _PC_PATH_MAX -# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX)) -#endif - -/* Don't include sys/param.h if it already has been. */ -#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN -# include <sys/param.h> -#endif - -#if !defined PATH_MAX && defined MAXPATHLEN -# define PATH_MAX MAXPATHLEN -#endif - -#ifndef PATH_MAX -# define PATH_MAX _POSIX_PATH_MAX -#endif - -/* Pathname support. - ISSLASH(C) tests whether C is a directory separator character. - IS_ABSOLUTE_PATH(P) tests whether P is an absolute path. If it is not, - it may be concatenated to a directory pathname. - IS_PATH_WITH_DIR(P) tests whether P contains a directory specification. - */ -#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ - /* Win32, OS/2, DOS */ -# define ISSLASH(C) ((C) == '/' || (C) == '\\') -# define HAS_DEVICE(P) \ - ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ - && (P)[1] == ':') -# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P)) -# define IS_PATH_WITH_DIR(P) \ - (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P)) -#else - /* Unix */ -# define ISSLASH(C) ((C) == '/') -# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0]) -# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL) -#endif - -/* This is the type used for the search tree where known translations - are stored. */ -struct known_translation_t -{ - /* Domain in which to search. */ - char *domainname; - - /* The category. */ - int category; - - /* State of the catalog counter at the point the string was found. */ - int counter; - - /* Catalog where the string was found. */ - struct loaded_l10nfile *domain; - - /* And finally the translation. */ - const char *translation; - size_t translation_length; - - /* Pointer to the string in question. */ - char msgid[ZERO]; -}; - -/* Root of the search tree with known translations. We can use this - only if the system provides the `tsearch' function family. */ -#if defined HAVE_TSEARCH || defined _LIBC -# include <search.h> - -static void *root; - -# ifdef _LIBC -# define tsearch __tsearch -# endif - -/* Function to compare two entries in the table of known translations. */ -static int transcmp PARAMS ((const void *p1, const void *p2)); -static int -transcmp (p1, p2) - const void *p1; - const void *p2; -{ - const struct known_translation_t *s1; - const struct known_translation_t *s2; - int result; - - s1 = (const struct known_translation_t *) p1; - s2 = (const struct known_translation_t *) p2; - - result = strcmp (s1->msgid, s2->msgid); - if (result == 0) - { - result = strcmp (s1->domainname, s2->domainname); - if (result == 0) - /* We compare the category last (though this is the cheapest - operation) since it is hopefully always the same (namely - LC_MESSAGES). */ - result = s1->category - s2->category; - } - - return result; -} -#endif - -#ifndef INTVARDEF -# define INTVARDEF(name) -#endif -#ifndef INTUSE -# define INTUSE(name) name -#endif - -/* Name of the default domain used for gettext(3) prior any call to - textdomain(3). The default value for this is "messages". */ -const char _nl_default_default_domain[] attribute_hidden = "messages"; - -/* Value used as the default domain for gettext(3). */ -const char *_nl_current_default_domain attribute_hidden - = _nl_default_default_domain; - -/* Contains the default location of the message catalogs. */ -#if defined __EMX__ -extern const char _nl_default_dirname[]; -#else -const char _nl_default_dirname[] = LOCALEDIR; -INTVARDEF (_nl_default_dirname) -#endif - -/* List with bindings of specific domains created by bindtextdomain() - calls. */ -struct binding *_nl_domain_bindings; - -/* Prototypes for local functions. */ -static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain, - unsigned long int n, - const char *translation, - size_t translation_len)) - internal_function; -static const char *guess_category_value PARAMS ((int category, - const char *categoryname)) - internal_function; -#ifdef _LIBC -# include "../locale/localeinfo.h" -# define category_to_name(category) _nl_category_names[category] -#else -static const char *category_to_name PARAMS ((int category)) internal_function; -#endif - - -/* For those loosing systems which don't have `alloca' we have to add - some additional code emulating it. */ -#ifdef HAVE_ALLOCA -/* Nothing has to be done. */ -# define freea(p) /* nothing */ -# define ADD_BLOCK(list, address) /* nothing */ -# define FREE_BLOCKS(list) /* nothing */ -#else -struct block_list -{ - void *address; - struct block_list *next; -}; -# define ADD_BLOCK(list, addr) \ - do { \ - struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \ - /* If we cannot get a free block we cannot add the new element to \ - the list. */ \ - if (newp != NULL) { \ - newp->address = (addr); \ - newp->next = (list); \ - (list) = newp; \ - } \ - } while (0) -# define FREE_BLOCKS(list) \ - do { \ - while (list != NULL) { \ - struct block_list *old = list; \ - list = list->next; \ - free (old->address); \ - free (old); \ - } \ - } while (0) -# undef alloca -# define alloca(size) (malloc (size)) -# define freea(p) free (p) -#endif /* have alloca */ - - -#ifdef _LIBC -/* List of blocks allocated for translations. */ -typedef struct transmem_list -{ - struct transmem_list *next; - char data[ZERO]; -} transmem_block_t; -static struct transmem_list *transmem_list; -#else -typedef unsigned char transmem_block_t; -#endif - - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define DCIGETTEXT __dcigettext -#else -# define DCIGETTEXT libintl_dcigettext -#endif - -/* Lock variable to protect the global data in the gettext implementation. */ -#ifdef _LIBC -__libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden) -#endif - -/* Checking whether the binaries runs SUID must be done and glibc provides - easier methods therefore we make a difference here. */ -#ifdef _LIBC -# define ENABLE_SECURE __libc_enable_secure -# define DETERMINE_SECURE -#else -# ifndef HAVE_GETUID -# define getuid() 0 -# endif -# ifndef HAVE_GETGID -# define getgid() 0 -# endif -# ifndef HAVE_GETEUID -# define geteuid() getuid() -# endif -# ifndef HAVE_GETEGID -# define getegid() getgid() -# endif -static int enable_secure; -# define ENABLE_SECURE (enable_secure == 1) -# define DETERMINE_SECURE \ - if (enable_secure == 0) \ - { \ - if (getuid () != geteuid () || getgid () != getegid ()) \ - enable_secure = 1; \ - else \ - enable_secure = -1; \ - } -#endif - -/* Get the function to evaluate the plural expression. */ -#include "eval-plural.h" - -/* Look up MSGID in the DOMAINNAME message catalog for the current - CATEGORY locale and, if PLURAL is nonzero, search over string - depending on the plural form determined by N. */ -char * -DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) - const char *domainname; - const char *msgid1; - const char *msgid2; - int plural; - unsigned long int n; - int category; -{ -#ifndef HAVE_ALLOCA - struct block_list *block_list = NULL; -#endif - struct loaded_l10nfile *domain; - struct binding *binding; - const char *categoryname; - const char *categoryvalue; - char *dirname, *xdomainname; - char *single_locale; - char *retval; - size_t retlen; - int saved_errno; -#if defined HAVE_TSEARCH || defined _LIBC - struct known_translation_t *search; - struct known_translation_t **foundp = NULL; - size_t msgid_len; -#endif - size_t domainname_len; - - /* If no real MSGID is given return NULL. */ - if (msgid1 == NULL) - return NULL; - -#ifdef _LIBC - if (category < 0 || category >= __LC_LAST || category == LC_ALL) - /* Bogus. */ - return (plural == 0 - ? (char *) msgid1 - /* Use the Germanic plural rule. */ - : n == 1 ? (char *) msgid1 : (char *) msgid2); -#endif - - __libc_rwlock_rdlock (_nl_state_lock); - - /* If DOMAINNAME is NULL, we are interested in the default domain. If - CATEGORY is not LC_MESSAGES this might not make much sense but the - definition left this undefined. */ - if (domainname == NULL) - domainname = _nl_current_default_domain; - - /* OS/2 specific: backward compatibility with older libintl versions */ -#ifdef LC_MESSAGES_COMPAT - if (category == LC_MESSAGES_COMPAT) - category = LC_MESSAGES; -#endif - -#if defined HAVE_TSEARCH || defined _LIBC - msgid_len = strlen (msgid1) + 1; - - /* Try to find the translation among those which we found at - some time. */ - search = (struct known_translation_t *) - alloca (offsetof (struct known_translation_t, msgid) + msgid_len); - memcpy (search->msgid, msgid1, msgid_len); - search->domainname = (char *) domainname; - search->category = category; - - foundp = (struct known_translation_t **) tfind (search, &root, transcmp); - freea (search); - if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr) - { - /* Now deal with plural. */ - if (plural) - retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation, - (*foundp)->translation_length); - else - retval = (char *) (*foundp)->translation; - - __libc_rwlock_unlock (_nl_state_lock); - return retval; - } -#endif - - /* Preserve the `errno' value. */ - saved_errno = errno; - - /* See whether this is a SUID binary or not. */ - DETERMINE_SECURE; - - /* First find matching binding. */ - for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) - { - int compare = strcmp (domainname, binding->domainname); - if (compare == 0) - /* We found it! */ - break; - if (compare < 0) - { - /* It is not in the list. */ - binding = NULL; - break; - } - } - - if (binding == NULL) - dirname = (char *) INTUSE(_nl_default_dirname); - else if (IS_ABSOLUTE_PATH (binding->dirname)) - dirname = binding->dirname; - else - { - /* We have a relative path. Make it absolute now. */ - size_t dirname_len = strlen (binding->dirname) + 1; - size_t path_max; - char *ret; - - path_max = (unsigned int) PATH_MAX; - path_max += 2; /* The getcwd docs say to do this. */ - - for (;;) - { - dirname = (char *) alloca (path_max + dirname_len); - ADD_BLOCK (block_list, dirname); - - __set_errno (0); - ret = getcwd (dirname, path_max); - if (ret != NULL || errno != ERANGE) - break; - - path_max += path_max / 2; - path_max += PATH_INCR; - } - - if (ret == NULL) - /* We cannot get the current working directory. Don't signal an - error but simply return the default string. */ - goto return_untranslated; - - stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname); - } - - /* Now determine the symbolic name of CATEGORY and its value. */ - categoryname = category_to_name (category); - categoryvalue = guess_category_value (category, categoryname); - - domainname_len = strlen (domainname); - xdomainname = (char *) alloca (strlen (categoryname) - + domainname_len + 5); - ADD_BLOCK (block_list, xdomainname); - - stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"), - domainname, domainname_len), - ".mo"); - - /* Creating working area. */ - single_locale = (char *) alloca (strlen (categoryvalue) + 1); - ADD_BLOCK (block_list, single_locale); - - - /* Search for the given string. This is a loop because we perhaps - got an ordered list of languages to consider for the translation. */ - while (1) - { - /* Make CATEGORYVALUE point to the next element of the list. */ - while (categoryvalue[0] != '\0' && categoryvalue[0] == ':') - ++categoryvalue; - if (categoryvalue[0] == '\0') - { - /* The whole contents of CATEGORYVALUE has been searched but - no valid entry has been found. We solve this situation - by implicitly appending a "C" entry, i.e. no translation - will take place. */ - single_locale[0] = 'C'; - single_locale[1] = '\0'; - } - else - { - char *cp = single_locale; - while (categoryvalue[0] != '\0' && categoryvalue[0] != ':') - *cp++ = *categoryvalue++; - *cp = '\0'; - - /* When this is a SUID binary we must not allow accessing files - outside the dedicated directories. */ - if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale)) - /* Ingore this entry. */ - continue; - } - - /* If the current locale value is C (or POSIX) we don't load a - domain. Return the MSGID. */ - if (strcmp (single_locale, "C") == 0 - || strcmp (single_locale, "POSIX") == 0) - break; - - /* Find structure describing the message catalog matching the - DOMAINNAME and CATEGORY. */ - domain = _nl_find_domain (dirname, single_locale, xdomainname, binding); - - if (domain != NULL) - { - retval = _nl_find_msg (domain, binding, msgid1, &retlen); - - if (retval == NULL) - { - int cnt; - - for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) - { - retval = _nl_find_msg (domain->successor[cnt], binding, - msgid1, &retlen); - - if (retval != NULL) - { - domain = domain->successor[cnt]; - break; - } - } - } - - if (retval != NULL) - { - /* Found the translation of MSGID1 in domain DOMAIN: - starting at RETVAL, RETLEN bytes. */ - FREE_BLOCKS (block_list); -#if defined HAVE_TSEARCH || defined _LIBC - if (foundp == NULL) - { - /* Create a new entry and add it to the search tree. */ - struct known_translation_t *newp; - - newp = (struct known_translation_t *) - malloc (offsetof (struct known_translation_t, msgid) - + msgid_len + domainname_len + 1); - if (newp != NULL) - { - newp->domainname = - mempcpy (newp->msgid, msgid1, msgid_len); - memcpy (newp->domainname, domainname, domainname_len + 1); - newp->category = category; - newp->counter = _nl_msg_cat_cntr; - newp->domain = domain; - newp->translation = retval; - newp->translation_length = retlen; - - /* Insert the entry in the search tree. */ - foundp = (struct known_translation_t **) - tsearch (newp, &root, transcmp); - if (foundp == NULL - || __builtin_expect (*foundp != newp, 0)) - /* The insert failed. */ - free (newp); - } - } - else - { - /* We can update the existing entry. */ - (*foundp)->counter = _nl_msg_cat_cntr; - (*foundp)->domain = domain; - (*foundp)->translation = retval; - (*foundp)->translation_length = retlen; - } -#endif - __set_errno (saved_errno); - - /* Now deal with plural. */ - if (plural) - retval = plural_lookup (domain, n, retval, retlen); - - __libc_rwlock_unlock (_nl_state_lock); - return retval; - } - } - } - - return_untranslated: - /* Return the untranslated MSGID. */ - FREE_BLOCKS (block_list); - __libc_rwlock_unlock (_nl_state_lock); -#ifndef _LIBC - if (!ENABLE_SECURE) - { - extern void _nl_log_untranslated PARAMS ((const char *logfilename, - const char *domainname, - const char *msgid1, - const char *msgid2, - int plural)); - const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED"); - - if (logfilename != NULL && logfilename[0] != '\0') - _nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural); - } -#endif - __set_errno (saved_errno); - return (plural == 0 - ? (char *) msgid1 - /* Use the Germanic plural rule. */ - : n == 1 ? (char *) msgid1 : (char *) msgid2); -} - - -char * -internal_function -_nl_find_msg (domain_file, domainbinding, msgid, lengthp) - struct loaded_l10nfile *domain_file; - struct binding *domainbinding; - const char *msgid; - size_t *lengthp; -{ - struct loaded_domain *domain; - nls_uint32 nstrings; - size_t act; - char *result; - size_t resultlen; - - if (domain_file->decided == 0) - _nl_load_domain (domain_file, domainbinding); - - if (domain_file->data == NULL) - return NULL; - - domain = (struct loaded_domain *) domain_file->data; - - nstrings = domain->nstrings; - - /* Locate the MSGID and its translation. */ - if (domain->hash_tab != NULL) - { - /* Use the hashing table. */ - nls_uint32 len = strlen (msgid); - nls_uint32 hash_val = hash_string (msgid); - nls_uint32 idx = hash_val % domain->hash_size; - nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); - - while (1) - { - nls_uint32 nstr = - W (domain->must_swap_hash_tab, domain->hash_tab[idx]); - - if (nstr == 0) - /* Hash table entry is empty. */ - return NULL; - - nstr--; - - /* Compare msgid with the original string at index nstr. - We compare the lengths with >=, not ==, because plural entries - are represented by strings with an embedded NUL. */ - if (nstr < nstrings - ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len - && (strcmp (msgid, - domain->data + W (domain->must_swap, - domain->orig_tab[nstr].offset)) - == 0) - : domain->orig_sysdep_tab[nstr - nstrings].length > len - && (strcmp (msgid, - domain->orig_sysdep_tab[nstr - nstrings].pointer) - == 0)) - { - act = nstr; - goto found; - } - - if (idx >= domain->hash_size - incr) - idx -= domain->hash_size - incr; - else - idx += incr; - } - /* NOTREACHED */ - } - else - { - /* Try the default method: binary search in the sorted array of - messages. */ - size_t top, bottom; - - bottom = 0; - top = nstrings; - while (bottom < top) - { - int cmp_val; - - act = (bottom + top) / 2; - cmp_val = strcmp (msgid, (domain->data - + W (domain->must_swap, - domain->orig_tab[act].offset))); - if (cmp_val < 0) - top = act; - else if (cmp_val > 0) - bottom = act + 1; - else - goto found; - } - /* No translation was found. */ - return NULL; - } - - found: - /* The translation was found at index ACT. If we have to convert the - string to use a different character set, this is the time. */ - if (act < nstrings) - { - result = (char *) - (domain->data + W (domain->must_swap, domain->trans_tab[act].offset)); - resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1; - } - else - { - result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer; - resultlen = domain->trans_sysdep_tab[act - nstrings].length; - } - -#if defined _LIBC || HAVE_ICONV - if (domain->codeset_cntr - != (domainbinding != NULL ? domainbinding->codeset_cntr : 0)) - { - /* The domain's codeset has changed through bind_textdomain_codeset() - since the message catalog was initialized or last accessed. We - have to reinitialize the converter. */ - _nl_free_domain_conv (domain); - _nl_init_domain_conv (domain_file, domain, domainbinding); - } - - if ( -# ifdef _LIBC - domain->conv != (__gconv_t) -1 -# else -# if HAVE_ICONV - domain->conv != (iconv_t) -1 -# endif -# endif - ) - { - /* We are supposed to do a conversion. First allocate an - appropriate table with the same structure as the table - of translations in the file, where we can put the pointers - to the converted strings in. - There is a slight complication with plural entries. They - are represented by consecutive NUL terminated strings. We - handle this case by converting RESULTLEN bytes, including - NULs. */ - - if (domain->conv_tab == NULL - && ((domain->conv_tab = - (char **) calloc (nstrings + domain->n_sysdep_strings, - sizeof (char *))) - == NULL)) - /* Mark that we didn't succeed allocating a table. */ - domain->conv_tab = (char **) -1; - - if (__builtin_expect (domain->conv_tab == (char **) -1, 0)) - /* Nothing we can do, no more memory. */ - goto converted; - - if (domain->conv_tab[act] == NULL) - { - /* We haven't used this string so far, so it is not - translated yet. Do this now. */ - /* We use a bit more efficient memory handling. - We allocate always larger blocks which get used over - time. This is faster than many small allocations. */ - __libc_lock_define_initialized (static, lock) -# define INITIAL_BLOCK_SIZE 4080 - static unsigned char *freemem; - static size_t freemem_size; - - const unsigned char *inbuf; - unsigned char *outbuf; - int malloc_count; -# ifndef _LIBC - transmem_block_t *transmem_list = NULL; -# endif - - __libc_lock_lock (lock); - - inbuf = (const unsigned char *) result; - outbuf = freemem + sizeof (size_t); - - malloc_count = 0; - while (1) - { - transmem_block_t *newmem; -# ifdef _LIBC - size_t non_reversible; - int res; - - if (freemem_size < sizeof (size_t)) - goto resize_freemem; - - res = __gconv (domain->conv, - &inbuf, inbuf + resultlen, - &outbuf, - outbuf + freemem_size - sizeof (size_t), - &non_reversible); - - if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT) - break; - - if (res != __GCONV_FULL_OUTPUT) - { - __libc_lock_unlock (lock); - goto converted; - } - - inbuf = result; -# else -# if HAVE_ICONV - const char *inptr = (const char *) inbuf; - size_t inleft = resultlen; - char *outptr = (char *) outbuf; - size_t outleft; - - if (freemem_size < sizeof (size_t)) - goto resize_freemem; - - outleft = freemem_size - sizeof (size_t); - if (iconv (domain->conv, - (ICONV_CONST char **) &inptr, &inleft, - &outptr, &outleft) - != (size_t) (-1)) - { - outbuf = (unsigned char *) outptr; - break; - } - if (errno != E2BIG) - { - __libc_lock_unlock (lock); - goto converted; - } -# endif -# endif - - resize_freemem: - /* We must allocate a new buffer or resize the old one. */ - if (malloc_count > 0) - { - ++malloc_count; - freemem_size = malloc_count * INITIAL_BLOCK_SIZE; - newmem = (transmem_block_t *) realloc (transmem_list, - freemem_size); -# ifdef _LIBC - if (newmem != NULL) - transmem_list = transmem_list->next; - else - { - struct transmem_list *old = transmem_list; - - transmem_list = transmem_list->next; - free (old); - } -# endif - } - else - { - malloc_count = 1; - freemem_size = INITIAL_BLOCK_SIZE; - newmem = (transmem_block_t *) malloc (freemem_size); - } - if (__builtin_expect (newmem == NULL, 0)) - { - freemem = NULL; - freemem_size = 0; - __libc_lock_unlock (lock); - goto converted; - } - -# ifdef _LIBC - /* Add the block to the list of blocks we have to free - at some point. */ - newmem->next = transmem_list; - transmem_list = newmem; - - freemem = newmem->data; - freemem_size -= offsetof (struct transmem_list, data); -# else - transmem_list = newmem; - freemem = newmem; -# endif - - outbuf = freemem + sizeof (size_t); - } - - /* We have now in our buffer a converted string. Put this - into the table of conversions. */ - *(size_t *) freemem = outbuf - freemem - sizeof (size_t); - domain->conv_tab[act] = (char *) freemem; - /* Shrink freemem, but keep it aligned. */ - freemem_size -= outbuf - freemem; - freemem = outbuf; - freemem += freemem_size & (alignof (size_t) - 1); - freemem_size = freemem_size & ~ (alignof (size_t) - 1); - - __libc_lock_unlock (lock); - } - - /* Now domain->conv_tab[act] contains the translation of all - the plural variants. */ - result = domain->conv_tab[act] + sizeof (size_t); - resultlen = *(size_t *) domain->conv_tab[act]; - } - - converted: - /* The result string is converted. */ - -#endif /* _LIBC || HAVE_ICONV */ - - *lengthp = resultlen; - return result; -} - - -/* Look up a plural variant. */ -static char * -internal_function -plural_lookup (domain, n, translation, translation_len) - struct loaded_l10nfile *domain; - unsigned long int n; - const char *translation; - size_t translation_len; -{ - struct loaded_domain *domaindata = (struct loaded_domain *) domain->data; - unsigned long int index; - const char *p; - - index = plural_eval (domaindata->plural, n); - if (index >= domaindata->nplurals) - /* This should never happen. It means the plural expression and the - given maximum value do not match. */ - index = 0; - - /* Skip INDEX strings at TRANSLATION. */ - p = translation; - while (index-- > 0) - { -#ifdef _LIBC - p = __rawmemchr (p, '\0'); -#else - p = strchr (p, '\0'); -#endif - /* And skip over the NUL byte. */ - p++; - - if (p >= translation + translation_len) - /* This should never happen. It means the plural expression - evaluated to a value larger than the number of variants - available for MSGID1. */ - return (char *) translation; - } - return (char *) p; -} - -#ifndef _LIBC -/* Return string representation of locale CATEGORY. */ -static const char * -internal_function -category_to_name (category) - int category; -{ - const char *retval; - - switch (category) - { -#ifdef LC_COLLATE - case LC_COLLATE: - retval = "LC_COLLATE"; - break; -#endif -#ifdef LC_CTYPE - case LC_CTYPE: - retval = "LC_CTYPE"; - break; -#endif -#ifdef LC_MONETARY - case LC_MONETARY: - retval = "LC_MONETARY"; - break; -#endif -#ifdef LC_NUMERIC - case LC_NUMERIC: - retval = "LC_NUMERIC"; - break; -#endif -#ifdef LC_TIME - case LC_TIME: - retval = "LC_TIME"; - break; -#endif -#ifdef LC_MESSAGES - case LC_MESSAGES: - retval = "LC_MESSAGES"; - break; -#endif -#ifdef LC_RESPONSE - case LC_RESPONSE: - retval = "LC_RESPONSE"; - break; -#endif -#ifdef LC_ALL - case LC_ALL: - /* This might not make sense but is perhaps better than any other - value. */ - retval = "LC_ALL"; - break; -#endif - default: - /* If you have a better idea for a default value let me know. */ - retval = "LC_XXX"; - } - - return retval; -} -#endif - -/* Guess value of current locale from value of the environment variables. */ -static const char * -internal_function -guess_category_value (category, categoryname) - int category; - const char *categoryname; -{ - const char *language; - const char *retval; - - /* The highest priority value is the `LANGUAGE' environment - variable. But we don't use the value if the currently selected - locale is the C locale. This is a GNU extension. */ - language = getenv ("LANGUAGE"); - if (language != NULL && language[0] == '\0') - language = NULL; - - /* We have to proceed with the POSIX methods of looking to `LC_ALL', - `LC_xxx', and `LANG'. On some systems this can be done by the - `setlocale' function itself. */ -#ifdef _LIBC - retval = __current_locale_name (category); -#else - retval = _nl_locale_name (category, categoryname); -#endif - - /* Ignore LANGUAGE if the locale is set to "C" because - 1. "C" locale usually uses the ASCII encoding, and most international - messages use non-ASCII characters. These characters get displayed - as question marks (if using glibc's iconv()) or as invalid 8-bit - characters (because other iconv()s refuse to convert most non-ASCII - characters to ASCII). In any case, the output is ugly. - 2. The precise output of some programs in the "C" locale is specified - by POSIX and should not depend on environment variables like - "LANGUAGE". We allow such programs to use gettext(). */ - return language != NULL && strcmp (retval, "C") != 0 ? language : retval; -} - -/* @@ begin of epilog @@ */ - -/* We don't want libintl.a to depend on any other library. So we - avoid the non-standard function stpcpy. In GNU C Library this - function is available, though. Also allow the symbol HAVE_STPCPY - to be defined. */ -#if !_LIBC && !HAVE_STPCPY -static char * -stpcpy (dest, src) - char *dest; - const char *src; -{ - while ((*dest++ = *src++) != '\0') - /* Do nothing. */ ; - return dest - 1; -} -#endif - -#if !_LIBC && !HAVE_MEMPCPY -static void * -mempcpy (dest, src, n) - void *dest; - const void *src; - size_t n; -{ - return (void *) ((char *) memcpy (dest, src, n) + n); -} -#endif - - -#ifdef _LIBC -/* If we want to free all resources we have to do some work at - program's end. */ -libc_freeres_fn (free_mem) -{ - void *old; - - while (_nl_domain_bindings != NULL) - { - struct binding *oldp = _nl_domain_bindings; - _nl_domain_bindings = _nl_domain_bindings->next; - if (oldp->dirname != INTUSE(_nl_default_dirname)) - /* Yes, this is a pointer comparison. */ - free (oldp->dirname); - free (oldp->codeset); - free (oldp); - } - - if (_nl_current_default_domain != _nl_default_default_domain) - /* Yes, again a pointer comparison. */ - free ((char *) _nl_current_default_domain); - - /* Remove the search tree with the known translations. */ - __tdestroy (root, free); - root = NULL; - - while (transmem_list != NULL) - { - old = transmem_list; - transmem_list = transmem_list->next; - free (old); - } -} -#endif diff --git a/src/audacious/intl/dcngettext.c b/src/audacious/intl/dcngettext.c deleted file mode 100644 index 7006978..0000000 --- a/src/audacious/intl/dcngettext.c +++ /dev/null @@ -1,60 +0,0 @@ -/* Implementation of the dcngettext(3) function. - Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include "gettextP.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif - -/* @@ end of prolog @@ */ - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define DCNGETTEXT __dcngettext -# define DCIGETTEXT __dcigettext -#else -# define DCNGETTEXT libintl_dcngettext -# define DCIGETTEXT libintl_dcigettext -#endif - -/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY - locale. */ -char * -DCNGETTEXT (domainname, msgid1, msgid2, n, category) - const char *domainname; - const char *msgid1; - const char *msgid2; - unsigned long int n; - int category; -{ - return DCIGETTEXT (domainname, msgid1, msgid2, 1, n, category); -} - -#ifdef _LIBC -/* Alias for function name in GNU C Library. */ -weak_alias (__dcngettext, dcngettext); -#endif diff --git a/src/audacious/intl/dgettext.c b/src/audacious/intl/dgettext.c deleted file mode 100644 index 01789d3..0000000 --- a/src/audacious/intl/dgettext.c +++ /dev/null @@ -1,59 +0,0 @@ -/* Implementation of the dgettext(3) function. - Copyright (C) 1995-1997, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <locale.h> - -#include "gettextP.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif - -/* @@ end of prolog @@ */ - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define DGETTEXT __dgettext -# define DCGETTEXT INTUSE(__dcgettext) -#else -# define DGETTEXT libintl_dgettext -# define DCGETTEXT libintl_dcgettext -#endif - -/* Look up MSGID in the DOMAINNAME message catalog of the current - LC_MESSAGES locale. */ -char * -DGETTEXT (domainname, msgid) - const char *domainname; - const char *msgid; -{ - return DCGETTEXT (domainname, msgid, LC_MESSAGES); -} - -#ifdef _LIBC -/* Alias for function name in GNU C Library. */ -weak_alias (__dgettext, dgettext); -#endif diff --git a/src/audacious/intl/dngettext.c b/src/audacious/intl/dngettext.c deleted file mode 100644 index cfcae12..0000000 --- a/src/audacious/intl/dngettext.c +++ /dev/null @@ -1,61 +0,0 @@ -/* Implementation of the dngettext(3) function. - Copyright (C) 1995-1997, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <locale.h> - -#include "gettextP.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif - -/* @@ end of prolog @@ */ - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define DNGETTEXT __dngettext -# define DCNGETTEXT __dcngettext -#else -# define DNGETTEXT libintl_dngettext -# define DCNGETTEXT libintl_dcngettext -#endif - -/* Look up MSGID in the DOMAINNAME message catalog of the current - LC_MESSAGES locale and skip message according to the plural form. */ -char * -DNGETTEXT (domainname, msgid1, msgid2, n) - const char *domainname; - const char *msgid1; - const char *msgid2; - unsigned long int n; -{ - return DCNGETTEXT (domainname, msgid1, msgid2, n, LC_MESSAGES); -} - -#ifdef _LIBC -/* Alias for function name in GNU C Library. */ -weak_alias (__dngettext, dngettext); -#endif diff --git a/src/audacious/intl/eval-plural.h b/src/audacious/intl/eval-plural.h deleted file mode 100644 index 18f40ae..0000000 --- a/src/audacious/intl/eval-plural.h +++ /dev/null @@ -1,114 +0,0 @@ -/* Plural expression evaluation. - Copyright (C) 2000-2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef STATIC -#define STATIC static -#endif - -/* Evaluate the plural expression and return an index value. */ -STATIC unsigned long int plural_eval PARAMS ((struct expression *pexp, - unsigned long int n)) - internal_function; - -STATIC -unsigned long int -internal_function -plural_eval (pexp, n) - struct expression *pexp; - unsigned long int n; -{ - switch (pexp->nargs) - { - case 0: - switch (pexp->operation) - { - case var: - return n; - case num: - return pexp->val.num; - default: - break; - } - /* NOTREACHED */ - break; - case 1: - { - /* pexp->operation must be lnot. */ - unsigned long int arg = plural_eval (pexp->val.args[0], n); - return ! arg; - } - case 2: - { - unsigned long int leftarg = plural_eval (pexp->val.args[0], n); - if (pexp->operation == lor) - return leftarg || plural_eval (pexp->val.args[1], n); - else if (pexp->operation == land) - return leftarg && plural_eval (pexp->val.args[1], n); - else - { - unsigned long int rightarg = plural_eval (pexp->val.args[1], n); - - switch (pexp->operation) - { - case mult: - return leftarg * rightarg; - case divide: -#if !INTDIV0_RAISES_SIGFPE - if (rightarg == 0) - raise (SIGFPE); -#endif - return leftarg / rightarg; - case module: -#if !INTDIV0_RAISES_SIGFPE - if (rightarg == 0) - raise (SIGFPE); -#endif - return leftarg % rightarg; - case plus: - return leftarg + rightarg; - case minus: - return leftarg - rightarg; - case less_than: - return leftarg < rightarg; - case greater_than: - return leftarg > rightarg; - case less_or_equal: - return leftarg <= rightarg; - case greater_or_equal: - return leftarg >= rightarg; - case equal: - return leftarg == rightarg; - case not_equal: - return leftarg != rightarg; - default: - break; - } - } - /* NOTREACHED */ - break; - } - case 3: - { - /* pexp->operation must be qmop. */ - unsigned long int boolarg = plural_eval (pexp->val.args[0], n); - return plural_eval (pexp->val.args[boolarg ? 1 : 2], n); - } - } - /* NOTREACHED */ - return 0; -} diff --git a/src/audacious/intl/explodename.c b/src/audacious/intl/explodename.c deleted file mode 100644 index c47ef63..0000000 --- a/src/audacious/intl/explodename.c +++ /dev/null @@ -1,192 +0,0 @@ -/* Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc. - Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> - -#include "loadinfo.h" - -/* On some strange systems still no definition of NULL is found. Sigh! */ -#ifndef NULL -# if defined __STDC__ && __STDC__ -# define NULL ((void *) 0) -# else -# define NULL 0 -# endif -#endif - -/* @@ end of prolog @@ */ - -char * -_nl_find_language (name) - const char *name; -{ - while (name[0] != '\0' && name[0] != '_' && name[0] != '@' - && name[0] != '+' && name[0] != ',') - ++name; - - return (char *) name; -} - - -int -_nl_explode_name (name, language, modifier, territory, codeset, - normalized_codeset, special, sponsor, revision) - char *name; - const char **language; - const char **modifier; - const char **territory; - const char **codeset; - const char **normalized_codeset; - const char **special; - const char **sponsor; - const char **revision; -{ - enum { undecided, xpg, cen } syntax; - char *cp; - int mask; - - *modifier = NULL; - *territory = NULL; - *codeset = NULL; - *normalized_codeset = NULL; - *special = NULL; - *sponsor = NULL; - *revision = NULL; - - /* Now we determine the single parts of the locale name. First - look for the language. Termination symbols are `_' and `@' if - we use XPG4 style, and `_', `+', and `,' if we use CEN syntax. */ - mask = 0; - syntax = undecided; - *language = cp = name; - cp = _nl_find_language (*language); - - if (*language == cp) - /* This does not make sense: language has to be specified. Use - this entry as it is without exploding. Perhaps it is an alias. */ - cp = strchr (*language, '\0'); - else if (cp[0] == '_') - { - /* Next is the territory. */ - cp[0] = '\0'; - *territory = ++cp; - - while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@' - && cp[0] != '+' && cp[0] != ',' && cp[0] != '_') - ++cp; - - mask |= TERRITORY; - - if (cp[0] == '.') - { - /* Next is the codeset. */ - syntax = xpg; - cp[0] = '\0'; - *codeset = ++cp; - - while (cp[0] != '\0' && cp[0] != '@') - ++cp; - - mask |= XPG_CODESET; - - if (*codeset != cp && (*codeset)[0] != '\0') - { - *normalized_codeset = _nl_normalize_codeset (*codeset, - cp - *codeset); - if (strcmp (*codeset, *normalized_codeset) == 0) - free ((char *) *normalized_codeset); - else - mask |= XPG_NORM_CODESET; - } - } - } - - if (cp[0] == '@' || (syntax != xpg && cp[0] == '+')) - { - /* Next is the modifier. */ - syntax = cp[0] == '@' ? xpg : cen; - cp[0] = '\0'; - *modifier = ++cp; - - while (syntax == cen && cp[0] != '\0' && cp[0] != '+' - && cp[0] != ',' && cp[0] != '_') - ++cp; - - mask |= XPG_MODIFIER | CEN_AUDIENCE; - } - - if (syntax != xpg && (cp[0] == '+' || cp[0] == ',' || cp[0] == '_')) - { - syntax = cen; - - if (cp[0] == '+') - { - /* Next is special application (CEN syntax). */ - cp[0] = '\0'; - *special = ++cp; - - while (cp[0] != '\0' && cp[0] != ',' && cp[0] != '_') - ++cp; - - mask |= CEN_SPECIAL; - } - - if (cp[0] == ',') - { - /* Next is sponsor (CEN syntax). */ - cp[0] = '\0'; - *sponsor = ++cp; - - while (cp[0] != '\0' && cp[0] != '_') - ++cp; - - mask |= CEN_SPONSOR; - } - - if (cp[0] == '_') - { - /* Next is revision (CEN syntax). */ - cp[0] = '\0'; - *revision = ++cp; - - mask |= CEN_REVISION; - } - } - - /* For CEN syntax values it might be important to have the - separator character in the file name, not for XPG syntax. */ - if (syntax == xpg) - { - if (*territory != NULL && (*territory)[0] == '\0') - mask &= ~TERRITORY; - - if (*codeset != NULL && (*codeset)[0] == '\0') - mask &= ~XPG_CODESET; - - if (*modifier != NULL && (*modifier)[0] == '\0') - mask &= ~XPG_MODIFIER; - } - - return mask; -} diff --git a/src/audacious/intl/finddomain.c b/src/audacious/intl/finddomain.c deleted file mode 100644 index 5d209fb..0000000 --- a/src/audacious/intl/finddomain.c +++ /dev/null @@ -1,195 +0,0 @@ -/* Handle list of needed message catalogs - Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc. - Written by Ulrich Drepper <drepper@gnu.org>, 1995. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stdio.h> -#include <sys/types.h> -#include <stdlib.h> -#include <string.h> - -#if defined HAVE_UNISTD_H || defined _LIBC -# include <unistd.h> -#endif - -#include "gettextP.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif - -/* @@ end of prolog @@ */ -/* List of already loaded domains. */ -static struct loaded_l10nfile *_nl_loaded_domains; - - -/* Return a data structure describing the message catalog described by - the DOMAINNAME and CATEGORY parameters with respect to the currently - established bindings. */ -struct loaded_l10nfile * -internal_function -_nl_find_domain (dirname, locale, domainname, domainbinding) - const char *dirname; - char *locale; - const char *domainname; - struct binding *domainbinding; -{ - struct loaded_l10nfile *retval; - const char *language; - const char *modifier; - const char *territory; - const char *codeset; - const char *normalized_codeset; - const char *special; - const char *sponsor; - const char *revision; - const char *alias_value; - int mask; - - /* LOCALE can consist of up to four recognized parts for the XPG syntax: - - language[_territory[.codeset]][@modifier] - - and six parts for the CEN syntax: - - language[_territory][+audience][+special][,[sponsor][_revision]] - - Beside the first part all of them are allowed to be missing. If - the full specified locale is not found, the less specific one are - looked for. The various parts will be stripped off according to - the following order: - (1) revision - (2) sponsor - (3) special - (4) codeset - (5) normalized codeset - (6) territory - (7) audience/modifier - */ - - /* If we have already tested for this locale entry there has to - be one data set in the list of loaded domains. */ - retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname, - strlen (dirname) + 1, 0, locale, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, domainname, 0); - if (retval != NULL) - { - /* We know something about this locale. */ - int cnt; - - if (retval->decided == 0) - _nl_load_domain (retval, domainbinding); - - if (retval->data != NULL) - return retval; - - for (cnt = 0; retval->successor[cnt] != NULL; ++cnt) - { - if (retval->successor[cnt]->decided == 0) - _nl_load_domain (retval->successor[cnt], domainbinding); - - if (retval->successor[cnt]->data != NULL) - break; - } - return cnt >= 0 ? retval : NULL; - /* NOTREACHED */ - } - - /* See whether the locale value is an alias. If yes its value - *overwrites* the alias name. No test for the original value is - done. */ - alias_value = _nl_expand_alias (locale); - if (alias_value != NULL) - { -#if defined _LIBC || defined HAVE_STRDUP - locale = strdup (alias_value); - if (locale == NULL) - return NULL; -#else - size_t len = strlen (alias_value) + 1; - locale = (char *) malloc (len); - if (locale == NULL) - return NULL; - - memcpy (locale, alias_value, len); -#endif - } - - /* Now we determine the single parts of the locale name. First - look for the language. Termination symbols are `_' and `@' if - we use XPG4 style, and `_', `+', and `,' if we use CEN syntax. */ - mask = _nl_explode_name (locale, &language, &modifier, &territory, - &codeset, &normalized_codeset, &special, - &sponsor, &revision); - - /* Create all possible locale entries which might be interested in - generalization. */ - retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname, - strlen (dirname) + 1, mask, language, territory, - codeset, normalized_codeset, modifier, special, - sponsor, revision, domainname, 1); - if (retval == NULL) - /* This means we are out of core. */ - return NULL; - - if (retval->decided == 0) - _nl_load_domain (retval, domainbinding); - if (retval->data == NULL) - { - int cnt; - for (cnt = 0; retval->successor[cnt] != NULL; ++cnt) - { - if (retval->successor[cnt]->decided == 0) - _nl_load_domain (retval->successor[cnt], domainbinding); - if (retval->successor[cnt]->data != NULL) - break; - } - } - - /* The room for an alias was dynamically allocated. Free it now. */ - if (alias_value != NULL) - free (locale); - - /* The space for normalized_codeset is dynamically allocated. Free it. */ - if (mask & XPG_NORM_CODESET) - free ((void *) normalized_codeset); - - return retval; -} - - -#ifdef _LIBC -libc_freeres_fn (free_mem) -{ - struct loaded_l10nfile *runp = _nl_loaded_domains; - - while (runp != NULL) - { - struct loaded_l10nfile *here = runp; - if (runp->data != NULL) - _nl_unload_domain ((struct loaded_domain *) runp->data); - runp = runp->next; - free ((char *) here->filename); - free (here); - } -} -#endif diff --git a/src/audacious/intl/gettext.c b/src/audacious/intl/gettext.c deleted file mode 100644 index affc1a0..0000000 --- a/src/audacious/intl/gettext.c +++ /dev/null @@ -1,64 +0,0 @@ -/* Implementation of gettext(3) function. - Copyright (C) 1995, 1997, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#ifdef _LIBC -# define __need_NULL -# include <stddef.h> -#else -# include <stdlib.h> /* Just for NULL. */ -#endif - -#include "gettextP.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif - -/* @@ end of prolog @@ */ - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define GETTEXT __gettext -# define DCGETTEXT INTUSE(__dcgettext) -#else -# define GETTEXT libintl_gettext -# define DCGETTEXT libintl_dcgettext -#endif - -/* Look up MSGID in the current default message catalog for the current - LC_MESSAGES locale. If not found, returns MSGID itself (the default - text). */ -char * -GETTEXT (msgid) - const char *msgid; -{ - return DCGETTEXT (NULL, msgid, LC_MESSAGES); -} - -#ifdef _LIBC -/* Alias for function name in GNU C Library. */ -weak_alias (__gettext, gettext); -#endif diff --git a/src/audacious/intl/gettextP.h b/src/audacious/intl/gettextP.h deleted file mode 100644 index 5df0552..0000000 --- a/src/audacious/intl/gettextP.h +++ /dev/null @@ -1,224 +0,0 @@ -/* Header describing internals of libintl library. - Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc. - Written by Ulrich Drepper <drepper@cygnus.com>, 1995. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef _GETTEXTP_H -#define _GETTEXTP_H - -#include <stddef.h> /* Get size_t. */ - -#ifdef _LIBC -# include "../iconv/gconv_int.h" -#else -# if HAVE_ICONV -# include <iconv.h> -# endif -#endif - -#include "loadinfo.h" - -#include "gmo.h" /* Get nls_uint32. */ - -/* @@ end of prolog @@ */ - -#ifndef PARAMS -# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES -# define PARAMS(args) args -# else -# define PARAMS(args) () -# endif -#endif - -#ifndef internal_function -# define internal_function -#endif - -#ifndef attribute_hidden -# define attribute_hidden -#endif - -/* Tell the compiler when a conditional or integer expression is - almost always true or almost always false. */ -#ifndef HAVE_BUILTIN_EXPECT -# define __builtin_expect(expr, val) (expr) -#endif - -#ifndef W -# define W(flag, data) ((flag) ? SWAP (data) : (data)) -#endif - - -#ifdef _LIBC -# include <byteswap.h> -# define SWAP(i) bswap_32 (i) -#else -static inline nls_uint32 -SWAP (i) - nls_uint32 i; -{ - return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24); -} -#endif - - -/* In-memory representation of system dependent string. */ -struct sysdep_string_desc -{ - /* Length of addressed string, including the trailing NUL. */ - size_t length; - /* Pointer to addressed string. */ - const char *pointer; -}; - -/* The representation of an opened message catalog. */ -struct loaded_domain -{ - /* Pointer to memory containing the .mo file. */ - const char *data; - /* 1 if the memory is mmap()ed, 0 if the memory is malloc()ed. */ - int use_mmap; - /* Size of mmap()ed memory. */ - size_t mmap_size; - /* 1 if the .mo file uses a different endianness than this machine. */ - int must_swap; - /* Pointer to additional malloc()ed memory. */ - void *malloced; - - /* Number of static strings pairs. */ - nls_uint32 nstrings; - /* Pointer to descriptors of original strings in the file. */ - const struct string_desc *orig_tab; - /* Pointer to descriptors of translated strings in the file. */ - const struct string_desc *trans_tab; - - /* Number of system dependent strings pairs. */ - nls_uint32 n_sysdep_strings; - /* Pointer to descriptors of original sysdep strings. */ - const struct sysdep_string_desc *orig_sysdep_tab; - /* Pointer to descriptors of translated sysdep strings. */ - const struct sysdep_string_desc *trans_sysdep_tab; - - /* Size of hash table. */ - nls_uint32 hash_size; - /* Pointer to hash table. */ - const nls_uint32 *hash_tab; - /* 1 if the hash table uses a different endianness than this machine. */ - int must_swap_hash_tab; - - int codeset_cntr; -#ifdef _LIBC - __gconv_t conv; -#else -# if HAVE_ICONV - iconv_t conv; -# endif -#endif - char **conv_tab; - - struct expression *plural; - unsigned long int nplurals; -}; - -/* We want to allocate a string at the end of the struct. But ISO C - doesn't allow zero sized arrays. */ -#ifdef __GNUC__ -# define ZERO 0 -#else -# define ZERO 1 -#endif - -/* A set of settings bound to a message domain. Used to store settings - from bindtextdomain() and bind_textdomain_codeset(). */ -struct binding -{ - struct binding *next; - char *dirname; - int codeset_cntr; /* Incremented each time codeset changes. */ - char *codeset; - char domainname[ZERO]; -}; - -/* A counter which is incremented each time some previous translations - become invalid. - This variable is part of the external ABI of the GNU libintl. */ -extern int _nl_msg_cat_cntr; - -#ifndef _LIBC -const char *_nl_locale_name PARAMS ((int category, const char *categoryname)); -#endif - -struct loaded_l10nfile *_nl_find_domain PARAMS ((const char *__dirname, - char *__locale, - const char *__domainname, - struct binding *__domainbinding)) - internal_function; -void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain, - struct binding *__domainbinding)) - internal_function; -void _nl_unload_domain PARAMS ((struct loaded_domain *__domain)) - internal_function; -const char *_nl_init_domain_conv PARAMS ((struct loaded_l10nfile *__domain_file, - struct loaded_domain *__domain, - struct binding *__domainbinding)) - internal_function; -void _nl_free_domain_conv PARAMS ((struct loaded_domain *__domain)) - internal_function; - -char *_nl_find_msg PARAMS ((struct loaded_l10nfile *domain_file, - struct binding *domainbinding, - const char *msgid, size_t *lengthp)) - internal_function; - -#ifdef _LIBC -extern char *__gettext PARAMS ((const char *__msgid)); -extern char *__dgettext PARAMS ((const char *__domainname, - const char *__msgid)); -extern char *__dcgettext PARAMS ((const char *__domainname, - const char *__msgid, int __category)); -extern char *__ngettext PARAMS ((const char *__msgid1, const char *__msgid2, - unsigned long int __n)); -extern char *__dngettext PARAMS ((const char *__domainname, - const char *__msgid1, const char *__msgid2, - unsigned long int n)); -extern char *__dcngettext PARAMS ((const char *__domainname, - const char *__msgid1, const char *__msgid2, - unsigned long int __n, int __category)); -extern char *__dcigettext PARAMS ((const char *__domainname, - const char *__msgid1, const char *__msgid2, - int __plural, unsigned long int __n, - int __category)); -extern char *__textdomain PARAMS ((const char *__domainname)); -extern char *__bindtextdomain PARAMS ((const char *__domainname, - const char *__dirname)); -extern char *__bind_textdomain_codeset PARAMS ((const char *__domainname, - const char *__codeset)); -#else -/* Declare the exported libintl_* functions, in a way that allows us to - call them under their real name. */ -# define _INTL_REDIRECT_MACROS -# include "libgnuintl.h" -extern char *libintl_dcigettext PARAMS ((const char *__domainname, - const char *__msgid1, - const char *__msgid2, - int __plural, unsigned long int __n, - int __category)); -#endif - -/* @@ begin of epilog @@ */ - -#endif /* gettextP.h */ diff --git a/src/audacious/intl/gmo.h b/src/audacious/intl/gmo.h deleted file mode 100644 index 5c71e06..0000000 --- a/src/audacious/intl/gmo.h +++ /dev/null @@ -1,148 +0,0 @@ -/* Description of GNU message catalog format: general file layout. - Copyright (C) 1995, 1997, 2000-2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef _GETTEXT_H -#define _GETTEXT_H 1 - -#include <limits.h> - -/* @@ end of prolog @@ */ - -/* The magic number of the GNU message catalog format. */ -#define _MAGIC 0x950412de -#define _MAGIC_SWAPPED 0xde120495 - -/* Revision number of the currently used .mo (binary) file format. */ -#define MO_REVISION_NUMBER 0 - -/* The following contortions are an attempt to use the C preprocessor - to determine an unsigned integral type that is 32 bits wide. An - alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but - as of version autoconf-2.13, the AC_CHECK_SIZEOF macro doesn't work - when cross-compiling. */ - -#if __STDC__ -# define UINT_MAX_32_BITS 4294967295U -#else -# define UINT_MAX_32_BITS 0xFFFFFFFF -#endif - -/* If UINT_MAX isn't defined, assume it's a 32-bit type. - This should be valid for all systems GNU cares about because - that doesn't include 16-bit systems, and only modern systems - (that certainly have <limits.h>) have 64+-bit integral types. */ - -#ifndef UINT_MAX -# define UINT_MAX UINT_MAX_32_BITS -#endif - -#if UINT_MAX == UINT_MAX_32_BITS -typedef unsigned nls_uint32; -#else -# if USHRT_MAX == UINT_MAX_32_BITS -typedef unsigned short nls_uint32; -# else -# if ULONG_MAX == UINT_MAX_32_BITS -typedef unsigned long nls_uint32; -# else - /* The following line is intended to throw an error. Using #error is - not portable enough. */ - "Cannot determine unsigned 32-bit data type." -# endif -# endif -#endif - - -/* Header for binary .mo file format. */ -struct mo_file_header -{ - /* The magic number. */ - nls_uint32 magic; - /* The revision number of the file format. */ - nls_uint32 revision; - - /* The following are only used in .mo files with major revision 0. */ - - /* The number of strings pairs. */ - nls_uint32 nstrings; - /* Offset of table with start offsets of original strings. */ - nls_uint32 orig_tab_offset; - /* Offset of table with start offsets of translated strings. */ - nls_uint32 trans_tab_offset; - /* Size of hash table. */ - nls_uint32 hash_tab_size; - /* Offset of first hash table entry. */ - nls_uint32 hash_tab_offset; - - /* The following are only used in .mo files with minor revision >= 1. */ - - /* The number of system dependent segments. */ - nls_uint32 n_sysdep_segments; - /* Offset of table describing system dependent segments. */ - nls_uint32 sysdep_segments_offset; - /* The number of system dependent strings pairs. */ - nls_uint32 n_sysdep_strings; - /* Offset of table with start offsets of original sysdep strings. */ - nls_uint32 orig_sysdep_tab_offset; - /* Offset of table with start offsets of translated sysdep strings. */ - nls_uint32 trans_sysdep_tab_offset; -}; - -/* Descriptor for static string contained in the binary .mo file. */ -struct string_desc -{ - /* Length of addressed string, not including the trailing NUL. */ - nls_uint32 length; - /* Offset of string in file. */ - nls_uint32 offset; -}; - -/* The following are only used in .mo files with minor revision >= 1. */ - -/* Descriptor for system dependent string segment. */ -struct sysdep_segment -{ - /* Length of addressed string, including the trailing NUL. */ - nls_uint32 length; - /* Offset of string in file. */ - nls_uint32 offset; -}; - -/* Descriptor for system dependent string. */ -struct sysdep_string -{ - /* Offset of static string segments in file. */ - nls_uint32 offset; - /* Alternating sequence of static and system dependent segments. - The last segment is a static segment, including the trailing NUL. */ - struct segment_pair - { - /* Size of static segment. */ - nls_uint32 segsize; - /* Reference to system dependent string segment, or ~0 at the end. */ - nls_uint32 sysdepref; - } segments[1]; -}; - -/* Marker for the end of the segments[] array. This has the value 0xFFFFFFFF, - regardless whether 'int' is 16 bit, 32 bit, or 64 bit. */ -#define SEGMENTS_END ((nls_uint32) ~0) - -/* @@ begin of epilog @@ */ - -#endif /* gettext.h */ diff --git a/src/audacious/intl/hash-string.h b/src/audacious/intl/hash-string.h deleted file mode 100644 index 1c63776..0000000 --- a/src/audacious/intl/hash-string.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Description of GNU message catalog format: string hashing function. - Copyright (C) 1995, 1997, 1998, 2000, 2001 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* @@ end of prolog @@ */ - -#ifndef PARAMS -# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES -# define PARAMS(Args) Args -# else -# define PARAMS(Args) () -# endif -#endif - -/* We assume to have `unsigned long int' value with at least 32 bits. */ -#define HASHWORDBITS 32 - - -/* Defines the so called `hashpjw' function by P.J. Weinberger - [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, - 1986, 1987 Bell Telephone Laboratories, Inc.] */ -static unsigned long int hash_string PARAMS ((const char *__str_param)); - -static inline unsigned long int -hash_string (str_param) - const char *str_param; -{ - unsigned long int hval, g; - const char *str = str_param; - - /* Compute the hash value for the given string. */ - hval = 0; - while (*str != '\0') - { - hval <<= 4; - hval += (unsigned long int) *str++; - g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4)); - if (g != 0) - { - hval ^= g >> (HASHWORDBITS - 8); - hval ^= g; - } - } - return hval; -} diff --git a/src/audacious/intl/intl-compat.c b/src/audacious/intl/intl-compat.c deleted file mode 100644 index e0a1783..0000000 --- a/src/audacious/intl/intl-compat.c +++ /dev/null @@ -1,151 +0,0 @@ -/* intl-compat.c - Stub functions to call gettext functions from GNU gettext - Library. - Copyright (C) 1995, 2000-2003 Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include "gettextP.h" - -/* @@ end of prolog @@ */ - -/* This file redirects the gettext functions (without prefix) to those - defined in the included GNU libintl library (with "libintl_" prefix). - It is compiled into libintl in order to make the AM_GNU_GETTEXT test - of gettext <= 0.11.2 work with the libintl library >= 0.11.3 which - has the redirections primarily in the <libintl.h> include file. - It is also compiled into libgnuintl so that libgnuintl.so can be used - as LD_PRELOADable library on glibc systems, to provide the extra - features that the functions in the libc don't have (namely, logging). */ - - -#undef gettext -#undef dgettext -#undef dcgettext -#undef ngettext -#undef dngettext -#undef dcngettext -#undef textdomain -#undef bindtextdomain -#undef bind_textdomain_codeset - - -/* When building a DLL, we must export some functions. Note that because - the functions are only defined for binary backward compatibility, we - don't need to use __declspec(dllimport) in any case. */ -#if defined _MSC_VER && BUILDING_DLL -# define DLL_EXPORTED __declspec(dllexport) -#else -# define DLL_EXPORTED -#endif - - -DLL_EXPORTED -char * -gettext (msgid) - const char *msgid; -{ - return libintl_gettext (msgid); -} - - -DLL_EXPORTED -char * -dgettext (domainname, msgid) - const char *domainname; - const char *msgid; -{ - return libintl_dgettext (domainname, msgid); -} - - -DLL_EXPORTED -char * -dcgettext (domainname, msgid, category) - const char *domainname; - const char *msgid; - int category; -{ - return libintl_dcgettext (domainname, msgid, category); -} - - -DLL_EXPORTED -char * -ngettext (msgid1, msgid2, n) - const char *msgid1; - const char *msgid2; - unsigned long int n; -{ - return libintl_ngettext (msgid1, msgid2, n); -} - - -DLL_EXPORTED -char * -dngettext (domainname, msgid1, msgid2, n) - const char *domainname; - const char *msgid1; - const char *msgid2; - unsigned long int n; -{ - return libintl_dngettext (domainname, msgid1, msgid2, n); -} - - -DLL_EXPORTED -char * -dcngettext (domainname, msgid1, msgid2, n, category) - const char *domainname; - const char *msgid1; - const char *msgid2; - unsigned long int n; - int category; -{ - return libintl_dcngettext (domainname, msgid1, msgid2, n, category); -} - - -DLL_EXPORTED -char * -textdomain (domainname) - const char *domainname; -{ - return libintl_textdomain (domainname); -} - - -DLL_EXPORTED -char * -bindtextdomain (domainname, dirname) - const char *domainname; - const char *dirname; -{ - return libintl_bindtextdomain (domainname, dirname); -} - - -DLL_EXPORTED -char * -bind_textdomain_codeset (domainname, codeset) - const char *domainname; - const char *codeset; -{ - return libintl_bind_textdomain_codeset (domainname, codeset); -} diff --git a/src/audacious/intl/l10nflist.c b/src/audacious/intl/l10nflist.c deleted file mode 100644 index d149155..0000000 --- a/src/audacious/intl/l10nflist.c +++ /dev/null @@ -1,453 +0,0 @@ -/* Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc. - Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Tell glibc's <string.h> to provide a prototype for stpcpy(). - This must come before <config.h> because <config.h> may include - <features.h>, and once <features.h> has been included, it's too late. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <string.h> - -#if defined _LIBC || defined HAVE_ARGZ_H -# include <argz.h> -#endif -#include <ctype.h> -#include <sys/types.h> -#include <stdlib.h> - -#include "loadinfo.h" - -/* On some strange systems still no definition of NULL is found. Sigh! */ -#ifndef NULL -# if defined __STDC__ && __STDC__ -# define NULL ((void *) 0) -# else -# define NULL 0 -# endif -#endif - -/* @@ end of prolog @@ */ - -#ifdef _LIBC -/* Rename the non ANSI C functions. This is required by the standard - because some ANSI C functions will require linking with this object - file and the name space must not be polluted. */ -# ifndef stpcpy -# define stpcpy(dest, src) __stpcpy(dest, src) -# endif -#else -# ifndef HAVE_STPCPY -static char *stpcpy PARAMS ((char *dest, const char *src)); -# endif -#endif - -/* Pathname support. - ISSLASH(C) tests whether C is a directory separator character. - IS_ABSOLUTE_PATH(P) tests whether P is an absolute path. If it is not, - it may be concatenated to a directory pathname. - */ -#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ - /* Win32, OS/2, DOS */ -# define ISSLASH(C) ((C) == '/' || (C) == '\\') -# define HAS_DEVICE(P) \ - ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ - && (P)[1] == ':') -# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P)) -#else - /* Unix */ -# define ISSLASH(C) ((C) == '/') -# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0]) -#endif - -/* Define function which are usually not available. */ - -#if !defined _LIBC && !defined HAVE___ARGZ_COUNT -/* Returns the number of strings in ARGZ. */ -static size_t argz_count__ PARAMS ((const char *argz, size_t len)); - -static size_t -argz_count__ (argz, len) - const char *argz; - size_t len; -{ - size_t count = 0; - while (len > 0) - { - size_t part_len = strlen (argz); - argz += part_len + 1; - len -= part_len + 1; - count++; - } - return count; -} -# undef __argz_count -# define __argz_count(argz, len) argz_count__ (argz, len) -#else -# ifdef _LIBC -# define __argz_count(argz, len) INTUSE(__argz_count) (argz, len) -# endif -#endif /* !_LIBC && !HAVE___ARGZ_COUNT */ - -#if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY -/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's - except the last into the character SEP. */ -static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep)); - -static void -argz_stringify__ (argz, len, sep) - char *argz; - size_t len; - int sep; -{ - while (len > 0) - { - size_t part_len = strlen (argz); - argz += part_len; - len -= part_len + 1; - if (len > 0) - *argz++ = sep; - } -} -# undef __argz_stringify -# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep) -#else -# ifdef _LIBC -# define __argz_stringify(argz, len, sep) \ - INTUSE(__argz_stringify) (argz, len, sep) -# endif -#endif /* !_LIBC && !HAVE___ARGZ_STRINGIFY */ - -#if !defined _LIBC && !defined HAVE___ARGZ_NEXT -static char *argz_next__ PARAMS ((char *argz, size_t argz_len, - const char *entry)); - -static char * -argz_next__ (argz, argz_len, entry) - char *argz; - size_t argz_len; - const char *entry; -{ - if (entry) - { - if (entry < argz + argz_len) - entry = strchr (entry, '\0') + 1; - - return entry >= argz + argz_len ? NULL : (char *) entry; - } - else - if (argz_len > 0) - return argz; - else - return 0; -} -# undef __argz_next -# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry) -#endif /* !_LIBC && !HAVE___ARGZ_NEXT */ - - -/* Return number of bits set in X. */ -static int pop PARAMS ((int x)); - -static inline int -pop (x) - int x; -{ - /* We assume that no more than 16 bits are used. */ - x = ((x & ~0x5555) >> 1) + (x & 0x5555); - x = ((x & ~0x3333) >> 2) + (x & 0x3333); - x = ((x >> 4) + x) & 0x0f0f; - x = ((x >> 8) + x) & 0xff; - - return x; -} - - -struct loaded_l10nfile * -_nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language, - territory, codeset, normalized_codeset, modifier, special, - sponsor, revision, filename, do_allocate) - struct loaded_l10nfile **l10nfile_list; - const char *dirlist; - size_t dirlist_len; - int mask; - const char *language; - const char *territory; - const char *codeset; - const char *normalized_codeset; - const char *modifier; - const char *special; - const char *sponsor; - const char *revision; - const char *filename; - int do_allocate; -{ - char *abs_filename; - struct loaded_l10nfile **lastp; - struct loaded_l10nfile *retval; - char *cp; - size_t dirlist_count; - size_t entries; - int cnt; - - /* If LANGUAGE contains an absolute directory specification, we ignore - DIRLIST. */ - if (IS_ABSOLUTE_PATH (language)) - dirlist_len = 0; - - /* Allocate room for the full file name. */ - abs_filename = (char *) malloc (dirlist_len - + strlen (language) - + ((mask & TERRITORY) != 0 - ? strlen (territory) + 1 : 0) - + ((mask & XPG_CODESET) != 0 - ? strlen (codeset) + 1 : 0) - + ((mask & XPG_NORM_CODESET) != 0 - ? strlen (normalized_codeset) + 1 : 0) - + (((mask & XPG_MODIFIER) != 0 - || (mask & CEN_AUDIENCE) != 0) - ? strlen (modifier) + 1 : 0) - + ((mask & CEN_SPECIAL) != 0 - ? strlen (special) + 1 : 0) - + (((mask & CEN_SPONSOR) != 0 - || (mask & CEN_REVISION) != 0) - ? (1 + ((mask & CEN_SPONSOR) != 0 - ? strlen (sponsor) : 0) - + ((mask & CEN_REVISION) != 0 - ? strlen (revision) + 1 : 0)) : 0) - + 1 + strlen (filename) + 1); - - if (abs_filename == NULL) - return NULL; - - /* Construct file name. */ - cp = abs_filename; - if (dirlist_len > 0) - { - memcpy (cp, dirlist, dirlist_len); - __argz_stringify (cp, dirlist_len, PATH_SEPARATOR); - cp += dirlist_len; - cp[-1] = '/'; - } - - cp = stpcpy (cp, language); - - if ((mask & TERRITORY) != 0) - { - *cp++ = '_'; - cp = stpcpy (cp, territory); - } - if ((mask & XPG_CODESET) != 0) - { - *cp++ = '.'; - cp = stpcpy (cp, codeset); - } - if ((mask & XPG_NORM_CODESET) != 0) - { - *cp++ = '.'; - cp = stpcpy (cp, normalized_codeset); - } - if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0) - { - /* This component can be part of both syntaces but has different - leading characters. For CEN we use `+', else `@'. */ - *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@'; - cp = stpcpy (cp, modifier); - } - if ((mask & CEN_SPECIAL) != 0) - { - *cp++ = '+'; - cp = stpcpy (cp, special); - } - if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0) - { - *cp++ = ','; - if ((mask & CEN_SPONSOR) != 0) - cp = stpcpy (cp, sponsor); - if ((mask & CEN_REVISION) != 0) - { - *cp++ = '_'; - cp = stpcpy (cp, revision); - } - } - - *cp++ = '/'; - stpcpy (cp, filename); - - /* Look in list of already loaded domains whether it is already - available. */ - lastp = l10nfile_list; - for (retval = *l10nfile_list; retval != NULL; retval = retval->next) - if (retval->filename != NULL) - { - int compare = strcmp (retval->filename, abs_filename); - if (compare == 0) - /* We found it! */ - break; - if (compare < 0) - { - /* It's not in the list. */ - retval = NULL; - break; - } - - lastp = &retval->next; - } - - if (retval != NULL || do_allocate == 0) - { - free (abs_filename); - return retval; - } - - dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1); - - /* Allocate a new loaded_l10nfile. */ - retval = - (struct loaded_l10nfile *) - malloc (sizeof (*retval) - + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0)) - * sizeof (struct loaded_l10nfile *))); - if (retval == NULL) - return NULL; - - retval->filename = abs_filename; - - /* We set retval->data to NULL here; it is filled in later. - Setting retval->decided to 1 here means that retval does not - correspond to a real file (dirlist_count > 1) or is not worth - looking up (if an unnormalized codeset was specified). */ - retval->decided = (dirlist_count > 1 - || ((mask & XPG_CODESET) != 0 - && (mask & XPG_NORM_CODESET) != 0)); - retval->data = NULL; - - retval->next = *lastp; - *lastp = retval; - - entries = 0; - /* Recurse to fill the inheritance list of RETVAL. - If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL - entry does not correspond to a real file; retval->filename contains - colons. In this case we loop across all elements of DIRLIST and - across all bit patterns dominated by MASK. - If the DIRLIST is a single directory or entirely redundant (i.e. - DIRLIST_COUNT == 1), we loop across all bit patterns dominated by - MASK, excluding MASK itself. - In either case, we loop down from MASK to 0. This has the effect - that the extra bits in the locale name are dropped in this order: - first the modifier, then the territory, then the codeset, then the - normalized_codeset. */ - for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt) - if ((cnt & ~mask) == 0 - && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0) - && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0)) - { - if (dirlist_count > 1) - { - /* Iterate over all elements of the DIRLIST. */ - char *dir = NULL; - - while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir)) - != NULL) - retval->successor[entries++] - = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, - cnt, language, territory, codeset, - normalized_codeset, modifier, special, - sponsor, revision, filename, 1); - } - else - retval->successor[entries++] - = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, - cnt, language, territory, codeset, - normalized_codeset, modifier, special, - sponsor, revision, filename, 1); - } - retval->successor[entries] = NULL; - - return retval; -} - -/* Normalize codeset name. There is no standard for the codeset - names. Normalization allows the user to use any of the common - names. The return value is dynamically allocated and has to be - freed by the caller. */ -const char * -_nl_normalize_codeset (codeset, name_len) - const char *codeset; - size_t name_len; -{ - int len = 0; - int only_digit = 1; - char *retval; - char *wp; - size_t cnt; - - for (cnt = 0; cnt < name_len; ++cnt) - if (isalnum ((unsigned char) codeset[cnt])) - { - ++len; - - if (isalpha ((unsigned char) codeset[cnt])) - only_digit = 0; - } - - retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1); - - if (retval != NULL) - { - if (only_digit) - wp = stpcpy (retval, "iso"); - else - wp = retval; - - for (cnt = 0; cnt < name_len; ++cnt) - if (isalpha ((unsigned char) codeset[cnt])) - *wp++ = tolower ((unsigned char) codeset[cnt]); - else if (isdigit ((unsigned char) codeset[cnt])) - *wp++ = codeset[cnt]; - - *wp = '\0'; - } - - return (const char *) retval; -} - - -/* @@ begin of epilog @@ */ - -/* We don't want libintl.a to depend on any other library. So we - avoid the non-standard function stpcpy. In GNU C Library this - function is available, though. Also allow the symbol HAVE_STPCPY - to be defined. */ -#if !_LIBC && !HAVE_STPCPY -static char * -stpcpy (dest, src) - char *dest; - const char *src; -{ - while ((*dest++ = *src++) != '\0') - /* Do nothing. */ ; - return dest - 1; -} -#endif diff --git a/src/audacious/intl/libgnuintl.h b/src/audacious/intl/libgnuintl.h deleted file mode 100644 index 5290bc0..0000000 --- a/src/audacious/intl/libgnuintl.h +++ /dev/null @@ -1,309 +0,0 @@ -/* Message catalogs for internationalization. - Copyright (C) 1995-1997, 2000-2003 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef _LIBINTL_H -#define _LIBINTL_H 1 - -#include <locale.h> - -/* The LC_MESSAGES locale category is the category used by the functions - gettext() and dgettext(). It is specified in POSIX, but not in ANSI C. - On systems that don't define it, use an arbitrary value instead. - On Solaris, <locale.h> defines __LOCALE_H (or _LOCALE_H in Solaris 2.5) - then includes <libintl.h> (i.e. this file!) and then only defines - LC_MESSAGES. To avoid a redefinition warning, don't define LC_MESSAGES - in this case. */ -#if !defined LC_MESSAGES && !(defined __LOCALE_H || (defined _LOCALE_H && defined __sun)) -# define LC_MESSAGES 1729 -#endif - -/* We define an additional symbol to signal that we use the GNU - implementation of gettext. */ -#define __USE_GNU_GETTEXT 1 - -/* Provide information about the supported file formats. Returns the - maximum minor revision number supported for a given major revision. */ -#define __GNU_GETTEXT_SUPPORTED_REVISION(major) \ - ((major) == 0 ? 1 : -1) - -/* Resolve a platform specific conflict on DJGPP. GNU gettext takes - precedence over _conio_gettext. */ -#ifdef __DJGPP__ -# undef gettext -#endif - -/* Use _INTL_PARAMS, not PARAMS, in order to avoid clashes with identifiers - used by programs. Similarly, test __PROTOTYPES, not PROTOTYPES. */ -#ifndef _INTL_PARAMS -# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES -# define _INTL_PARAMS(args) args -# else -# define _INTL_PARAMS(args) () -# endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - -/* We redirect the functions to those prefixed with "libintl_". This is - necessary, because some systems define gettext/textdomain/... in the C - library (namely, Solaris 2.4 and newer, and GNU libc 2.0 and newer). - If we used the unprefixed names, there would be cases where the - definition in the C library would override the one in the libintl.so - shared library. Recall that on ELF systems, the symbols are looked - up in the following order: - 1. in the executable, - 2. in the shared libraries specified on the link command line, in order, - 3. in the dependencies of the shared libraries specified on the link - command line, - 4. in the dlopen()ed shared libraries, in the order in which they were - dlopen()ed. - The definition in the C library would override the one in libintl.so if - either - * -lc is given on the link command line and -lintl isn't, or - * -lc is given on the link command line before -lintl, or - * libintl.so is a dependency of a dlopen()ed shared library but not - linked to the executable at link time. - Since Solaris gettext() behaves differently than GNU gettext(), this - would be unacceptable. - - The redirection happens by default through macros in C, so that &gettext - is independent of the compilation unit, but through inline functions in - C++, in order not to interfere with the name mangling of class fields or - class methods called 'gettext'. */ - -/* The user can define _INTL_REDIRECT_INLINE or _INTL_REDIRECT_MACROS. - If he doesn't, we choose the method. A third possible method is - _INTL_REDIRECT_ASM, supported only by GCC. */ -#if !(defined _INTL_REDIRECT_INLINE || defined _INTL_REDIRECT_MACROS) -# if __GNUC__ >= 2 && !defined __APPLE_CC__ && (defined __STDC__ || defined __cplusplus) -# define _INTL_REDIRECT_ASM -# else -# ifdef __cplusplus -# define _INTL_REDIRECT_INLINE -# else -# define _INTL_REDIRECT_MACROS -# endif -# endif -#endif -/* Auxiliary macros. */ -#ifdef _INTL_REDIRECT_ASM -# define _INTL_ASM(cname) __asm__ (_INTL_ASMNAME (__USER_LABEL_PREFIX__, #cname)) -# define _INTL_ASMNAME(prefix,cnamestring) _INTL_STRINGIFY (prefix) cnamestring -# define _INTL_STRINGIFY(prefix) #prefix -#else -# define _INTL_ASM(cname) -#endif - -/* Look up MSGID in the current default message catalog for the current - LC_MESSAGES locale. If not found, returns MSGID itself (the default - text). */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_gettext (const char *__msgid); -static inline char *gettext (const char *__msgid) -{ - return libintl_gettext (__msgid); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define gettext libintl_gettext -#endif -extern char *gettext _INTL_PARAMS ((const char *__msgid)) - _INTL_ASM (libintl_gettext); -#endif - -/* Look up MSGID in the DOMAINNAME message catalog for the current - LC_MESSAGES locale. */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_dgettext (const char *__domainname, const char *__msgid); -static inline char *dgettext (const char *__domainname, const char *__msgid) -{ - return libintl_dgettext (__domainname, __msgid); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define dgettext libintl_dgettext -#endif -extern char *dgettext _INTL_PARAMS ((const char *__domainname, - const char *__msgid)) - _INTL_ASM (libintl_dgettext); -#endif - -/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY - locale. */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_dcgettext (const char *__domainname, const char *__msgid, - int __category); -static inline char *dcgettext (const char *__domainname, const char *__msgid, - int __category) -{ - return libintl_dcgettext (__domainname, __msgid, __category); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define dcgettext libintl_dcgettext -#endif -extern char *dcgettext _INTL_PARAMS ((const char *__domainname, - const char *__msgid, - int __category)) - _INTL_ASM (libintl_dcgettext); -#endif - - -/* Similar to `gettext' but select the plural form corresponding to the - number N. */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_ngettext (const char *__msgid1, const char *__msgid2, - unsigned long int __n); -static inline char *ngettext (const char *__msgid1, const char *__msgid2, - unsigned long int __n) -{ - return libintl_ngettext (__msgid1, __msgid2, __n); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define ngettext libintl_ngettext -#endif -extern char *ngettext _INTL_PARAMS ((const char *__msgid1, - const char *__msgid2, - unsigned long int __n)) - _INTL_ASM (libintl_ngettext); -#endif - -/* Similar to `dgettext' but select the plural form corresponding to the - number N. */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_dngettext (const char *__domainname, const char *__msgid1, - const char *__msgid2, unsigned long int __n); -static inline char *dngettext (const char *__domainname, const char *__msgid1, - const char *__msgid2, unsigned long int __n) -{ - return libintl_dngettext (__domainname, __msgid1, __msgid2, __n); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define dngettext libintl_dngettext -#endif -extern char *dngettext _INTL_PARAMS ((const char *__domainname, - const char *__msgid1, - const char *__msgid2, - unsigned long int __n)) - _INTL_ASM (libintl_dngettext); -#endif - -/* Similar to `dcgettext' but select the plural form corresponding to the - number N. */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_dcngettext (const char *__domainname, - const char *__msgid1, const char *__msgid2, - unsigned long int __n, int __category); -static inline char *dcngettext (const char *__domainname, - const char *__msgid1, const char *__msgid2, - unsigned long int __n, int __category) -{ - return libintl_dcngettext (__domainname, __msgid1, __msgid2, __n, __category); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define dcngettext libintl_dcngettext -#endif -extern char *dcngettext _INTL_PARAMS ((const char *__domainname, - const char *__msgid1, - const char *__msgid2, - unsigned long int __n, - int __category)) - _INTL_ASM (libintl_dcngettext); -#endif - - -/* Set the current default message catalog to DOMAINNAME. - If DOMAINNAME is null, return the current default. - If DOMAINNAME is "", reset to the default of "messages". */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_textdomain (const char *__domainname); -static inline char *textdomain (const char *__domainname) -{ - return libintl_textdomain (__domainname); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define textdomain libintl_textdomain -#endif -extern char *textdomain _INTL_PARAMS ((const char *__domainname)) - _INTL_ASM (libintl_textdomain); -#endif - -/* Specify that the DOMAINNAME message catalog will be found - in DIRNAME rather than in the system locale data base. */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_bindtextdomain (const char *__domainname, - const char *__dirname); -static inline char *bindtextdomain (const char *__domainname, - const char *__dirname) -{ - return libintl_bindtextdomain (__domainname, __dirname); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define bindtextdomain libintl_bindtextdomain -#endif -extern char *bindtextdomain _INTL_PARAMS ((const char *__domainname, - const char *__dirname)) - _INTL_ASM (libintl_bindtextdomain); -#endif - -/* Specify the character encoding in which the messages from the - DOMAINNAME message catalog will be returned. */ -#ifdef _INTL_REDIRECT_INLINE -extern char *libintl_bind_textdomain_codeset (const char *__domainname, - const char *__codeset); -static inline char *bind_textdomain_codeset (const char *__domainname, - const char *__codeset) -{ - return libintl_bind_textdomain_codeset (__domainname, __codeset); -} -#else -#ifdef _INTL_REDIRECT_MACROS -# define bind_textdomain_codeset libintl_bind_textdomain_codeset -#endif -extern char *bind_textdomain_codeset _INTL_PARAMS ((const char *__domainname, - const char *__codeset)) - _INTL_ASM (libintl_bind_textdomain_codeset); -#endif - - -/* Support for relocatable packages. */ - -/* Sets the original and the current installation prefix of the package. - Relocation simply replaces a pathname starting with the original prefix - by the corresponding pathname with the current prefix instead. Both - prefixes should be directory names without trailing slash (i.e. use "" - instead of "/"). */ -#define libintl_set_relocation_prefix libintl_set_relocation_prefix -extern void - libintl_set_relocation_prefix _INTL_PARAMS ((const char *orig_prefix, - const char *curr_prefix)); - - -#ifdef __cplusplus -} -#endif - -#endif /* libintl.h */ diff --git a/src/audacious/intl/loadinfo.h b/src/audacious/intl/loadinfo.h deleted file mode 100644 index 8fdd670..0000000 --- a/src/audacious/intl/loadinfo.h +++ /dev/null @@ -1,156 +0,0 @@ -/* Copyright (C) 1996-1999, 2000-2002 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef _LOADINFO_H -#define _LOADINFO_H 1 - -/* Declarations of locale dependent catalog lookup functions. - Implemented in - - localealias.c Possibly replace a locale name by another. - explodename.c Split a locale name into its various fields. - l10nflist.c Generate a list of filenames of possible message catalogs. - finddomain.c Find and open the relevant message catalogs. - - The main function _nl_find_domain() in finddomain.c is declared - in gettextP.h. - */ - -#ifndef PARAMS -# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES -# define PARAMS(args) args -# else -# define PARAMS(args) () -# endif -#endif - -#ifndef internal_function -# define internal_function -#endif - -/* Tell the compiler when a conditional or integer expression is - almost always true or almost always false. */ -#ifndef HAVE_BUILTIN_EXPECT -# define __builtin_expect(expr, val) (expr) -#endif - -/* Separator in PATH like lists of pathnames. */ -#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ - /* Win32, OS/2, DOS */ -# define PATH_SEPARATOR ';' -#else - /* Unix */ -# define PATH_SEPARATOR ':' -#endif - -/* Encoding of locale name parts. */ -#define CEN_REVISION 1 -#define CEN_SPONSOR 2 -#define CEN_SPECIAL 4 -#define XPG_NORM_CODESET 8 -#define XPG_CODESET 16 -#define TERRITORY 32 -#define CEN_AUDIENCE 64 -#define XPG_MODIFIER 128 - -#define CEN_SPECIFIC (CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE) -#define XPG_SPECIFIC (XPG_CODESET|XPG_NORM_CODESET|XPG_MODIFIER) - - -struct loaded_l10nfile -{ - const char *filename; - int decided; - - const void *data; - - struct loaded_l10nfile *next; - struct loaded_l10nfile *successor[1]; -}; - - -/* Normalize codeset name. There is no standard for the codeset - names. Normalization allows the user to use any of the common - names. The return value is dynamically allocated and has to be - freed by the caller. */ -extern const char *_nl_normalize_codeset PARAMS ((const char *codeset, - size_t name_len)); - -/* Lookup a locale dependent file. - *L10NFILE_LIST denotes a pool of lookup results of locale dependent - files of the same kind, sorted in decreasing order of ->filename. - DIRLIST and DIRLIST_LEN are an argz list of directories in which to - look, containing at least one directory (i.e. DIRLIST_LEN > 0). - MASK, LANGUAGE, TERRITORY, CODESET, NORMALIZED_CODESET, MODIFIER, - SPECIAL, SPONSOR, REVISION are the pieces of the locale name, as - produced by _nl_explode_name(). FILENAME is the filename suffix. - The return value is the lookup result, either found in *L10NFILE_LIST, - or - if DO_ALLOCATE is nonzero - freshly allocated, or possibly NULL. - If the return value is non-NULL, it is added to *L10NFILE_LIST, and - its ->next field denotes the chaining inside *L10NFILE_LIST, and - furthermore its ->successor[] field contains a list of other lookup - results from which this lookup result inherits. */ -extern struct loaded_l10nfile * -_nl_make_l10nflist PARAMS ((struct loaded_l10nfile **l10nfile_list, - const char *dirlist, size_t dirlist_len, int mask, - const char *language, const char *territory, - const char *codeset, - const char *normalized_codeset, - const char *modifier, const char *special, - const char *sponsor, const char *revision, - const char *filename, int do_allocate)); - -/* Lookup the real locale name for a locale alias NAME, or NULL if - NAME is not a locale alias (but possibly a real locale name). - The return value is statically allocated and must not be freed. */ -extern const char *_nl_expand_alias PARAMS ((const char *name)); - -/* Split a locale name NAME into its pieces: language, modifier, - territory, codeset, special, sponsor, revision. - NAME gets destructively modified: NUL bytes are inserted here and - there. *LANGUAGE gets assigned NAME. Each of *MODIFIER, *TERRITORY, - *CODESET, *SPECIAL, *SPONSOR, *REVISION gets assigned either a - pointer into the old NAME string, or NULL. *NORMALIZED_CODESET - gets assigned the expanded *CODESET, if it is different from *CODESET; - this one is dynamically allocated and has to be freed by the caller. - The return value is a bitmask, where each bit corresponds to one - filled-in value: - XPG_MODIFIER, CEN_AUDIENCE for *MODIFIER, - TERRITORY for *TERRITORY, - XPG_CODESET for *CODESET, - XPG_NORM_CODESET for *NORMALIZED_CODESET, - CEN_SPECIAL for *SPECIAL, - CEN_SPONSOR for *SPONSOR, - CEN_REVISION for *REVISION. - */ -extern int _nl_explode_name PARAMS ((char *name, const char **language, - const char **modifier, - const char **territory, - const char **codeset, - const char **normalized_codeset, - const char **special, - const char **sponsor, - const char **revision)); - -/* Split a locale name NAME into a leading language part and all the - rest. Return a pointer to the first character after the language, - i.e. to the first byte of the rest. */ -extern char *_nl_find_language PARAMS ((const char *name)); - -#endif /* loadinfo.h */ diff --git a/src/audacious/intl/loadmsgcat.c b/src/audacious/intl/loadmsgcat.c deleted file mode 100644 index 78c7cad..0000000 --- a/src/audacious/intl/loadmsgcat.c +++ /dev/null @@ -1,1322 +0,0 @@ -/* Load needed message catalogs. - Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Tell glibc's <string.h> to provide a prototype for mempcpy(). - This must come before <config.h> because <config.h> may include - <features.h>, and once <features.h> has been included, it's too late. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> - -#ifdef __GNUC__ -# undef alloca -# define alloca __builtin_alloca -# define HAVE_ALLOCA 1 -#else -# ifdef _MSC_VER -# include <malloc.h> -# define alloca _alloca -# else -# if defined HAVE_ALLOCA_H || defined _LIBC -# include <alloca.h> -# else -# ifdef _AIX - #pragma alloca -# else -# ifndef alloca -char *alloca (); -# endif -# endif -# endif -# endif -#endif - -#include <stdlib.h> -#include <string.h> - -#if defined HAVE_UNISTD_H || defined _LIBC -# include <unistd.h> -#endif - -#ifdef _LIBC -# include <langinfo.h> -# include <locale.h> -#endif - -#if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) \ - || (defined _LIBC && defined _POSIX_MAPPED_FILES) -# include <sys/mman.h> -# undef HAVE_MMAP -# define HAVE_MMAP 1 -#else -# undef HAVE_MMAP -#endif - -#if defined HAVE_STDINT_H_WITH_UINTMAX || defined _LIBC -# include <stdint.h> -#endif -#if defined HAVE_INTTYPES_H || defined _LIBC -# include <inttypes.h> -#endif - -#include "gmo.h" -#include "gettextP.h" -#include "hash-string.h" -#include "plural-exp.h" - -#ifdef _LIBC -# include "../locale/localeinfo.h" -#endif - -/* Provide fallback values for macros that ought to be defined in <inttypes.h>. - Note that our fallback values need not be literal strings, because we don't - use them with preprocessor string concatenation. */ -#if !defined PRId8 || PRI_MACROS_BROKEN -# undef PRId8 -# define PRId8 "d" -#endif -#if !defined PRIi8 || PRI_MACROS_BROKEN -# undef PRIi8 -# define PRIi8 "i" -#endif -#if !defined PRIo8 || PRI_MACROS_BROKEN -# undef PRIo8 -# define PRIo8 "o" -#endif -#if !defined PRIu8 || PRI_MACROS_BROKEN -# undef PRIu8 -# define PRIu8 "u" -#endif -#if !defined PRIx8 || PRI_MACROS_BROKEN -# undef PRIx8 -# define PRIx8 "x" -#endif -#if !defined PRIX8 || PRI_MACROS_BROKEN -# undef PRIX8 -# define PRIX8 "X" -#endif -#if !defined PRId16 || PRI_MACROS_BROKEN -# undef PRId16 -# define PRId16 "d" -#endif -#if !defined PRIi16 || PRI_MACROS_BROKEN -# undef PRIi16 -# define PRIi16 "i" -#endif -#if !defined PRIo16 || PRI_MACROS_BROKEN -# undef PRIo16 -# define PRIo16 "o" -#endif -#if !defined PRIu16 || PRI_MACROS_BROKEN -# undef PRIu16 -# define PRIu16 "u" -#endif -#if !defined PRIx16 || PRI_MACROS_BROKEN -# undef PRIx16 -# define PRIx16 "x" -#endif -#if !defined PRIX16 || PRI_MACROS_BROKEN -# undef PRIX16 -# define PRIX16 "X" -#endif -#if !defined PRId32 || PRI_MACROS_BROKEN -# undef PRId32 -# define PRId32 "d" -#endif -#if !defined PRIi32 || PRI_MACROS_BROKEN -# undef PRIi32 -# define PRIi32 "i" -#endif -#if !defined PRIo32 || PRI_MACROS_BROKEN -# undef PRIo32 -# define PRIo32 "o" -#endif -#if !defined PRIu32 || PRI_MACROS_BROKEN -# undef PRIu32 -# define PRIu32 "u" -#endif -#if !defined PRIx32 || PRI_MACROS_BROKEN -# undef PRIx32 -# define PRIx32 "x" -#endif -#if !defined PRIX32 || PRI_MACROS_BROKEN -# undef PRIX32 -# define PRIX32 "X" -#endif -#if !defined PRId64 || PRI_MACROS_BROKEN -# undef PRId64 -# define PRId64 (sizeof (long) == 8 ? "ld" : "lld") -#endif -#if !defined PRIi64 || PRI_MACROS_BROKEN -# undef PRIi64 -# define PRIi64 (sizeof (long) == 8 ? "li" : "lli") -#endif -#if !defined PRIo64 || PRI_MACROS_BROKEN -# undef PRIo64 -# define PRIo64 (sizeof (long) == 8 ? "lo" : "llo") -#endif -#if !defined PRIu64 || PRI_MACROS_BROKEN -# undef PRIu64 -# define PRIu64 (sizeof (long) == 8 ? "lu" : "llu") -#endif -#if !defined PRIx64 || PRI_MACROS_BROKEN -# undef PRIx64 -# define PRIx64 (sizeof (long) == 8 ? "lx" : "llx") -#endif -#if !defined PRIX64 || PRI_MACROS_BROKEN -# undef PRIX64 -# define PRIX64 (sizeof (long) == 8 ? "lX" : "llX") -#endif -#if !defined PRIdLEAST8 || PRI_MACROS_BROKEN -# undef PRIdLEAST8 -# define PRIdLEAST8 "d" -#endif -#if !defined PRIiLEAST8 || PRI_MACROS_BROKEN -# undef PRIiLEAST8 -# define PRIiLEAST8 "i" -#endif -#if !defined PRIoLEAST8 || PRI_MACROS_BROKEN -# undef PRIoLEAST8 -# define PRIoLEAST8 "o" -#endif -#if !defined PRIuLEAST8 || PRI_MACROS_BROKEN -# undef PRIuLEAST8 -# define PRIuLEAST8 "u" -#endif -#if !defined PRIxLEAST8 || PRI_MACROS_BROKEN -# undef PRIxLEAST8 -# define PRIxLEAST8 "x" -#endif -#if !defined PRIXLEAST8 || PRI_MACROS_BROKEN -# undef PRIXLEAST8 -# define PRIXLEAST8 "X" -#endif -#if !defined PRIdLEAST16 || PRI_MACROS_BROKEN -# undef PRIdLEAST16 -# define PRIdLEAST16 "d" -#endif -#if !defined PRIiLEAST16 || PRI_MACROS_BROKEN -# undef PRIiLEAST16 -# define PRIiLEAST16 "i" -#endif -#if !defined PRIoLEAST16 || PRI_MACROS_BROKEN -# undef PRIoLEAST16 -# define PRIoLEAST16 "o" -#endif -#if !defined PRIuLEAST16 || PRI_MACROS_BROKEN -# undef PRIuLEAST16 -# define PRIuLEAST16 "u" -#endif -#if !defined PRIxLEAST16 || PRI_MACROS_BROKEN -# undef PRIxLEAST16 -# define PRIxLEAST16 "x" -#endif -#if !defined PRIXLEAST16 || PRI_MACROS_BROKEN -# undef PRIXLEAST16 -# define PRIXLEAST16 "X" -#endif -#if !defined PRIdLEAST32 || PRI_MACROS_BROKEN -# undef PRIdLEAST32 -# define PRIdLEAST32 "d" -#endif -#if !defined PRIiLEAST32 || PRI_MACROS_BROKEN -# undef PRIiLEAST32 -# define PRIiLEAST32 "i" -#endif -#if !defined PRIoLEAST32 || PRI_MACROS_BROKEN -# undef PRIoLEAST32 -# define PRIoLEAST32 "o" -#endif -#if !defined PRIuLEAST32 || PRI_MACROS_BROKEN -# undef PRIuLEAST32 -# define PRIuLEAST32 "u" -#endif -#if !defined PRIxLEAST32 || PRI_MACROS_BROKEN -# undef PRIxLEAST32 -# define PRIxLEAST32 "x" -#endif -#if !defined PRIXLEAST32 || PRI_MACROS_BROKEN -# undef PRIXLEAST32 -# define PRIXLEAST32 "X" -#endif -#if !defined PRIdLEAST64 || PRI_MACROS_BROKEN -# undef PRIdLEAST64 -# define PRIdLEAST64 PRId64 -#endif -#if !defined PRIiLEAST64 || PRI_MACROS_BROKEN -# undef PRIiLEAST64 -# define PRIiLEAST64 PRIi64 -#endif -#if !defined PRIoLEAST64 || PRI_MACROS_BROKEN -# undef PRIoLEAST64 -# define PRIoLEAST64 PRIo64 -#endif -#if !defined PRIuLEAST64 || PRI_MACROS_BROKEN -# undef PRIuLEAST64 -# define PRIuLEAST64 PRIu64 -#endif -#if !defined PRIxLEAST64 || PRI_MACROS_BROKEN -# undef PRIxLEAST64 -# define PRIxLEAST64 PRIx64 -#endif -#if !defined PRIXLEAST64 || PRI_MACROS_BROKEN -# undef PRIXLEAST64 -# define PRIXLEAST64 PRIX64 -#endif -#if !defined PRIdFAST8 || PRI_MACROS_BROKEN -# undef PRIdFAST8 -# define PRIdFAST8 "d" -#endif -#if !defined PRIiFAST8 || PRI_MACROS_BROKEN -# undef PRIiFAST8 -# define PRIiFAST8 "i" -#endif -#if !defined PRIoFAST8 || PRI_MACROS_BROKEN -# undef PRIoFAST8 -# define PRIoFAST8 "o" -#endif -#if !defined PRIuFAST8 || PRI_MACROS_BROKEN -# undef PRIuFAST8 -# define PRIuFAST8 "u" -#endif -#if !defined PRIxFAST8 || PRI_MACROS_BROKEN -# undef PRIxFAST8 -# define PRIxFAST8 "x" -#endif -#if !defined PRIXFAST8 || PRI_MACROS_BROKEN -# undef PRIXFAST8 -# define PRIXFAST8 "X" -#endif -#if !defined PRIdFAST16 || PRI_MACROS_BROKEN -# undef PRIdFAST16 -# define PRIdFAST16 "d" -#endif -#if !defined PRIiFAST16 || PRI_MACROS_BROKEN -# undef PRIiFAST16 -# define PRIiFAST16 "i" -#endif -#if !defined PRIoFAST16 || PRI_MACROS_BROKEN -# undef PRIoFAST16 -# define PRIoFAST16 "o" -#endif -#if !defined PRIuFAST16 || PRI_MACROS_BROKEN -# undef PRIuFAST16 -# define PRIuFAST16 "u" -#endif -#if !defined PRIxFAST16 || PRI_MACROS_BROKEN -# undef PRIxFAST16 -# define PRIxFAST16 "x" -#endif -#if !defined PRIXFAST16 || PRI_MACROS_BROKEN -# undef PRIXFAST16 -# define PRIXFAST16 "X" -#endif -#if !defined PRIdFAST32 || PRI_MACROS_BROKEN -# undef PRIdFAST32 -# define PRIdFAST32 "d" -#endif -#if !defined PRIiFAST32 || PRI_MACROS_BROKEN -# undef PRIiFAST32 -# define PRIiFAST32 "i" -#endif -#if !defined PRIoFAST32 || PRI_MACROS_BROKEN -# undef PRIoFAST32 -# define PRIoFAST32 "o" -#endif -#if !defined PRIuFAST32 || PRI_MACROS_BROKEN -# undef PRIuFAST32 -# define PRIuFAST32 "u" -#endif -#if !defined PRIxFAST32 || PRI_MACROS_BROKEN -# undef PRIxFAST32 -# define PRIxFAST32 "x" -#endif -#if !defined PRIXFAST32 || PRI_MACROS_BROKEN -# undef PRIXFAST32 -# define PRIXFAST32 "X" -#endif -#if !defined PRIdFAST64 || PRI_MACROS_BROKEN -# undef PRIdFAST64 -# define PRIdFAST64 PRId64 -#endif -#if !defined PRIiFAST64 || PRI_MACROS_BROKEN -# undef PRIiFAST64 -# define PRIiFAST64 PRIi64 -#endif -#if !defined PRIoFAST64 || PRI_MACROS_BROKEN -# undef PRIoFAST64 -# define PRIoFAST64 PRIo64 -#endif -#if !defined PRIuFAST64 || PRI_MACROS_BROKEN -# undef PRIuFAST64 -# define PRIuFAST64 PRIu64 -#endif -#if !defined PRIxFAST64 || PRI_MACROS_BROKEN -# undef PRIxFAST64 -# define PRIxFAST64 PRIx64 -#endif -#if !defined PRIXFAST64 || PRI_MACROS_BROKEN -# undef PRIXFAST64 -# define PRIXFAST64 PRIX64 -#endif -#if !defined PRIdMAX || PRI_MACROS_BROKEN -# undef PRIdMAX -# define PRIdMAX (sizeof (uintmax_t) == sizeof (long) ? "ld" : "lld") -#endif -#if !defined PRIiMAX || PRI_MACROS_BROKEN -# undef PRIiMAX -# define PRIiMAX (sizeof (uintmax_t) == sizeof (long) ? "li" : "lli") -#endif -#if !defined PRIoMAX || PRI_MACROS_BROKEN -# undef PRIoMAX -# define PRIoMAX (sizeof (uintmax_t) == sizeof (long) ? "lo" : "llo") -#endif -#if !defined PRIuMAX || PRI_MACROS_BROKEN -# undef PRIuMAX -# define PRIuMAX (sizeof (uintmax_t) == sizeof (long) ? "lu" : "llu") -#endif -#if !defined PRIxMAX || PRI_MACROS_BROKEN -# undef PRIxMAX -# define PRIxMAX (sizeof (uintmax_t) == sizeof (long) ? "lx" : "llx") -#endif -#if !defined PRIXMAX || PRI_MACROS_BROKEN -# undef PRIXMAX -# define PRIXMAX (sizeof (uintmax_t) == sizeof (long) ? "lX" : "llX") -#endif -#if !defined PRIdPTR || PRI_MACROS_BROKEN -# undef PRIdPTR -# define PRIdPTR \ - (sizeof (void *) == sizeof (long) ? "ld" : \ - sizeof (void *) == sizeof (int) ? "d" : \ - "lld") -#endif -#if !defined PRIiPTR || PRI_MACROS_BROKEN -# undef PRIiPTR -# define PRIiPTR \ - (sizeof (void *) == sizeof (long) ? "li" : \ - sizeof (void *) == sizeof (int) ? "i" : \ - "lli") -#endif -#if !defined PRIoPTR || PRI_MACROS_BROKEN -# undef PRIoPTR -# define PRIoPTR \ - (sizeof (void *) == sizeof (long) ? "lo" : \ - sizeof (void *) == sizeof (int) ? "o" : \ - "llo") -#endif -#if !defined PRIuPTR || PRI_MACROS_BROKEN -# undef PRIuPTR -# define PRIuPTR \ - (sizeof (void *) == sizeof (long) ? "lu" : \ - sizeof (void *) == sizeof (int) ? "u" : \ - "llu") -#endif -#if !defined PRIxPTR || PRI_MACROS_BROKEN -# undef PRIxPTR -# define PRIxPTR \ - (sizeof (void *) == sizeof (long) ? "lx" : \ - sizeof (void *) == sizeof (int) ? "x" : \ - "llx") -#endif -#if !defined PRIXPTR || PRI_MACROS_BROKEN -# undef PRIXPTR -# define PRIXPTR \ - (sizeof (void *) == sizeof (long) ? "lX" : \ - sizeof (void *) == sizeof (int) ? "X" : \ - "llX") -#endif - -/* @@ end of prolog @@ */ - -#ifdef _LIBC -/* Rename the non ISO C functions. This is required by the standard - because some ISO C functions will require linking with this object - file and the name space must not be polluted. */ -# define open __open -# define close __close -# define read __read -# define mmap __mmap -# define munmap __munmap -#endif - -/* For those losing systems which don't have `alloca' we have to add - some additional code emulating it. */ -#ifdef HAVE_ALLOCA -# define freea(p) /* nothing */ -#else -# define alloca(n) malloc (n) -# define freea(p) free (p) -#endif - -/* For systems that distinguish between text and binary I/O. - O_BINARY is usually declared in <fcntl.h>. */ -#if !defined O_BINARY && defined _O_BINARY - /* For MSC-compatible compilers. */ -# define O_BINARY _O_BINARY -# define O_TEXT _O_TEXT -#endif -#ifdef __BEOS__ - /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect. */ -# undef O_BINARY -# undef O_TEXT -#endif -/* On reasonable systems, binary I/O is the default. */ -#ifndef O_BINARY -# define O_BINARY 0 -#endif - - -/* Prototypes for local functions. Needed to ensure compiler checking of - function argument counts despite of K&R C function definition syntax. */ -static const char *get_sysdep_segment_value PARAMS ((const char *name)); - - -/* We need a sign, whether a new catalog was loaded, which can be associated - with all translations. This is important if the translations are - cached by one of GCC's features. */ -int _nl_msg_cat_cntr; - - -/* Expand a system dependent string segment. Return NULL if unsupported. */ -static const char * -get_sysdep_segment_value (name) - const char *name; -{ - /* Test for an ISO C 99 section 7.8.1 format string directive. - Syntax: - P R I { d | i | o | u | x | X } - { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */ - /* We don't use a table of 14 times 6 'const char *' strings here, because - data relocations cost startup time. */ - if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I') - { - if (name[3] == 'd' || name[3] == 'i' || name[3] == 'o' || name[3] == 'u' - || name[3] == 'x' || name[3] == 'X') - { - if (name[4] == '8' && name[5] == '\0') - { - if (name[3] == 'd') - return PRId8; - if (name[3] == 'i') - return PRIi8; - if (name[3] == 'o') - return PRIo8; - if (name[3] == 'u') - return PRIu8; - if (name[3] == 'x') - return PRIx8; - if (name[3] == 'X') - return PRIX8; - abort (); - } - if (name[4] == '1' && name[5] == '6' && name[6] == '\0') - { - if (name[3] == 'd') - return PRId16; - if (name[3] == 'i') - return PRIi16; - if (name[3] == 'o') - return PRIo16; - if (name[3] == 'u') - return PRIu16; - if (name[3] == 'x') - return PRIx16; - if (name[3] == 'X') - return PRIX16; - abort (); - } - if (name[4] == '3' && name[5] == '2' && name[6] == '\0') - { - if (name[3] == 'd') - return PRId32; - if (name[3] == 'i') - return PRIi32; - if (name[3] == 'o') - return PRIo32; - if (name[3] == 'u') - return PRIu32; - if (name[3] == 'x') - return PRIx32; - if (name[3] == 'X') - return PRIX32; - abort (); - } - if (name[4] == '6' && name[5] == '4' && name[6] == '\0') - { - if (name[3] == 'd') - return PRId64; - if (name[3] == 'i') - return PRIi64; - if (name[3] == 'o') - return PRIo64; - if (name[3] == 'u') - return PRIu64; - if (name[3] == 'x') - return PRIx64; - if (name[3] == 'X') - return PRIX64; - abort (); - } - if (name[4] == 'L' && name[5] == 'E' && name[6] == 'A' - && name[7] == 'S' && name[8] == 'T') - { - if (name[9] == '8' && name[10] == '\0') - { - if (name[3] == 'd') - return PRIdLEAST8; - if (name[3] == 'i') - return PRIiLEAST8; - if (name[3] == 'o') - return PRIoLEAST8; - if (name[3] == 'u') - return PRIuLEAST8; - if (name[3] == 'x') - return PRIxLEAST8; - if (name[3] == 'X') - return PRIXLEAST8; - abort (); - } - if (name[9] == '1' && name[10] == '6' && name[11] == '\0') - { - if (name[3] == 'd') - return PRIdLEAST16; - if (name[3] == 'i') - return PRIiLEAST16; - if (name[3] == 'o') - return PRIoLEAST16; - if (name[3] == 'u') - return PRIuLEAST16; - if (name[3] == 'x') - return PRIxLEAST16; - if (name[3] == 'X') - return PRIXLEAST16; - abort (); - } - if (name[9] == '3' && name[10] == '2' && name[11] == '\0') - { - if (name[3] == 'd') - return PRIdLEAST32; - if (name[3] == 'i') - return PRIiLEAST32; - if (name[3] == 'o') - return PRIoLEAST32; - if (name[3] == 'u') - return PRIuLEAST32; - if (name[3] == 'x') - return PRIxLEAST32; - if (name[3] == 'X') - return PRIXLEAST32; - abort (); - } - if (name[9] == '6' && name[10] == '4' && name[11] == '\0') - { - if (name[3] == 'd') - return PRIdLEAST64; - if (name[3] == 'i') - return PRIiLEAST64; - if (name[3] == 'o') - return PRIoLEAST64; - if (name[3] == 'u') - return PRIuLEAST64; - if (name[3] == 'x') - return PRIxLEAST64; - if (name[3] == 'X') - return PRIXLEAST64; - abort (); - } - } - if (name[4] == 'F' && name[5] == 'A' && name[6] == 'S' - && name[7] == 'T') - { - if (name[8] == '8' && name[9] == '\0') - { - if (name[3] == 'd') - return PRIdFAST8; - if (name[3] == 'i') - return PRIiFAST8; - if (name[3] == 'o') - return PRIoFAST8; - if (name[3] == 'u') - return PRIuFAST8; - if (name[3] == 'x') - return PRIxFAST8; - if (name[3] == 'X') - return PRIXFAST8; - abort (); - } - if (name[8] == '1' && name[9] == '6' && name[10] == '\0') - { - if (name[3] == 'd') - return PRIdFAST16; - if (name[3] == 'i') - return PRIiFAST16; - if (name[3] == 'o') - return PRIoFAST16; - if (name[3] == 'u') - return PRIuFAST16; - if (name[3] == 'x') - return PRIxFAST16; - if (name[3] == 'X') - return PRIXFAST16; - abort (); - } - if (name[8] == '3' && name[9] == '2' && name[10] == '\0') - { - if (name[3] == 'd') - return PRIdFAST32; - if (name[3] == 'i') - return PRIiFAST32; - if (name[3] == 'o') - return PRIoFAST32; - if (name[3] == 'u') - return PRIuFAST32; - if (name[3] == 'x') - return PRIxFAST32; - if (name[3] == 'X') - return PRIXFAST32; - abort (); - } - if (name[8] == '6' && name[9] == '4' && name[10] == '\0') - { - if (name[3] == 'd') - return PRIdFAST64; - if (name[3] == 'i') - return PRIiFAST64; - if (name[3] == 'o') - return PRIoFAST64; - if (name[3] == 'u') - return PRIuFAST64; - if (name[3] == 'x') - return PRIxFAST64; - if (name[3] == 'X') - return PRIXFAST64; - abort (); - } - } - if (name[4] == 'M' && name[5] == 'A' && name[6] == 'X' - && name[7] == '\0') - { - if (name[3] == 'd') - return PRIdMAX; - if (name[3] == 'i') - return PRIiMAX; - if (name[3] == 'o') - return PRIoMAX; - if (name[3] == 'u') - return PRIuMAX; - if (name[3] == 'x') - return PRIxMAX; - if (name[3] == 'X') - return PRIXMAX; - abort (); - } - if (name[4] == 'P' && name[5] == 'T' && name[6] == 'R' - && name[7] == '\0') - { - if (name[3] == 'd') - return PRIdPTR; - if (name[3] == 'i') - return PRIiPTR; - if (name[3] == 'o') - return PRIoPTR; - if (name[3] == 'u') - return PRIuPTR; - if (name[3] == 'x') - return PRIxPTR; - if (name[3] == 'X') - return PRIXPTR; - abort (); - } - } - } - /* Other system dependent strings are not valid. */ - return NULL; -} - -/* Initialize the codeset dependent parts of an opened message catalog. - Return the header entry. */ -const char * -internal_function -_nl_init_domain_conv (domain_file, domain, domainbinding) - struct loaded_l10nfile *domain_file; - struct loaded_domain *domain; - struct binding *domainbinding; -{ - /* Find out about the character set the file is encoded with. - This can be found (in textual form) in the entry "". If this - entry does not exist or if this does not contain the `charset=' - information, we will assume the charset matches the one the - current locale and we don't have to perform any conversion. */ - char *nullentry; - size_t nullentrylen; - - /* Preinitialize fields, to avoid recursion during _nl_find_msg. */ - domain->codeset_cntr = - (domainbinding != NULL ? domainbinding->codeset_cntr : 0); -#ifdef _LIBC - domain->conv = (__gconv_t) -1; -#else -# if HAVE_ICONV - domain->conv = (iconv_t) -1; -# endif -#endif - domain->conv_tab = NULL; - - /* Get the header entry. */ - nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen); - - if (nullentry != NULL) - { -#if defined _LIBC || HAVE_ICONV - const char *charsetstr; - - charsetstr = strstr (nullentry, "charset="); - if (charsetstr != NULL) - { - size_t len; - char *charset; - const char *outcharset; - - charsetstr += strlen ("charset="); - len = strcspn (charsetstr, " \t\n"); - - charset = (char *) alloca (len + 1); -# if defined _LIBC || HAVE_MEMPCPY - *((char *) mempcpy (charset, charsetstr, len)) = '\0'; -# else - memcpy (charset, charsetstr, len); - charset[len] = '\0'; -# endif - - /* The output charset should normally be determined by the - locale. But sometimes the locale is not used or not correctly - set up, so we provide a possibility for the user to override - this. Moreover, the value specified through - bind_textdomain_codeset overrides both. */ - if (domainbinding != NULL && domainbinding->codeset != NULL) - outcharset = domainbinding->codeset; - else - { - outcharset = getenv ("OUTPUT_CHARSET"); - if (outcharset == NULL || outcharset[0] == '\0') - { -# ifdef _LIBC - outcharset = _NL_CURRENT (LC_CTYPE, CODESET); -# else -# if HAVE_ICONV - extern const char *locale_charset PARAMS ((void)); - outcharset = locale_charset (); -# endif -# endif - } - } - -# ifdef _LIBC - /* We always want to use transliteration. */ - outcharset = norm_add_slashes (outcharset, "TRANSLIT"); - charset = norm_add_slashes (charset, NULL); - if (__gconv_open (outcharset, charset, &domain->conv, - GCONV_AVOID_NOCONV) - != __GCONV_OK) - domain->conv = (__gconv_t) -1; -# else -# if HAVE_ICONV - /* When using GNU libc >= 2.2 or GNU libiconv >= 1.5, - we want to use transliteration. */ -# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 \ - || _LIBICONV_VERSION >= 0x0105 - if (strchr (outcharset, '/') == NULL) - { - char *tmp; - - len = strlen (outcharset); - tmp = (char *) alloca (len + 10 + 1); - memcpy (tmp, outcharset, len); - memcpy (tmp + len, "//TRANSLIT", 10 + 1); - outcharset = tmp; - - domain->conv = iconv_open (outcharset, charset); - - freea (outcharset); - } - else -# endif - domain->conv = iconv_open (outcharset, charset); -# endif -# endif - - freea (charset); - } -#endif /* _LIBC || HAVE_ICONV */ - } - - return nullentry; -} - -/* Frees the codeset dependent parts of an opened message catalog. */ -void -internal_function -_nl_free_domain_conv (domain) - struct loaded_domain *domain; -{ - if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1) - free (domain->conv_tab); - -#ifdef _LIBC - if (domain->conv != (__gconv_t) -1) - __gconv_close (domain->conv); -#else -# if HAVE_ICONV - if (domain->conv != (iconv_t) -1) - iconv_close (domain->conv); -# endif -#endif -} - -/* Load the message catalogs specified by FILENAME. If it is no valid - message catalog do nothing. */ -void -internal_function -_nl_load_domain (domain_file, domainbinding) - struct loaded_l10nfile *domain_file; - struct binding *domainbinding; -{ - int fd; - size_t size; -#ifdef _LIBC - struct stat64 st; -#else - struct stat st; -#endif - struct mo_file_header *data = (struct mo_file_header *) -1; - int use_mmap = 0; - struct loaded_domain *domain; - int revision; - const char *nullentry; - - domain_file->decided = 1; - domain_file->data = NULL; - - /* Note that it would be useless to store domainbinding in domain_file - because domainbinding might be == NULL now but != NULL later (after - a call to bind_textdomain_codeset). */ - - /* If the record does not represent a valid locale the FILENAME - might be NULL. This can happen when according to the given - specification the locale file name is different for XPG and CEN - syntax. */ - if (domain_file->filename == NULL) - return; - - /* Try to open the addressed file. */ - fd = open (domain_file->filename, O_RDONLY | O_BINARY); - if (fd == -1) - return; - - /* We must know about the size of the file. */ - if ( -#ifdef _LIBC - __builtin_expect (fstat64 (fd, &st) != 0, 0) -#else - __builtin_expect (fstat (fd, &st) != 0, 0) -#endif - || __builtin_expect ((size = (size_t) st.st_size) != st.st_size, 0) - || __builtin_expect (size < sizeof (struct mo_file_header), 0)) - { - /* Something went wrong. */ - close (fd); - return; - } - -#ifdef HAVE_MMAP - /* Now we are ready to load the file. If mmap() is available we try - this first. If not available or it failed we try to load it. */ - data = (struct mo_file_header *) mmap (NULL, size, PROT_READ, - MAP_PRIVATE, fd, 0); - - if (__builtin_expect (data != (struct mo_file_header *) -1, 1)) - { - /* mmap() call was successful. */ - close (fd); - use_mmap = 1; - } -#endif - - /* If the data is not yet available (i.e. mmap'ed) we try to load - it manually. */ - if (data == (struct mo_file_header *) -1) - { - size_t to_read; - char *read_ptr; - - data = (struct mo_file_header *) malloc (size); - if (data == NULL) - return; - - to_read = size; - read_ptr = (char *) data; - do - { - long int nb = (long int) read (fd, read_ptr, to_read); - if (nb <= 0) - { -#ifdef EINTR - if (nb == -1 && errno == EINTR) - continue; -#endif - close (fd); - return; - } - read_ptr += nb; - to_read -= nb; - } - while (to_read > 0); - - close (fd); - } - - /* Using the magic number we can test whether it really is a message - catalog file. */ - if (__builtin_expect (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED, - 0)) - { - /* The magic number is wrong: not a message catalog file. */ -#ifdef HAVE_MMAP - if (use_mmap) - munmap ((caddr_t) data, size); - else -#endif - free (data); - return; - } - - domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain)); - if (domain == NULL) - return; - domain_file->data = domain; - - domain->data = (char *) data; - domain->use_mmap = use_mmap; - domain->mmap_size = size; - domain->must_swap = data->magic != _MAGIC; - domain->malloced = NULL; - - /* Fill in the information about the available tables. */ - revision = W (domain->must_swap, data->revision); - /* We support only the major revision 0. */ - switch (revision >> 16) - { - case 0: - domain->nstrings = W (domain->must_swap, data->nstrings); - domain->orig_tab = (const struct string_desc *) - ((char *) data + W (domain->must_swap, data->orig_tab_offset)); - domain->trans_tab = (const struct string_desc *) - ((char *) data + W (domain->must_swap, data->trans_tab_offset)); - domain->hash_size = W (domain->must_swap, data->hash_tab_size); - domain->hash_tab = - (domain->hash_size > 2 - ? (const nls_uint32 *) - ((char *) data + W (domain->must_swap, data->hash_tab_offset)) - : NULL); - domain->must_swap_hash_tab = domain->must_swap; - - /* Now dispatch on the minor revision. */ - switch (revision & 0xffff) - { - case 0: - domain->n_sysdep_strings = 0; - domain->orig_sysdep_tab = NULL; - domain->trans_sysdep_tab = NULL; - break; - case 1: - default: - { - nls_uint32 n_sysdep_strings; - - if (domain->hash_tab == NULL) - /* This is invalid. These minor revisions need a hash table. */ - goto invalid; - - n_sysdep_strings = - W (domain->must_swap, data->n_sysdep_strings); - if (n_sysdep_strings > 0) - { - nls_uint32 n_sysdep_segments; - const struct sysdep_segment *sysdep_segments; - const char **sysdep_segment_values; - const nls_uint32 *orig_sysdep_tab; - const nls_uint32 *trans_sysdep_tab; - size_t memneed; - char *mem; - struct sysdep_string_desc *inmem_orig_sysdep_tab; - struct sysdep_string_desc *inmem_trans_sysdep_tab; - nls_uint32 *inmem_hash_tab; - unsigned int i; - - /* Get the values of the system dependent segments. */ - n_sysdep_segments = - W (domain->must_swap, data->n_sysdep_segments); - sysdep_segments = (const struct sysdep_segment *) - ((char *) data - + W (domain->must_swap, data->sysdep_segments_offset)); - sysdep_segment_values = - alloca (n_sysdep_segments * sizeof (const char *)); - for (i = 0; i < n_sysdep_segments; i++) - { - const char *name = - (char *) data - + W (domain->must_swap, sysdep_segments[i].offset); - nls_uint32 namelen = - W (domain->must_swap, sysdep_segments[i].length); - - if (!(namelen > 0 && name[namelen - 1] == '\0')) - { - freea (sysdep_segment_values); - goto invalid; - } - - sysdep_segment_values[i] = get_sysdep_segment_value (name); - } - - orig_sysdep_tab = (const nls_uint32 *) - ((char *) data - + W (domain->must_swap, data->orig_sysdep_tab_offset)); - trans_sysdep_tab = (const nls_uint32 *) - ((char *) data - + W (domain->must_swap, data->trans_sysdep_tab_offset)); - - /* Compute the amount of additional memory needed for the - system dependent strings and the augmented hash table. */ - memneed = 2 * n_sysdep_strings - * sizeof (struct sysdep_string_desc) - + domain->hash_size * sizeof (nls_uint32); - for (i = 0; i < 2 * n_sysdep_strings; i++) - { - const struct sysdep_string *sysdep_string = - (const struct sysdep_string *) - ((char *) data - + W (domain->must_swap, - i < n_sysdep_strings - ? orig_sysdep_tab[i] - : trans_sysdep_tab[i - n_sysdep_strings])); - size_t need = 0; - const struct segment_pair *p = sysdep_string->segments; - - if (W (domain->must_swap, p->sysdepref) != SEGMENTS_END) - for (p = sysdep_string->segments;; p++) - { - nls_uint32 sysdepref; - - need += W (domain->must_swap, p->segsize); - - sysdepref = W (domain->must_swap, p->sysdepref); - if (sysdepref == SEGMENTS_END) - break; - - if (sysdepref >= n_sysdep_segments) - { - /* Invalid. */ - freea (sysdep_segment_values); - goto invalid; - } - - need += strlen (sysdep_segment_values[sysdepref]); - } - - memneed += need; - } - - /* Allocate additional memory. */ - mem = (char *) malloc (memneed); - if (mem == NULL) - goto invalid; - - domain->malloced = mem; - inmem_orig_sysdep_tab = (struct sysdep_string_desc *) mem; - mem += n_sysdep_strings * sizeof (struct sysdep_string_desc); - inmem_trans_sysdep_tab = (struct sysdep_string_desc *) mem; - mem += n_sysdep_strings * sizeof (struct sysdep_string_desc); - inmem_hash_tab = (nls_uint32 *) mem; - mem += domain->hash_size * sizeof (nls_uint32); - - /* Compute the system dependent strings. */ - for (i = 0; i < 2 * n_sysdep_strings; i++) - { - const struct sysdep_string *sysdep_string = - (const struct sysdep_string *) - ((char *) data - + W (domain->must_swap, - i < n_sysdep_strings - ? orig_sysdep_tab[i] - : trans_sysdep_tab[i - n_sysdep_strings])); - const char *static_segments = - (char *) data - + W (domain->must_swap, sysdep_string->offset); - const struct segment_pair *p = sysdep_string->segments; - - /* Concatenate the segments, and fill - inmem_orig_sysdep_tab[i] (for i < n_sysdep_strings) and - inmem_trans_sysdep_tab[i-n_sysdep_strings] (for - i >= n_sysdep_strings). */ - - if (W (domain->must_swap, p->sysdepref) == SEGMENTS_END) - { - /* Only one static segment. */ - inmem_orig_sysdep_tab[i].length = - W (domain->must_swap, p->segsize); - inmem_orig_sysdep_tab[i].pointer = static_segments; - } - else - { - inmem_orig_sysdep_tab[i].pointer = mem; - - for (p = sysdep_string->segments;; p++) - { - nls_uint32 segsize = - W (domain->must_swap, p->segsize); - nls_uint32 sysdepref = - W (domain->must_swap, p->sysdepref); - size_t n; - - if (segsize > 0) - { - memcpy (mem, static_segments, segsize); - mem += segsize; - static_segments += segsize; - } - - if (sysdepref == SEGMENTS_END) - break; - - n = strlen (sysdep_segment_values[sysdepref]); - memcpy (mem, sysdep_segment_values[sysdepref], n); - mem += n; - } - - inmem_orig_sysdep_tab[i].length = - mem - inmem_orig_sysdep_tab[i].pointer; - } - } - - /* Compute the augmented hash table. */ - for (i = 0; i < domain->hash_size; i++) - inmem_hash_tab[i] = - W (domain->must_swap_hash_tab, domain->hash_tab[i]); - for (i = 0; i < n_sysdep_strings; i++) - { - const char *msgid = inmem_orig_sysdep_tab[i].pointer; - nls_uint32 hash_val = hash_string (msgid); - nls_uint32 idx = hash_val % domain->hash_size; - nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); - - for (;;) - { - if (inmem_hash_tab[idx] == 0) - { - /* Hash table entry is empty. Use it. */ - inmem_hash_tab[idx] = 1 + domain->nstrings + i; - break; - } - - if (idx >= domain->hash_size - incr) - idx -= domain->hash_size - incr; - else - idx += incr; - } - } - - freea (sysdep_segment_values); - - domain->n_sysdep_strings = n_sysdep_strings; - domain->orig_sysdep_tab = inmem_orig_sysdep_tab; - domain->trans_sysdep_tab = inmem_trans_sysdep_tab; - - domain->hash_tab = inmem_hash_tab; - domain->must_swap_hash_tab = 0; - } - else - { - domain->n_sysdep_strings = 0; - domain->orig_sysdep_tab = NULL; - domain->trans_sysdep_tab = NULL; - } - } - break; - } - break; - default: - /* This is an invalid revision. */ - invalid: - /* This is an invalid .mo file. */ - if (domain->malloced) - free (domain->malloced); -#ifdef HAVE_MMAP - if (use_mmap) - munmap ((caddr_t) data, size); - else -#endif - free (data); - free (domain); - domain_file->data = NULL; - return; - } - - /* Now initialize the character set converter from the character set - the file is encoded with (found in the header entry) to the domain's - specified character set or the locale's character set. */ - nullentry = _nl_init_domain_conv (domain_file, domain, domainbinding); - - /* Also look for a plural specification. */ - EXTRACT_PLURAL_EXPRESSION (nullentry, &domain->plural, &domain->nplurals); -} - - -#ifdef _LIBC -void -internal_function -_nl_unload_domain (domain) - struct loaded_domain *domain; -{ - if (domain->plural != &__gettext_germanic_plural) - __gettext_free_exp (domain->plural); - - _nl_free_domain_conv (domain); - - if (domain->malloced) - free (domain->malloced); - -# ifdef _POSIX_MAPPED_FILES - if (domain->use_mmap) - munmap ((caddr_t) domain->data, domain->mmap_size); - else -# endif /* _POSIX_MAPPED_FILES */ - free ((void *) domain->data); - - free (domain); -} -#endif diff --git a/src/audacious/intl/localcharset.c b/src/audacious/intl/localcharset.c deleted file mode 100644 index 9b91d62..0000000 --- a/src/audacious/intl/localcharset.c +++ /dev/null @@ -1,398 +0,0 @@ -/* Determine a canonical name for the current locale's character encoding. - - Copyright (C) 2000-2003 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Written by Bruno Haible <bruno@clisp.org>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -/* Specification. */ -#include "localcharset.h" - -#if HAVE_STDDEF_H -# include <stddef.h> -#endif - -#include <stdio.h> -#if HAVE_STRING_H -# include <string.h> -#else -# include <strings.h> -#endif -#if HAVE_STDLIB_H -# include <stdlib.h> -#endif - -#if defined _WIN32 || defined __WIN32__ -# undef WIN32 /* avoid warning on mingw32 */ -# define WIN32 -#endif - -#if defined __EMX__ -/* Assume EMX program runs on OS/2, even if compiled under DOS. */ -# define OS2 -#endif - -#if !defined WIN32 -# if HAVE_LANGINFO_CODESET -# include <langinfo.h> -# else -# if HAVE_SETLOCALE -# include <locale.h> -# endif -# endif -#elif defined WIN32 -# define WIN32_LEAN_AND_MEAN -# include <windows.h> -#endif -#if defined OS2 -# define INCL_DOS -# include <os2.h> -#endif - -#if ENABLE_RELOCATABLE -# include "relocatable.h" -#else -# define relocate(pathname) (pathname) -#endif - -#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ - /* Win32, OS/2, DOS */ -# define ISSLASH(C) ((C) == '/' || (C) == '\\') -#endif - -#ifndef DIRECTORY_SEPARATOR -# define DIRECTORY_SEPARATOR '/' -#endif - -#ifndef ISSLASH -# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR) -#endif - -#ifdef HAVE_GETC_UNLOCKED -# undef getc -# define getc getc_unlocked -#endif - -/* The following static variable is declared 'volatile' to avoid a - possible multithread problem in the function get_charset_aliases. If we - are running in a threaded environment, and if two threads initialize - 'charset_aliases' simultaneously, both will produce the same value, - and everything will be ok if the two assignments to 'charset_aliases' - are atomic. But I don't know what will happen if the two assignments mix. */ -#if __STDC__ != 1 -# define volatile /* empty */ -#endif -/* Pointer to the contents of the charset.alias file, if it has already been - read, else NULL. Its format is: - ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0' */ -static const char * volatile charset_aliases; - -/* Return a pointer to the contents of the charset.alias file. */ -static const char * -get_charset_aliases () -{ - const char *cp; - - cp = charset_aliases; - if (cp == NULL) - { -#if !(defined VMS || defined WIN32) - FILE *fp; - const char *dir = relocate (LIBDIR); - const char *base = "charset.alias"; - char *file_name; - - /* Concatenate dir and base into freshly allocated file_name. */ - { - size_t dir_len = strlen (dir); - size_t base_len = strlen (base); - int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1])); - file_name = (char *) malloc (dir_len + add_slash + base_len + 1); - if (file_name != NULL) - { - memcpy (file_name, dir, dir_len); - if (add_slash) - file_name[dir_len] = DIRECTORY_SEPARATOR; - memcpy (file_name + dir_len + add_slash, base, base_len + 1); - } - } - - if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL) - /* Out of memory or file not found, treat it as empty. */ - cp = ""; - else - { - /* Parse the file's contents. */ - int c; - char buf1[50+1]; - char buf2[50+1]; - char *res_ptr = NULL; - size_t res_size = 0; - size_t l1, l2; - - for (;;) - { - c = getc (fp); - if (c == EOF) - break; - if (c == '\n' || c == ' ' || c == '\t') - continue; - if (c == '#') - { - /* Skip comment, to end of line. */ - do - c = getc (fp); - while (!(c == EOF || c == '\n')); - if (c == EOF) - break; - continue; - } - ungetc (c, fp); - if (fscanf (fp, "%50s %50s", buf1, buf2) < 2) - break; - l1 = strlen (buf1); - l2 = strlen (buf2); - if (res_size == 0) - { - res_size = l1 + 1 + l2 + 1; - res_ptr = (char *) malloc (res_size + 1); - } - else - { - res_size += l1 + 1 + l2 + 1; - res_ptr = (char *) realloc (res_ptr, res_size + 1); - } - if (res_ptr == NULL) - { - /* Out of memory. */ - res_size = 0; - break; - } - g_strlcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1, res_size - (l2 + 1)); - g_strlcpy (res_ptr + res_size - (l2 + 1), buf2, res_size - (l1 + 1)); - } - fclose (fp); - if (res_size == 0) - cp = ""; - else - { - *(res_ptr + res_size) = '\0'; - cp = res_ptr; - } - } - - if (file_name != NULL) - free (file_name); - -#else - -# if defined VMS - /* To avoid the troubles of an extra file charset.alias_vms in the - sources of many GNU packages, simply inline the aliases here. */ - /* The list of encodings is taken from the OpenVMS 7.3-1 documentation - "Compaq C Run-Time Library Reference Manual for OpenVMS systems" - section 10.7 "Handling Different Character Sets". */ - cp = "ISO8859-1" "\0" "ISO-8859-1" "\0" - "ISO8859-2" "\0" "ISO-8859-2" "\0" - "ISO8859-5" "\0" "ISO-8859-5" "\0" - "ISO8859-7" "\0" "ISO-8859-7" "\0" - "ISO8859-8" "\0" "ISO-8859-8" "\0" - "ISO8859-9" "\0" "ISO-8859-9" "\0" - /* Japanese */ - "eucJP" "\0" "EUC-JP" "\0" - "SJIS" "\0" "SHIFT_JIS" "\0" - "DECKANJI" "\0" "DEC-KANJI" "\0" - "SDECKANJI" "\0" "EUC-JP" "\0" - /* Chinese */ - "eucTW" "\0" "EUC-TW" "\0" - "DECHANYU" "\0" "DEC-HANYU" "\0" - "DECHANZI" "\0" "GB2312" "\0" - /* Korean */ - "DECKOREAN" "\0" "EUC-KR" "\0"; -# endif - -# if defined WIN32 - /* To avoid the troubles of installing a separate file in the same - directory as the DLL and of retrieving the DLL's directory at - runtime, simply inline the aliases here. */ - - cp = "CP936" "\0" "GBK" "\0" - "CP1361" "\0" "JOHAB" "\0" - "CP20127" "\0" "ASCII" "\0" - "CP20866" "\0" "KOI8-R" "\0" - "CP21866" "\0" "KOI8-RU" "\0" - "CP28591" "\0" "ISO-8859-1" "\0" - "CP28592" "\0" "ISO-8859-2" "\0" - "CP28593" "\0" "ISO-8859-3" "\0" - "CP28594" "\0" "ISO-8859-4" "\0" - "CP28595" "\0" "ISO-8859-5" "\0" - "CP28596" "\0" "ISO-8859-6" "\0" - "CP28597" "\0" "ISO-8859-7" "\0" - "CP28598" "\0" "ISO-8859-8" "\0" - "CP28599" "\0" "ISO-8859-9" "\0" - "CP28605" "\0" "ISO-8859-15" "\0"; -# endif -#endif - - charset_aliases = cp; - } - - return cp; -} - -/* Determine the current locale's character encoding, and canonicalize it - into one of the canonical names listed in config.charset. - The result must not be freed; it is statically allocated. - If the canonical name cannot be determined, the result is a non-canonical - name. */ - -#ifdef STATIC -STATIC -#endif -const char * -locale_charset () -{ - const char *codeset; - const char *aliases; - -#if !(defined WIN32 || defined OS2) - -# if HAVE_LANGINFO_CODESET - - /* Most systems support nl_langinfo (CODESET) nowadays. */ - codeset = nl_langinfo (CODESET); - -# else - - /* On old systems which lack it, use setlocale or getenv. */ - const char *locale = NULL; - - /* But most old systems don't have a complete set of locales. Some - (like SunOS 4 or DJGPP) have only the C locale. Therefore we don't - use setlocale here; it would return "C" when it doesn't support the - locale name the user has set. */ -# if HAVE_SETLOCALE && 0 - locale = setlocale (LC_CTYPE, NULL); -# endif - if (locale == NULL || locale[0] == '\0') - { - locale = getenv ("LC_ALL"); - if (locale == NULL || locale[0] == '\0') - { - locale = getenv ("LC_CTYPE"); - if (locale == NULL || locale[0] == '\0') - locale = getenv ("LANG"); - } - } - - /* On some old systems, one used to set locale = "iso8859_1". On others, - you set it to "language_COUNTRY.charset". In any case, we resolve it - through the charset.alias file. */ - codeset = locale; - -# endif - -#elif defined WIN32 - - static char buf[2 + 10 + 1]; - - /* Woe32 has a function returning the locale's codepage as a number. */ - sprintf (buf, "CP%u", GetACP ()); - codeset = buf; - -#elif defined OS2 - - const char *locale; - static char buf[2 + 10 + 1]; - ULONG cp[3]; - ULONG cplen; - - /* Allow user to override the codeset, as set in the operating system, - with standard language environment variables. */ - locale = getenv ("LC_ALL"); - if (locale == NULL || locale[0] == '\0') - { - locale = getenv ("LC_CTYPE"); - if (locale == NULL || locale[0] == '\0') - locale = getenv ("LANG"); - } - if (locale != NULL && locale[0] != '\0') - { - /* If the locale name contains an encoding after the dot, return it. */ - const char *dot = strchr (locale, '.'); - - if (dot != NULL) - { - const char *modifier; - - dot++; - /* Look for the possible @... trailer and remove it, if any. */ - modifier = strchr (dot, '@'); - if (modifier == NULL) - return dot; - if (modifier - dot < sizeof (buf)) - { - memcpy (buf, dot, modifier - dot); - buf [modifier - dot] = '\0'; - return buf; - } - } - - /* Resolve through the charset.alias file. */ - codeset = locale; - } - else - { - /* OS/2 has a function returning the locale's codepage as a number. */ - if (DosQueryCp (sizeof (cp), cp, &cplen)) - codeset = ""; - else - { - sprintf (buf, "CP%u", cp[0]); - codeset = buf; - } - } - -#endif - - if (codeset == NULL) - /* The canonical name cannot be determined. */ - codeset = ""; - - /* Resolve alias. */ - for (aliases = get_charset_aliases (); - *aliases != '\0'; - aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1) - if (strcmp (codeset, aliases) == 0 - || (aliases[0] == '*' && aliases[1] == '\0')) - { - codeset = aliases + strlen (aliases) + 1; - break; - } - - /* Don't return an empty string. GNU libc and GNU libiconv interpret - the empty string as denoting "the locale's character encoding", - thus GNU libiconv would call this function a second time. */ - if (codeset[0] == '\0') - codeset = "ASCII"; - - return codeset; -} diff --git a/src/audacious/intl/localcharset.h b/src/audacious/intl/localcharset.h deleted file mode 100644 index 129e4a4..0000000 --- a/src/audacious/intl/localcharset.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Determine a canonical name for the current locale's character encoding. - Copyright (C) 2000-2003 Free Software Foundation, Inc. - This file is part of the GNU CHARSET Library. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef _LOCALCHARSET_H -#define _LOCALCHARSET_H - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Determine the current locale's character encoding, and canonicalize it - into one of the canonical names listed in config.charset. - The result must not be freed; it is statically allocated. - If the canonical name cannot be determined, the result is a non-canonical - name. */ -extern const char * locale_charset (void); - - -#ifdef __cplusplus -} -#endif - - -#endif /* _LOCALCHARSET_H */ diff --git a/src/audacious/intl/locale.alias b/src/audacious/intl/locale.alias deleted file mode 100644 index 213357b..0000000 --- a/src/audacious/intl/locale.alias +++ /dev/null @@ -1,78 +0,0 @@ -# Locale name alias data base. -# Copyright (C) 1996,1997,1998,1999,2000,2001 Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU Library General Public License as published -# by the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# Library General Public License for more details. -# -# You should have received a copy of the GNU Library General Public -# License along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. - -# The format of this file is the same as for the corresponding file of -# the X Window System, which normally can be found in -# /usr/lib/X11/locale/locale.alias -# A single line contains two fields: an alias and a substitution value. -# All entries are case independent. - -# Note: This file is far from being complete. If you have a value for -# your own site which you think might be useful for others too, share -# it with the rest of us. Send it using the `glibcbug' script to -# bugs@gnu.org. - -# Packages using this file: - -bokmal no_NO.ISO-8859-1 -bokmål no_NO.ISO-8859-1 -catalan ca_ES.ISO-8859-1 -croatian hr_HR.ISO-8859-2 -czech cs_CZ.ISO-8859-2 -danish da_DK.ISO-8859-1 -dansk da_DK.ISO-8859-1 -deutsch de_DE.ISO-8859-1 -dutch nl_NL.ISO-8859-1 -eesti et_EE.ISO-8859-1 -estonian et_EE.ISO-8859-1 -finnish fi_FI.ISO-8859-1 -français fr_FR.ISO-8859-1 -french fr_FR.ISO-8859-1 -galego gl_ES.ISO-8859-1 -galician gl_ES.ISO-8859-1 -german de_DE.ISO-8859-1 -greek el_GR.ISO-8859-7 -hebrew he_IL.ISO-8859-8 -hrvatski hr_HR.ISO-8859-2 -hungarian hu_HU.ISO-8859-2 -icelandic is_IS.ISO-8859-1 -italian it_IT.ISO-8859-1 -japanese ja_JP.eucJP -japanese.euc ja_JP.eucJP -ja_JP ja_JP.eucJP -ja_JP.ujis ja_JP.eucJP -japanese.sjis ja_JP.SJIS -korean ko_KR.eucKR -korean.euc ko_KR.eucKR -ko_KR ko_KR.eucKR -lithuanian lt_LT.ISO-8859-13 -nb_NO no_NO.ISO-8859-1 -nb_NO.ISO-8859-1 no_NO.ISO-8859-1 -norwegian no_NO.ISO-8859-1 -nynorsk nn_NO.ISO-8859-1 -polish pl_PL.ISO-8859-2 -portuguese pt_PT.ISO-8859-1 -romanian ro_RO.ISO-8859-2 -russian ru_RU.ISO-8859-5 -slovak sk_SK.ISO-8859-2 -slovene sl_SI.ISO-8859-2 -slovenian sl_SI.ISO-8859-2 -spanish es_ES.ISO-8859-1 -swedish sv_SE.ISO-8859-1 -thai th_TH.TIS-620 -turkish tr_TR.ISO-8859-9 diff --git a/src/audacious/intl/localealias.c b/src/audacious/intl/localealias.c deleted file mode 100644 index 3bc0f2e..0000000 --- a/src/audacious/intl/localealias.c +++ /dev/null @@ -1,419 +0,0 @@ -/* Handle aliases for locale names. - Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Tell glibc's <string.h> to provide a prototype for mempcpy(). - This must come before <config.h> because <config.h> may include - <features.h>, and once <features.h> has been included, it's too late. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <ctype.h> -#include <stdio.h> -#if defined _LIBC || defined HAVE___FSETLOCKING -# include <stdio_ext.h> -#endif -#include <sys/types.h> - -#ifdef __GNUC__ -# undef alloca -# define alloca __builtin_alloca -# define HAVE_ALLOCA 1 -#else -# ifdef _MSC_VER -# include <malloc.h> -# define alloca _alloca -# else -# if defined HAVE_ALLOCA_H || defined _LIBC -# include <alloca.h> -# else -# ifdef _AIX - #pragma alloca -# else -# ifndef alloca -char *alloca (); -# endif -# endif -# endif -# endif -#endif - -#include <stdlib.h> -#include <string.h> - -#include "gettextP.h" - -#if ENABLE_RELOCATABLE -# include "relocatable.h" -#else -# define relocate(pathname) (pathname) -#endif - -/* @@ end of prolog @@ */ - -#ifdef _LIBC -/* Rename the non ANSI C functions. This is required by the standard - because some ANSI C functions will require linking with this object - file and the name space must not be polluted. */ -# define strcasecmp __strcasecmp - -# ifndef mempcpy -# define mempcpy __mempcpy -# endif -# define HAVE_MEMPCPY 1 -# define HAVE___FSETLOCKING 1 - -/* We need locking here since we can be called from different places. */ -# include <bits/libc-lock.h> - -__libc_lock_define_initialized (static, lock); -#endif - -#ifndef internal_function -# define internal_function -#endif - -/* Some optimizations for glibc. */ -#ifdef _LIBC -# define FEOF(fp) feof_unlocked (fp) -# define FGETS(buf, n, fp) fgets_unlocked (buf, n, fp) -#else -# define FEOF(fp) feof (fp) -# define FGETS(buf, n, fp) fgets (buf, n, fp) -#endif - -/* For those losing systems which don't have `alloca' we have to add - some additional code emulating it. */ -#ifdef HAVE_ALLOCA -# define freea(p) /* nothing */ -#else -# define alloca(n) malloc (n) -# define freea(p) free (p) -#endif - -#if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED -# undef fgets -# define fgets(buf, len, s) fgets_unlocked (buf, len, s) -#endif -#if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED -# undef feof -# define feof(s) feof_unlocked (s) -#endif - - -struct alias_map -{ - const char *alias; - const char *value; -}; - - -#ifndef _LIBC -# define libc_freeres_ptr(decl) decl -#endif - -libc_freeres_ptr (static char *string_space); -static size_t string_space_act; -static size_t string_space_max; -libc_freeres_ptr (static struct alias_map *map); -static size_t nmap; -static size_t maxmap; - - -/* Prototypes for local functions. */ -static size_t read_alias_file PARAMS ((const char *fname, int fname_len)) - internal_function; -static int extend_alias_table PARAMS ((void)); -static int alias_compare PARAMS ((const struct alias_map *map1, - const struct alias_map *map2)); - - -const char * -_nl_expand_alias (name) - const char *name; -{ - static const char *locale_alias_path; - struct alias_map *retval; - const char *result = NULL; - size_t added; - -#ifdef _LIBC - __libc_lock_lock (lock); -#endif - - if (locale_alias_path == NULL) - locale_alias_path = LOCALE_ALIAS_PATH; - - do - { - struct alias_map item; - - item.alias = name; - - if (nmap > 0) - retval = (struct alias_map *) bsearch (&item, map, nmap, - sizeof (struct alias_map), - (int (*) PARAMS ((const void *, - const void *)) - ) alias_compare); - else - retval = NULL; - - /* We really found an alias. Return the value. */ - if (retval != NULL) - { - result = retval->value; - break; - } - - /* Perhaps we can find another alias file. */ - added = 0; - while (added == 0 && locale_alias_path[0] != '\0') - { - const char *start; - - while (locale_alias_path[0] == PATH_SEPARATOR) - ++locale_alias_path; - start = locale_alias_path; - - while (locale_alias_path[0] != '\0' - && locale_alias_path[0] != PATH_SEPARATOR) - ++locale_alias_path; - - if (start < locale_alias_path) - added = read_alias_file (start, locale_alias_path - start); - } - } - while (added != 0); - -#ifdef _LIBC - __libc_lock_unlock (lock); -#endif - - return result; -} - - -static size_t -internal_function -read_alias_file (fname, fname_len) - const char *fname; - int fname_len; -{ - FILE *fp; - char *full_fname; - size_t added; - static const char aliasfile[] = "/locale.alias"; - - full_fname = (char *) alloca (fname_len + sizeof aliasfile); -#ifdef HAVE_MEMPCPY - mempcpy (mempcpy (full_fname, fname, fname_len), - aliasfile, sizeof aliasfile); -#else - memcpy (full_fname, fname, fname_len); - memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile); -#endif - - fp = fopen (relocate (full_fname), "r"); - freea (full_fname); - if (fp == NULL) - return 0; - -#ifdef HAVE___FSETLOCKING - /* No threads present. */ - __fsetlocking (fp, FSETLOCKING_BYCALLER); -#endif - - added = 0; - while (!FEOF (fp)) - { - /* It is a reasonable approach to use a fix buffer here because - a) we are only interested in the first two fields - b) these fields must be usable as file names and so must not - be that long - We avoid a multi-kilobyte buffer here since this would use up - stack space which we might not have if the program ran out of - memory. */ - char buf[400]; - char *alias; - char *value; - char *cp; - - if (FGETS (buf, sizeof buf, fp) == NULL) - /* EOF reached. */ - break; - - cp = buf; - /* Ignore leading white space. */ - while (isspace ((unsigned char) cp[0])) - ++cp; - - /* A leading '#' signals a comment line. */ - if (cp[0] != '\0' && cp[0] != '#') - { - alias = cp++; - while (cp[0] != '\0' && !isspace ((unsigned char) cp[0])) - ++cp; - /* Terminate alias name. */ - if (cp[0] != '\0') - *cp++ = '\0'; - - /* Now look for the beginning of the value. */ - while (isspace ((unsigned char) cp[0])) - ++cp; - - if (cp[0] != '\0') - { - size_t alias_len; - size_t value_len; - - value = cp++; - while (cp[0] != '\0' && !isspace ((unsigned char) cp[0])) - ++cp; - /* Terminate value. */ - if (cp[0] == '\n') - { - /* This has to be done to make the following test - for the end of line possible. We are looking for - the terminating '\n' which do not overwrite here. */ - *cp++ = '\0'; - *cp = '\n'; - } - else if (cp[0] != '\0') - *cp++ = '\0'; - - if (nmap >= maxmap) - if (__builtin_expect (extend_alias_table (), 0)) - return added; - - alias_len = strlen (alias) + 1; - value_len = strlen (value) + 1; - - if (string_space_act + alias_len + value_len > string_space_max) - { - /* Increase size of memory pool. */ - size_t new_size = (string_space_max - + (alias_len + value_len > 1024 - ? alias_len + value_len : 1024)); - char *new_pool = (char *) realloc (string_space, new_size); - if (new_pool == NULL) - return added; - - if (__builtin_expect (string_space != new_pool, 0)) - { - size_t i; - - for (i = 0; i < nmap; i++) - { - map[i].alias += new_pool - string_space; - map[i].value += new_pool - string_space; - } - } - - string_space = new_pool; - string_space_max = new_size; - } - - map[nmap].alias = memcpy (&string_space[string_space_act], - alias, alias_len); - string_space_act += alias_len; - - map[nmap].value = memcpy (&string_space[string_space_act], - value, value_len); - string_space_act += value_len; - - ++nmap; - ++added; - } - } - - /* Possibly not the whole line fits into the buffer. Ignore - the rest of the line. */ - while (strchr (buf, '\n') == NULL) - if (FGETS (buf, sizeof buf, fp) == NULL) - /* Make sure the inner loop will be left. The outer loop - will exit at the `feof' test. */ - break; - } - - /* Should we test for ferror()? I think we have to silently ignore - errors. --drepper */ - fclose (fp); - - if (added > 0) - qsort (map, nmap, sizeof (struct alias_map), - (int (*) PARAMS ((const void *, const void *))) alias_compare); - - return added; -} - - -static int -extend_alias_table () -{ - size_t new_size; - struct alias_map *new_map; - - new_size = maxmap == 0 ? 100 : 2 * maxmap; - new_map = (struct alias_map *) realloc (map, (new_size - * sizeof (struct alias_map))); - if (new_map == NULL) - /* Simply don't extend: we don't have any more core. */ - return -1; - - map = new_map; - maxmap = new_size; - return 0; -} - - -static int -alias_compare (map1, map2) - const struct alias_map *map1; - const struct alias_map *map2; -{ -#if defined _LIBC || defined HAVE_STRCASECMP - return strcasecmp (map1->alias, map2->alias); -#else - const unsigned char *p1 = (const unsigned char *) map1->alias; - const unsigned char *p2 = (const unsigned char *) map2->alias; - unsigned char c1, c2; - - if (p1 == p2) - return 0; - - do - { - /* I know this seems to be odd but the tolower() function in - some systems libc cannot handle nonalpha characters. */ - c1 = isupper (*p1) ? tolower (*p1) : *p1; - c2 = isupper (*p2) ? tolower (*p2) : *p2; - if (c1 == '\0') - break; - ++p1; - ++p2; - } - while (c1 == c2); - - return c1 - c2; -#endif -} diff --git a/src/audacious/intl/localename.c b/src/audacious/intl/localename.c deleted file mode 100644 index f03e656..0000000 --- a/src/audacious/intl/localename.c +++ /dev/null @@ -1,772 +0,0 @@ -/* Determine the current selected locale. - Copyright (C) 1995-1999, 2000-2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Written by Ulrich Drepper <drepper@gnu.org>, 1995. */ -/* Win32 code written by Tor Lillqvist <tml@iki.fi>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stdlib.h> -#include <locale.h> - -#if defined _WIN32 || defined __WIN32__ -# undef WIN32 /* avoid warning on mingw32 */ -# define WIN32 -#endif - -#ifdef WIN32 -# define WIN32_LEAN_AND_MEAN -# include <windows.h> -/* Mingw headers don't have latest language and sublanguage codes. */ -# ifndef LANG_AFRIKAANS -# define LANG_AFRIKAANS 0x36 -# endif -# ifndef LANG_ALBANIAN -# define LANG_ALBANIAN 0x1c -# endif -# ifndef LANG_ARABIC -# define LANG_ARABIC 0x01 -# endif -# ifndef LANG_ARMENIAN -# define LANG_ARMENIAN 0x2b -# endif -# ifndef LANG_ASSAMESE -# define LANG_ASSAMESE 0x4d -# endif -# ifndef LANG_AZERI -# define LANG_AZERI 0x2c -# endif -# ifndef LANG_BASQUE -# define LANG_BASQUE 0x2d -# endif -# ifndef LANG_BELARUSIAN -# define LANG_BELARUSIAN 0x23 -# endif -# ifndef LANG_BENGALI -# define LANG_BENGALI 0x45 -# endif -# ifndef LANG_CATALAN -# define LANG_CATALAN 0x03 -# endif -# ifndef LANG_DIVEHI -# define LANG_DIVEHI 0x65 -# endif -# ifndef LANG_ESTONIAN -# define LANG_ESTONIAN 0x25 -# endif -# ifndef LANG_FAEROESE -# define LANG_FAEROESE 0x38 -# endif -# ifndef LANG_FARSI -# define LANG_FARSI 0x29 -# endif -# ifndef LANG_GALICIAN -# define LANG_GALICIAN 0x56 -# endif -# ifndef LANG_GEORGIAN -# define LANG_GEORGIAN 0x37 -# endif -# ifndef LANG_GUJARATI -# define LANG_GUJARATI 0x47 -# endif -# ifndef LANG_HEBREW -# define LANG_HEBREW 0x0d -# endif -# ifndef LANG_HINDI -# define LANG_HINDI 0x39 -# endif -# ifndef LANG_INDONESIAN -# define LANG_INDONESIAN 0x21 -# endif -# ifndef LANG_KANNADA -# define LANG_KANNADA 0x4b -# endif -# ifndef LANG_KASHMIRI -# define LANG_KASHMIRI 0x60 -# endif -# ifndef LANG_KAZAK -# define LANG_KAZAK 0x3f -# endif -# ifndef LANG_KONKANI -# define LANG_KONKANI 0x57 -# endif -# ifndef LANG_KYRGYZ -# define LANG_KYRGYZ 0x40 -# endif -# ifndef LANG_LATVIAN -# define LANG_LATVIAN 0x26 -# endif -# ifndef LANG_LITHUANIAN -# define LANG_LITHUANIAN 0x27 -# endif -# ifndef LANG_MACEDONIAN -# define LANG_MACEDONIAN 0x2f -# endif -# ifndef LANG_MALAY -# define LANG_MALAY 0x3e -# endif -# ifndef LANG_MALAYALAM -# define LANG_MALAYALAM 0x4c -# endif -# ifndef LANG_MANIPURI -# define LANG_MANIPURI 0x58 -# endif -# ifndef LANG_MARATHI -# define LANG_MARATHI 0x4e -# endif -# ifndef LANG_MONGOLIAN -# define LANG_MONGOLIAN 0x50 -# endif -# ifndef LANG_NEPALI -# define LANG_NEPALI 0x61 -# endif -# ifndef LANG_ORIYA -# define LANG_ORIYA 0x48 -# endif -# ifndef LANG_PUNJABI -# define LANG_PUNJABI 0x46 -# endif -# ifndef LANG_SANSKRIT -# define LANG_SANSKRIT 0x4f -# endif -# ifndef LANG_SERBIAN -# define LANG_SERBIAN 0x1a -# endif -# ifndef LANG_SINDHI -# define LANG_SINDHI 0x59 -# endif -# ifndef LANG_SLOVAK -# define LANG_SLOVAK 0x1b -# endif -# ifndef LANG_SORBIAN -# define LANG_SORBIAN 0x2e -# endif -# ifndef LANG_SWAHILI -# define LANG_SWAHILI 0x41 -# endif -# ifndef LANG_SYRIAC -# define LANG_SYRIAC 0x5a -# endif -# ifndef LANG_TAMIL -# define LANG_TAMIL 0x49 -# endif -# ifndef LANG_TATAR -# define LANG_TATAR 0x44 -# endif -# ifndef LANG_TELUGU -# define LANG_TELUGU 0x4a -# endif -# ifndef LANG_THAI -# define LANG_THAI 0x1e -# endif -# ifndef LANG_UKRAINIAN -# define LANG_UKRAINIAN 0x22 -# endif -# ifndef LANG_URDU -# define LANG_URDU 0x20 -# endif -# ifndef LANG_UZBEK -# define LANG_UZBEK 0x43 -# endif -# ifndef LANG_VIETNAMESE -# define LANG_VIETNAMESE 0x2a -# endif -# ifndef SUBLANG_ARABIC_SAUDI_ARABIA -# define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 -# endif -# ifndef SUBLANG_ARABIC_IRAQ -# define SUBLANG_ARABIC_IRAQ 0x02 -# endif -# ifndef SUBLANG_ARABIC_EGYPT -# define SUBLANG_ARABIC_EGYPT 0x03 -# endif -# ifndef SUBLANG_ARABIC_LIBYA -# define SUBLANG_ARABIC_LIBYA 0x04 -# endif -# ifndef SUBLANG_ARABIC_ALGERIA -# define SUBLANG_ARABIC_ALGERIA 0x05 -# endif -# ifndef SUBLANG_ARABIC_MOROCCO -# define SUBLANG_ARABIC_MOROCCO 0x06 -# endif -# ifndef SUBLANG_ARABIC_TUNISIA -# define SUBLANG_ARABIC_TUNISIA 0x07 -# endif -# ifndef SUBLANG_ARABIC_OMAN -# define SUBLANG_ARABIC_OMAN 0x08 -# endif -# ifndef SUBLANG_ARABIC_YEMEN -# define SUBLANG_ARABIC_YEMEN 0x09 -# endif -# ifndef SUBLANG_ARABIC_SYRIA -# define SUBLANG_ARABIC_SYRIA 0x0a -# endif -# ifndef SUBLANG_ARABIC_JORDAN -# define SUBLANG_ARABIC_JORDAN 0x0b -# endif -# ifndef SUBLANG_ARABIC_LEBANON -# define SUBLANG_ARABIC_LEBANON 0x0c -# endif -# ifndef SUBLANG_ARABIC_KUWAIT -# define SUBLANG_ARABIC_KUWAIT 0x0d -# endif -# ifndef SUBLANG_ARABIC_UAE -# define SUBLANG_ARABIC_UAE 0x0e -# endif -# ifndef SUBLANG_ARABIC_BAHRAIN -# define SUBLANG_ARABIC_BAHRAIN 0x0f -# endif -# ifndef SUBLANG_ARABIC_QATAR -# define SUBLANG_ARABIC_QATAR 0x10 -# endif -# ifndef SUBLANG_AZERI_LATIN -# define SUBLANG_AZERI_LATIN 0x01 -# endif -# ifndef SUBLANG_AZERI_CYRILLIC -# define SUBLANG_AZERI_CYRILLIC 0x02 -# endif -# ifndef SUBLANG_CHINESE_MACAU -# define SUBLANG_CHINESE_MACAU 0x05 -# endif -# ifndef SUBLANG_ENGLISH_SOUTH_AFRICA -# define SUBLANG_ENGLISH_SOUTH_AFRICA 0x07 -# endif -# ifndef SUBLANG_ENGLISH_JAMAICA -# define SUBLANG_ENGLISH_JAMAICA 0x08 -# endif -# ifndef SUBLANG_ENGLISH_CARIBBEAN -# define SUBLANG_ENGLISH_CARIBBEAN 0x09 -# endif -# ifndef SUBLANG_ENGLISH_BELIZE -# define SUBLANG_ENGLISH_BELIZE 0x0a -# endif -# ifndef SUBLANG_ENGLISH_TRINIDAD -# define SUBLANG_ENGLISH_TRINIDAD 0x0b -# endif -# ifndef SUBLANG_ENGLISH_ZIMBABWE -# define SUBLANG_ENGLISH_ZIMBABWE 0x0c -# endif -# ifndef SUBLANG_ENGLISH_PHILIPPINES -# define SUBLANG_ENGLISH_PHILIPPINES 0x0d -# endif -# ifndef SUBLANG_FRENCH_LUXEMBOURG -# define SUBLANG_FRENCH_LUXEMBOURG 0x05 -# endif -# ifndef SUBLANG_FRENCH_MONACO -# define SUBLANG_FRENCH_MONACO 0x06 -# endif -# ifndef SUBLANG_GERMAN_LUXEMBOURG -# define SUBLANG_GERMAN_LUXEMBOURG 0x04 -# endif -# ifndef SUBLANG_GERMAN_LIECHTENSTEIN -# define SUBLANG_GERMAN_LIECHTENSTEIN 0x05 -# endif -# ifndef SUBLANG_KASHMIRI_INDIA -# define SUBLANG_KASHMIRI_INDIA 0x02 -# endif -# ifndef SUBLANG_MALAY_MALAYSIA -# define SUBLANG_MALAY_MALAYSIA 0x01 -# endif -# ifndef SUBLANG_MALAY_BRUNEI_DARUSSALAM -# define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02 -# endif -# ifndef SUBLANG_NEPALI_INDIA -# define SUBLANG_NEPALI_INDIA 0x02 -# endif -# ifndef SUBLANG_SERBIAN_LATIN -# define SUBLANG_SERBIAN_LATIN 0x02 -# endif -# ifndef SUBLANG_SERBIAN_CYRILLIC -# define SUBLANG_SERBIAN_CYRILLIC 0x03 -# endif -# ifndef SUBLANG_SPANISH_GUATEMALA -# define SUBLANG_SPANISH_GUATEMALA 0x04 -# endif -# ifndef SUBLANG_SPANISH_COSTA_RICA -# define SUBLANG_SPANISH_COSTA_RICA 0x05 -# endif -# ifndef SUBLANG_SPANISH_PANAMA -# define SUBLANG_SPANISH_PANAMA 0x06 -# endif -# ifndef SUBLANG_SPANISH_DOMINICAN_REPUBLIC -# define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07 -# endif -# ifndef SUBLANG_SPANISH_VENEZUELA -# define SUBLANG_SPANISH_VENEZUELA 0x08 -# endif -# ifndef SUBLANG_SPANISH_COLOMBIA -# define SUBLANG_SPANISH_COLOMBIA 0x09 -# endif -# ifndef SUBLANG_SPANISH_PERU -# define SUBLANG_SPANISH_PERU 0x0a -# endif -# ifndef SUBLANG_SPANISH_ARGENTINA -# define SUBLANG_SPANISH_ARGENTINA 0x0b -# endif -# ifndef SUBLANG_SPANISH_ECUADOR -# define SUBLANG_SPANISH_ECUADOR 0x0c -# endif -# ifndef SUBLANG_SPANISH_CHILE -# define SUBLANG_SPANISH_CHILE 0x0d -# endif -# ifndef SUBLANG_SPANISH_URUGUAY -# define SUBLANG_SPANISH_URUGUAY 0x0e -# endif -# ifndef SUBLANG_SPANISH_PARAGUAY -# define SUBLANG_SPANISH_PARAGUAY 0x0f -# endif -# ifndef SUBLANG_SPANISH_BOLIVIA -# define SUBLANG_SPANISH_BOLIVIA 0x10 -# endif -# ifndef SUBLANG_SPANISH_EL_SALVADOR -# define SUBLANG_SPANISH_EL_SALVADOR 0x11 -# endif -# ifndef SUBLANG_SPANISH_HONDURAS -# define SUBLANG_SPANISH_HONDURAS 0x12 -# endif -# ifndef SUBLANG_SPANISH_NICARAGUA -# define SUBLANG_SPANISH_NICARAGUA 0x13 -# endif -# ifndef SUBLANG_SPANISH_PUERTO_RICO -# define SUBLANG_SPANISH_PUERTO_RICO 0x14 -# endif -# ifndef SUBLANG_SWEDISH_FINLAND -# define SUBLANG_SWEDISH_FINLAND 0x02 -# endif -# ifndef SUBLANG_URDU_PAKISTAN -# define SUBLANG_URDU_PAKISTAN 0x01 -# endif -# ifndef SUBLANG_URDU_INDIA -# define SUBLANG_URDU_INDIA 0x02 -# endif -# ifndef SUBLANG_UZBEK_LATIN -# define SUBLANG_UZBEK_LATIN 0x01 -# endif -# ifndef SUBLANG_UZBEK_CYRILLIC -# define SUBLANG_UZBEK_CYRILLIC 0x02 -# endif -#endif - -/* XPG3 defines the result of 'setlocale (category, NULL)' as: - "Directs 'setlocale()' to query 'category' and return the current - setting of 'local'." - However it does not specify the exact format. Neither do SUSV2 and - ISO C 99. So we can use this feature only on selected systems (e.g. - those using GNU C Library). */ -#if defined _LIBC || (defined __GNU_LIBRARY__ && __GNU_LIBRARY__ >= 2) -# define HAVE_LOCALE_NULL -#endif - -/* Determine the current locale's name, and canonicalize it into XPG syntax - language[_territory[.codeset]][@modifier] - The codeset part in the result is not reliable; the locale_charset() - should be used for codeset information instead. - The result must not be freed; it is statically allocated. */ - -const char * -_nl_locale_name (category, categoryname) - int category; - const char *categoryname; -{ - const char *retval; - -#ifndef WIN32 - - /* Use the POSIX methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'. - On some systems this can be done by the 'setlocale' function itself. */ -# if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL - retval = setlocale (category, NULL); -# else - /* Setting of LC_ALL overwrites all other. */ - retval = getenv ("LC_ALL"); - if (retval == NULL || retval[0] == '\0') - { - /* Next comes the name of the desired category. */ - retval = getenv (categoryname); - if (retval == NULL || retval[0] == '\0') - { - /* Last possibility is the LANG environment variable. */ - retval = getenv ("LANG"); - if (retval == NULL || retval[0] == '\0') - /* We use C as the default domain. POSIX says this is - implementation defined. */ - retval = "C"; - } - } -# endif - - return retval; - -#else /* WIN32 */ - - /* Return an XPG style locale name language[_territory][@modifier]. - Don't even bother determining the codeset; it's not useful in this - context, because message catalogs are not specific to a single - codeset. */ - - LCID lcid; - LANGID langid; - int primary, sub; - - /* Let the user override the system settings through environment - variables, as on POSIX systems. */ - retval = getenv ("LC_ALL"); - if (retval != NULL && retval[0] != '\0') - return retval; - retval = getenv (categoryname); - if (retval != NULL && retval[0] != '\0') - return retval; - retval = getenv ("LANG"); - if (retval != NULL && retval[0] != '\0') - return retval; - - /* Use native Win32 API locale ID. */ - lcid = GetThreadLocale (); - - /* Strip off the sorting rules, keep only the language part. */ - langid = LANGIDFROMLCID (lcid); - - /* Split into language and territory part. */ - primary = PRIMARYLANGID (langid); - sub = SUBLANGID (langid); - - /* Dispatch on language. - See also http://www.unicode.org/unicode/onlinedat/languages.html . - For details about languages, see http://www.ethnologue.com/ . */ - switch (primary) - { - case LANG_AFRIKAANS: return "af_ZA"; - case LANG_ALBANIAN: return "sq_AL"; - case 0x5e: /* AMHARIC */ return "am_ET"; - case LANG_ARABIC: - switch (sub) - { - case SUBLANG_ARABIC_SAUDI_ARABIA: return "ar_SA"; - case SUBLANG_ARABIC_IRAQ: return "ar_IQ"; - case SUBLANG_ARABIC_EGYPT: return "ar_EG"; - case SUBLANG_ARABIC_LIBYA: return "ar_LY"; - case SUBLANG_ARABIC_ALGERIA: return "ar_DZ"; - case SUBLANG_ARABIC_MOROCCO: return "ar_MA"; - case SUBLANG_ARABIC_TUNISIA: return "ar_TN"; - case SUBLANG_ARABIC_OMAN: return "ar_OM"; - case SUBLANG_ARABIC_YEMEN: return "ar_YE"; - case SUBLANG_ARABIC_SYRIA: return "ar_SY"; - case SUBLANG_ARABIC_JORDAN: return "ar_JO"; - case SUBLANG_ARABIC_LEBANON: return "ar_LB"; - case SUBLANG_ARABIC_KUWAIT: return "ar_KW"; - case SUBLANG_ARABIC_UAE: return "ar_AE"; - case SUBLANG_ARABIC_BAHRAIN: return "ar_BH"; - case SUBLANG_ARABIC_QATAR: return "ar_QA"; - } - return "ar"; - case LANG_ARMENIAN: return "hy_AM"; - case LANG_ASSAMESE: return "as_IN"; - case LANG_AZERI: - switch (sub) - { - /* FIXME: Adjust this when Azerbaijani locales appear on Unix. */ - case SUBLANG_AZERI_LATIN: return "az_AZ@latin"; - case SUBLANG_AZERI_CYRILLIC: return "az_AZ@cyrillic"; - } - return "az"; - case LANG_BASQUE: - return "eu"; /* Ambiguous: could be "eu_ES" or "eu_FR". */ - case LANG_BELARUSIAN: return "be_BY"; - case LANG_BENGALI: return "bn_IN"; - case LANG_BULGARIAN: return "bg_BG"; - case 0x55: /* BURMESE */ return "my_MM"; - case 0x53: /* CAMBODIAN */ return "km_KH"; - case LANG_CATALAN: return "ca_ES"; - case 0x5c: /* CHEROKEE */ return "chr_US"; - case LANG_CHINESE: - switch (sub) - { - case SUBLANG_CHINESE_TRADITIONAL: return "zh_TW"; - case SUBLANG_CHINESE_SIMPLIFIED: return "zh_CN"; - case SUBLANG_CHINESE_HONGKONG: return "zh_HK"; - case SUBLANG_CHINESE_SINGAPORE: return "zh_SG"; - case SUBLANG_CHINESE_MACAU: return "zh_MO"; - } - return "zh"; - case LANG_CROATIAN: /* LANG_CROATIAN == LANG_SERBIAN - * What used to be called Serbo-Croatian - * should really now be two separate - * languages because of political reasons. - * (Says tml, who knows nothing about Serbian - * or Croatian.) - * (I can feel those flames coming already.) - */ - switch (sub) - { - case SUBLANG_DEFAULT: return "hr_HR"; - case SUBLANG_SERBIAN_LATIN: return "sr_YU"; - case SUBLANG_SERBIAN_CYRILLIC: return "sr_YU@cyrillic"; - } - return "hr"; - case LANG_CZECH: return "cs_CZ"; - case LANG_DANISH: return "da_DK"; - case LANG_DIVEHI: return "div_MV"; - case LANG_DUTCH: - switch (sub) - { - case SUBLANG_DUTCH: return "nl_NL"; - case SUBLANG_DUTCH_BELGIAN: /* FLEMISH, VLAAMS */ return "nl_BE"; - } - return "nl"; - case 0x66: /* EDO */ return "bin_NG"; - case LANG_ENGLISH: - switch (sub) - { - /* SUBLANG_ENGLISH_US == SUBLANG_DEFAULT. Heh. I thought - * English was the language spoken in England. - * Oh well. - */ - case SUBLANG_ENGLISH_US: return "en_US"; - case SUBLANG_ENGLISH_UK: return "en_GB"; - case SUBLANG_ENGLISH_AUS: return "en_AU"; - case SUBLANG_ENGLISH_CAN: return "en_CA"; - case SUBLANG_ENGLISH_NZ: return "en_NZ"; - case SUBLANG_ENGLISH_EIRE: return "en_IE"; - case SUBLANG_ENGLISH_SOUTH_AFRICA: return "en_ZA"; - case SUBLANG_ENGLISH_JAMAICA: return "en_JM"; - case SUBLANG_ENGLISH_CARIBBEAN: return "en_GD"; /* Grenada? */ - case SUBLANG_ENGLISH_BELIZE: return "en_BZ"; - case SUBLANG_ENGLISH_TRINIDAD: return "en_TT"; - case SUBLANG_ENGLISH_ZIMBABWE: return "en_ZW"; - case SUBLANG_ENGLISH_PHILIPPINES: return "en_PH"; - } - return "en"; - case LANG_ESTONIAN: return "et_EE"; - case LANG_FAEROESE: return "fo_FO"; - case LANG_FARSI: return "fa_IR"; - case LANG_FINNISH: return "fi_FI"; - case LANG_FRENCH: - switch (sub) - { - case SUBLANG_FRENCH: return "fr_FR"; - case SUBLANG_FRENCH_BELGIAN: /* WALLOON */ return "fr_BE"; - case SUBLANG_FRENCH_CANADIAN: return "fr_CA"; - case SUBLANG_FRENCH_SWISS: return "fr_CH"; - case SUBLANG_FRENCH_LUXEMBOURG: return "fr_LU"; - case SUBLANG_FRENCH_MONACO: return "fr_MC"; - } - return "fr"; - case 0x62: /* FRISIAN */ return "fy_NL"; - case 0x67: /* FULFULDE */ return "ful_NG"; - case 0x3c: /* GAELIC */ - switch (sub) - { - case 0x01: /* SCOTTISH */ return "gd_GB"; - case 0x02: /* IRISH */ return "ga_IE"; - } - return "C"; - case LANG_GALICIAN: return "gl_ES"; - case LANG_GEORGIAN: return "ka_GE"; - case LANG_GERMAN: - switch (sub) - { - case SUBLANG_GERMAN: return "de_DE"; - case SUBLANG_GERMAN_SWISS: return "de_CH"; - case SUBLANG_GERMAN_AUSTRIAN: return "de_AT"; - case SUBLANG_GERMAN_LUXEMBOURG: return "de_LU"; - case SUBLANG_GERMAN_LIECHTENSTEIN: return "de_LI"; - } - return "de"; - case LANG_GREEK: return "el_GR"; - case 0x74: /* GUARANI */ return "gn_PY"; - case LANG_GUJARATI: return "gu_IN"; - case 0x68: /* HAUSA */ return "ha_NG"; - case 0x75: /* HAWAIIAN */ - /* FIXME: Do they mean Hawaiian ("haw_US", 1000 speakers) - or Hawaii Creole English ("cpe_US", 600000 speakers)? */ - return "cpe_US"; - case LANG_HEBREW: return "he_IL"; - case LANG_HINDI: return "hi_IN"; - case LANG_HUNGARIAN: return "hu_HU"; - case 0x69: /* IBIBIO */ return "nic_NG"; - case LANG_ICELANDIC: return "is_IS"; - case 0x70: /* IGBO */ return "ibo_NG"; - case LANG_INDONESIAN: return "id_ID"; - case 0x5d: /* INUKTITUT */ return "iu_CA"; - case LANG_ITALIAN: - switch (sub) - { - case SUBLANG_ITALIAN: return "it_IT"; - case SUBLANG_ITALIAN_SWISS: return "it_CH"; - } - return "it"; - case LANG_JAPANESE: return "ja_JP"; - case LANG_KANNADA: return "kn_IN"; - case 0x71: /* KANURI */ return "kau_NG"; - case LANG_KASHMIRI: - switch (sub) - { - case SUBLANG_DEFAULT: return "ks_PK"; - case SUBLANG_KASHMIRI_INDIA: return "ks_IN"; - } - return "ks"; - case LANG_KAZAK: return "kk_KZ"; - case LANG_KONKANI: - /* FIXME: Adjust this when such locales appear on Unix. */ - return "kok_IN"; - case LANG_KOREAN: return "ko_KR"; - case LANG_KYRGYZ: return "ky_KG"; - case 0x54: /* LAO */ return "lo_LA"; - case 0x76: /* LATIN */ return "la_VA"; - case LANG_LATVIAN: return "lv_LV"; - case LANG_LITHUANIAN: return "lt_LT"; - case LANG_MACEDONIAN: return "mk_MK"; - case LANG_MALAY: - switch (sub) - { - case SUBLANG_MALAY_MALAYSIA: return "ms_MY"; - case SUBLANG_MALAY_BRUNEI_DARUSSALAM: return "ms_BN"; - } - return "ms"; - case LANG_MALAYALAM: return "ml_IN"; - case 0x3a: /* MALTESE */ return "mt_MT"; - case LANG_MANIPURI: - /* FIXME: Adjust this when such locales appear on Unix. */ - return "mni_IN"; - case LANG_MARATHI: return "mr_IN"; - case LANG_MONGOLIAN: - return "mn"; /* Ambiguous: could be "mn_CN" or "mn_MN". */ - case LANG_NEPALI: - switch (sub) - { - case SUBLANG_DEFAULT: return "ne_NP"; - case SUBLANG_NEPALI_INDIA: return "ne_IN"; - } - return "ne"; - case LANG_NORWEGIAN: - switch (sub) - { - case SUBLANG_NORWEGIAN_BOKMAL: return "no_NO"; - case SUBLANG_NORWEGIAN_NYNORSK: return "nn_NO"; - } - return "no"; - case LANG_ORIYA: return "or_IN"; - case 0x72: /* OROMO */ return "om_ET"; - case 0x79: /* PAPIAMENTU */ return "pap_AN"; - case 0x63: /* PASHTO */ - return "ps"; /* Ambiguous: could be "ps_PK" or "ps_AF". */ - case LANG_POLISH: return "pl_PL"; - case LANG_PORTUGUESE: - switch (sub) - { - case SUBLANG_PORTUGUESE: return "pt_PT"; - /* Hmm. SUBLANG_PORTUGUESE_BRAZILIAN == SUBLANG_DEFAULT. - Same phenomenon as SUBLANG_ENGLISH_US == SUBLANG_DEFAULT. */ - case SUBLANG_PORTUGUESE_BRAZILIAN: return "pt_BR"; - } - return "pt"; - case LANG_PUNJABI: return "pa_IN"; - case 0x17: /* RHAETO-ROMANCE */ return "rm_CH"; - case LANG_ROMANIAN: return "ro_RO"; - case LANG_RUSSIAN: - return "ru"; /* Ambiguous: could be "ru_RU" or "ru_UA". */ - case 0x3b: /* SAMI */ return "se_NO"; - case LANG_SANSKRIT: return "sa_IN"; - case LANG_SINDHI: return "sd"; - case 0x5b: /* SINHALESE */ return "si_LK"; - case LANG_SLOVAK: return "sk_SK"; - case LANG_SLOVENIAN: return "sl_SI"; - case 0x77: /* SOMALI */ return "so_SO"; - case LANG_SORBIAN: - /* FIXME: Adjust this when such locales appear on Unix. */ - return "wen_DE"; - case LANG_SPANISH: - switch (sub) - { - case SUBLANG_SPANISH: return "es_ES"; - case SUBLANG_SPANISH_MEXICAN: return "es_MX"; - case SUBLANG_SPANISH_MODERN: - return "es_ES@modern"; /* not seen on Unix */ - case SUBLANG_SPANISH_GUATEMALA: return "es_GT"; - case SUBLANG_SPANISH_COSTA_RICA: return "es_CR"; - case SUBLANG_SPANISH_PANAMA: return "es_PA"; - case SUBLANG_SPANISH_DOMINICAN_REPUBLIC: return "es_DO"; - case SUBLANG_SPANISH_VENEZUELA: return "es_VE"; - case SUBLANG_SPANISH_COLOMBIA: return "es_CO"; - case SUBLANG_SPANISH_PERU: return "es_PE"; - case SUBLANG_SPANISH_ARGENTINA: return "es_AR"; - case SUBLANG_SPANISH_ECUADOR: return "es_EC"; - case SUBLANG_SPANISH_CHILE: return "es_CL"; - case SUBLANG_SPANISH_URUGUAY: return "es_UY"; - case SUBLANG_SPANISH_PARAGUAY: return "es_PY"; - case SUBLANG_SPANISH_BOLIVIA: return "es_BO"; - case SUBLANG_SPANISH_EL_SALVADOR: return "es_SV"; - case SUBLANG_SPANISH_HONDURAS: return "es_HN"; - case SUBLANG_SPANISH_NICARAGUA: return "es_NI"; - case SUBLANG_SPANISH_PUERTO_RICO: return "es_PR"; - } - return "es"; - case 0x30: /* SUTU */ return "bnt_TZ"; - case LANG_SWAHILI: return "sw_KE"; - case LANG_SWEDISH: - switch (sub) - { - case SUBLANG_DEFAULT: return "sv_SE"; - case SUBLANG_SWEDISH_FINLAND: return "sv_FI"; - } - return "sv"; - case LANG_SYRIAC: return "syr_TR"; /* An extinct language. */ - case 0x64: /* TAGALOG */ return "tl_PH"; - case 0x28: /* TAJIK */ return "tg_TJ"; - case 0x5f: /* TAMAZIGHT */ return "ber_MA"; - case LANG_TAMIL: - return "ta"; /* Ambiguous: could be "ta_IN" or "ta_LK" or "ta_SG". */ - case LANG_TATAR: return "tt_RU"; - case LANG_TELUGU: return "te_IN"; - case LANG_THAI: return "th_TH"; - case 0x51: /* TIBETAN */ return "bo_CN"; - case 0x73: /* TIGRINYA */ return "ti_ET"; - case 0x31: /* TSONGA */ return "ts_ZA"; - case LANG_TURKISH: return "tr_TR"; - case 0x42: /* TURKMEN */ return "tk_TM"; - case LANG_UKRAINIAN: return "uk_UA"; - case LANG_URDU: - switch (sub) - { - case SUBLANG_URDU_PAKISTAN: return "ur_PK"; - case SUBLANG_URDU_INDIA: return "ur_IN"; - } - return "ur"; - case LANG_UZBEK: - switch (sub) - { - /* FIXME: Adjust this when Uzbek locales appear on Unix. */ - case SUBLANG_UZBEK_LATIN: return "uz_UZ@latin"; - case SUBLANG_UZBEK_CYRILLIC: return "uz_UZ@cyrillic"; - } - return "uz"; - case 0x33: /* VENDA */ return "ven_ZA"; - case LANG_VIETNAMESE: return "vi_VN"; - case 0x52: /* WELSH */ return "cy_GB"; - case 0x34: /* XHOSA */ return "xh_ZA"; - case 0x78: /* YI */ return "sit_CN"; - case 0x3d: /* YIDDISH */ return "yi_IL"; - case 0x6a: /* YORUBA */ return "yo_NG"; - case 0x35: /* ZULU */ return "zu_ZA"; - default: return "C"; - } - -#endif -} diff --git a/src/audacious/intl/log.c b/src/audacious/intl/log.c deleted file mode 100644 index 9067401..0000000 --- a/src/audacious/intl/log.c +++ /dev/null @@ -1,104 +0,0 @@ -/* Log file output. - Copyright (C) 2003 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Written by Bruno Haible <bruno@clisp.org>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -/* Print an ASCII string with quotes and escape sequences where needed. */ -static void -print_escaped (stream, str) - FILE *stream; - const char *str; -{ - putc ('"', stream); - for (; *str != '\0'; str++) - if (*str == '\n') - { - fputs ("\\n\"", stream); - if (str[1] == '\0') - return; - fputs ("\n\"", stream); - } - else - { - if (*str == '"' || *str == '\\') - putc ('\\', stream); - putc (*str, stream); - } - putc ('"', stream); -} - -/* Add to the log file an entry denoting a failed translation. */ -void -_nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural) - const char *logfilename; - const char *domainname; - const char *msgid1; - const char *msgid2; - int plural; -{ - static char *last_logfilename = NULL; - static FILE *last_logfile = NULL; - FILE *logfile; - - /* Can we reuse the last opened logfile? */ - if (last_logfilename == NULL || strcmp (logfilename, last_logfilename) != 0) - { - /* Close the last used logfile. */ - if (last_logfilename != NULL) - { - if (last_logfile != NULL) - { - fclose (last_logfile); - last_logfile = NULL; - } - free (last_logfilename); - last_logfilename = NULL; - } - /* Open the logfile. */ - last_logfilename = (char *) malloc (strlen (logfilename) + 1); - if (last_logfilename == NULL) - return; - g_strlcpy (last_logfilename, logfilename, strlen(logfilename) + 1); - last_logfile = fopen (logfilename, "a"); - if (last_logfile == NULL) - return; - } - logfile = last_logfile; - - fprintf (logfile, "domain "); - print_escaped (logfile, domainname); - fprintf (logfile, "\nmsgid "); - print_escaped (logfile, msgid1); - if (plural) - { - fprintf (logfile, "\nmsgid_plural "); - print_escaped (logfile, msgid2); - fprintf (logfile, "\nmsgstr[0] \"\"\n"); - } - else - fprintf (logfile, "\nmsgstr \"\"\n"); - putc ('\n', logfile); -} diff --git a/src/audacious/intl/ngettext.c b/src/audacious/intl/ngettext.c deleted file mode 100644 index 92bebce..0000000 --- a/src/audacious/intl/ngettext.c +++ /dev/null @@ -1,68 +0,0 @@ -/* Implementation of ngettext(3) function. - Copyright (C) 1995, 1997, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#ifdef _LIBC -# define __need_NULL -# include <stddef.h> -#else -# include <stdlib.h> /* Just for NULL. */ -#endif - -#include "gettextP.h" -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif - -#include <locale.h> - -/* @@ end of prolog @@ */ - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define NGETTEXT __ngettext -# define DCNGETTEXT __dcngettext -#else -# define NGETTEXT libintl_ngettext -# define DCNGETTEXT libintl_dcngettext -#endif - -/* Look up MSGID in the current default message catalog for the current - LC_MESSAGES locale. If not found, returns MSGID itself (the default - text). */ -char * -NGETTEXT (msgid1, msgid2, n) - const char *msgid1; - const char *msgid2; - unsigned long int n; -{ - return DCNGETTEXT (NULL, msgid1, msgid2, n, LC_MESSAGES); -} - -#ifdef _LIBC -/* Alias for function name in GNU C Library. */ -weak_alias (__ngettext, ngettext); -#endif diff --git a/src/audacious/intl/os2compat.c b/src/audacious/intl/os2compat.c deleted file mode 100644 index 32423e2..0000000 --- a/src/audacious/intl/os2compat.c +++ /dev/null @@ -1,98 +0,0 @@ -/* OS/2 compatibility functions. - Copyright (C) 2001-2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#define OS2_AWARE -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdlib.h> -#include <string.h> -#include <sys/param.h> - -/* A version of getenv() that works from DLLs */ -extern unsigned long DosScanEnv (const unsigned char *pszName, unsigned char **ppszValue); - -char * -_nl_getenv (const char *name) -{ - unsigned char *value; - if (DosScanEnv (name, &value)) - return NULL; - else - return value; -} - -/* A fixed size buffer. */ -char libintl_nl_default_dirname[MAXPATHLEN+1]; - -char *_nlos2_libdir = NULL; -char *_nlos2_localealiaspath = NULL; -char *_nlos2_localedir = NULL; - -static __attribute__((constructor)) void -nlos2_initialize () -{ - char *root = getenv ("UNIXROOT"); - char *gnulocaledir = getenv ("GNULOCALEDIR"); - - _nlos2_libdir = gnulocaledir; - if (!_nlos2_libdir) - { - if (root) - { - size_t sl = strlen (root); - _nlos2_libdir = (char *) malloc (sl + strlen (LIBDIR) + 1); - memcpy (_nlos2_libdir, root, sl); - memcpy (_nlos2_libdir + sl, LIBDIR, strlen (LIBDIR) + 1); - } - else - _nlos2_libdir = LIBDIR; - } - - _nlos2_localealiaspath = gnulocaledir; - if (!_nlos2_localealiaspath) - { - if (root) - { - size_t sl = strlen (root); - _nlos2_localealiaspath = (char *) malloc (sl + strlen (LOCALE_ALIAS_PATH) + 1); - memcpy (_nlos2_localealiaspath, root, sl); - memcpy (_nlos2_localealiaspath + sl, LOCALE_ALIAS_PATH, strlen (LOCALE_ALIAS_PATH) + 1); - } - else - _nlos2_localealiaspath = LOCALE_ALIAS_PATH; - } - - _nlos2_localedir = gnulocaledir; - if (!_nlos2_localedir) - { - if (root) - { - size_t sl = strlen (root); - _nlos2_localedir = (char *) malloc (sl + strlen (LOCALEDIR) + 1); - memcpy (_nlos2_localedir, root, sl); - memcpy (_nlos2_localedir + sl, LOCALEDIR, strlen (LOCALEDIR) + 1); - } - else - _nlos2_localedir = LOCALEDIR; - } - - if (strlen (_nlos2_localedir) <= MAXPATHLEN) - g_strlcpy (libintl_nl_default_dirname, _nlos2_localedir, MAXPATHLEN + 1); -} diff --git a/src/audacious/intl/os2compat.h b/src/audacious/intl/os2compat.h deleted file mode 100644 index a18d582..0000000 --- a/src/audacious/intl/os2compat.h +++ /dev/null @@ -1,46 +0,0 @@ -/* OS/2 compatibility defines. - This file is intended to be included from config.h - Copyright (C) 2001-2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* When included from os2compat.h we need all the original definitions */ -#ifndef OS2_AWARE - -#undef LIBDIR -#define LIBDIR _nlos2_libdir -extern char *_nlos2_libdir; - -#undef LOCALEDIR -#define LOCALEDIR _nlos2_localedir -extern char *_nlos2_localedir; - -#undef LOCALE_ALIAS_PATH -#define LOCALE_ALIAS_PATH _nlos2_localealiaspath -extern char *_nlos2_localealiaspath; - -#endif - -#undef HAVE_STRCASECMP -#define HAVE_STRCASECMP 1 -#define strcasecmp stricmp -#define strncasecmp strnicmp - -/* We have our own getenv() which works even if library is compiled as DLL */ -#define getenv _nl_getenv - -/* Older versions of gettext used -1 as the value of LC_MESSAGES */ -#define LC_MESSAGES_COMPAT (-1) diff --git a/src/audacious/intl/osdep.c b/src/audacious/intl/osdep.c deleted file mode 100644 index d2d8575..0000000 --- a/src/audacious/intl/osdep.c +++ /dev/null @@ -1,24 +0,0 @@ -/* OS dependent parts of libintl. - Copyright (C) 2001-2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#if defined __EMX__ -# include "os2compat.c" -#else -/* Avoid AIX compiler warning. */ -typedef int dummy; -#endif diff --git a/src/audacious/intl/plural-exp.c b/src/audacious/intl/plural-exp.c deleted file mode 100644 index 0660896..0000000 --- a/src/audacious/intl/plural-exp.c +++ /dev/null @@ -1,156 +0,0 @@ -/* Expression parsing for plural form selection. - Copyright (C) 2000, 2001 Free Software Foundation, Inc. - Written by Ulrich Drepper <drepper@cygnus.com>, 2000. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "plural-exp.h" - -#if (defined __GNUC__ && !defined __APPLE_CC__) \ - || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) - -/* These structs are the constant expression for the germanic plural - form determination. It represents the expression "n != 1". */ -static const struct expression plvar = -{ - .nargs = 0, - .operation = var, -}; -static const struct expression plone = -{ - .nargs = 0, - .operation = num, - .val = - { - .num = 1 - } -}; -struct expression GERMANIC_PLURAL = -{ - .nargs = 2, - .operation = not_equal, - .val = - { - .args = - { - [0] = (struct expression *) &plvar, - [1] = (struct expression *) &plone - } - } -}; - -# define INIT_GERMANIC_PLURAL() - -#else - -/* For compilers without support for ISO C 99 struct/union initializers: - Initialization at run-time. */ - -static struct expression plvar; -static struct expression plone; -struct expression GERMANIC_PLURAL; - -static void -init_germanic_plural () -{ - if (plone.val.num == 0) - { - plvar.nargs = 0; - plvar.operation = var; - - plone.nargs = 0; - plone.operation = num; - plone.val.num = 1; - - GERMANIC_PLURAL.nargs = 2; - GERMANIC_PLURAL.operation = not_equal; - GERMANIC_PLURAL.val.args[0] = &plvar; - GERMANIC_PLURAL.val.args[1] = &plone; - } -} - -# define INIT_GERMANIC_PLURAL() init_germanic_plural () - -#endif - -void -internal_function -EXTRACT_PLURAL_EXPRESSION (nullentry, pluralp, npluralsp) - const char *nullentry; - struct expression **pluralp; - unsigned long int *npluralsp; -{ - if (nullentry != NULL) - { - const char *plural; - const char *nplurals; - - plural = strstr (nullentry, "plural="); - nplurals = strstr (nullentry, "nplurals="); - if (plural == NULL || nplurals == NULL) - goto no_plural; - else - { - char *endp; - unsigned long int n; - struct parse_args args; - - /* First get the number. */ - nplurals += 9; - while (*nplurals != '\0' && isspace ((unsigned char) *nplurals)) - ++nplurals; - if (!(*nplurals >= '0' && *nplurals <= '9')) - goto no_plural; -#if defined HAVE_STRTOUL || defined _LIBC - n = strtoul (nplurals, &endp, 10); -#else - for (endp = nplurals, n = 0; *endp >= '0' && *endp <= '9'; endp++) - n = n * 10 + (*endp - '0'); -#endif - if (nplurals == endp) - goto no_plural; - *npluralsp = n; - - /* Due to the restrictions bison imposes onto the interface of the - scanner function we have to put the input string and the result - passed up from the parser into the same structure which address - is passed down to the parser. */ - plural += 7; - args.cp = plural; - if (PLURAL_PARSE (&args) != 0) - goto no_plural; - *pluralp = args.res; - } - } - else - { - /* By default we are using the Germanic form: singular form only - for `one', the plural form otherwise. Yes, this is also what - English is using since English is a Germanic language. */ - no_plural: - INIT_GERMANIC_PLURAL (); - *pluralp = &GERMANIC_PLURAL; - *npluralsp = 2; - } -} diff --git a/src/audacious/intl/plural-exp.h b/src/audacious/intl/plural-exp.h deleted file mode 100644 index d096f32..0000000 --- a/src/audacious/intl/plural-exp.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Expression parsing and evaluation for plural form selection. - Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. - Written by Ulrich Drepper <drepper@cygnus.com>, 2000. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef _PLURAL_EXP_H -#define _PLURAL_EXP_H - -#ifndef PARAMS -# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES -# define PARAMS(args) args -# else -# define PARAMS(args) () -# endif -#endif - -#ifndef internal_function -# define internal_function -#endif - -#ifndef attribute_hidden -# define attribute_hidden -#endif - - -/* This is the representation of the expressions to determine the - plural form. */ -struct expression -{ - int nargs; /* Number of arguments. */ - enum operator - { - /* Without arguments: */ - var, /* The variable "n". */ - num, /* Decimal number. */ - /* Unary operators: */ - lnot, /* Logical NOT. */ - /* Binary operators: */ - mult, /* Multiplication. */ - divide, /* Division. */ - module, /* Modulo operation. */ - plus, /* Addition. */ - minus, /* Subtraction. */ - less_than, /* Comparison. */ - greater_than, /* Comparison. */ - less_or_equal, /* Comparison. */ - greater_or_equal, /* Comparison. */ - equal, /* Comparison for equality. */ - not_equal, /* Comparison for inequality. */ - land, /* Logical AND. */ - lor, /* Logical OR. */ - /* Ternary operators: */ - qmop /* Question mark operator. */ - } operation; - union - { - unsigned long int num; /* Number value for `num'. */ - struct expression *args[3]; /* Up to three arguments. */ - } val; -}; - -/* This is the data structure to pass information to the parser and get - the result in a thread-safe way. */ -struct parse_args -{ - const char *cp; - struct expression *res; -}; - - -/* Names for the libintl functions are a problem. This source code is used - 1. in the GNU C Library library, - 2. in the GNU libintl library, - 3. in the GNU gettext tools. - The function names in each situation must be different, to allow for - binary incompatible changes in 'struct expression'. Furthermore, - 1. in the GNU C Library library, the names have a __ prefix, - 2.+3. in the GNU libintl library and in the GNU gettext tools, the names - must follow ANSI C and not start with __. - So we have to distinguish the three cases. */ -#ifdef _LIBC -# define FREE_EXPRESSION __gettext_free_exp -# define PLURAL_PARSE __gettextparse -# define GERMANIC_PLURAL __gettext_germanic_plural -# define EXTRACT_PLURAL_EXPRESSION __gettext_extract_plural -#elif defined (IN_LIBINTL) -# define FREE_EXPRESSION libintl_gettext_free_exp -# define PLURAL_PARSE libintl_gettextparse -# define GERMANIC_PLURAL libintl_gettext_germanic_plural -# define EXTRACT_PLURAL_EXPRESSION libintl_gettext_extract_plural -#else -# define FREE_EXPRESSION free_plural_expression -# define PLURAL_PARSE parse_plural_expression -# define GERMANIC_PLURAL germanic_plural -# define EXTRACT_PLURAL_EXPRESSION extract_plural_expression -#endif - -extern void FREE_EXPRESSION PARAMS ((struct expression *exp)) - internal_function; -extern int PLURAL_PARSE PARAMS ((void *arg)); -extern struct expression GERMANIC_PLURAL attribute_hidden; -extern void EXTRACT_PLURAL_EXPRESSION PARAMS ((const char *nullentry, - struct expression **pluralp, - unsigned long int *npluralsp)) - internal_function; - -#if !defined (_LIBC) && !defined (IN_LIBINTL) -extern unsigned long int plural_eval PARAMS ((struct expression *pexp, - unsigned long int n)); -#endif - -#endif /* _PLURAL_EXP_H */ diff --git a/src/audacious/intl/plural.c b/src/audacious/intl/plural.c deleted file mode 100644 index d42bfb4..0000000 --- a/src/audacious/intl/plural.c +++ /dev/null @@ -1,1518 +0,0 @@ -/* A Bison parser, made from plural.y - by GNU bison 1.35. */ - -#define YYBISON 1 /* Identify Bison output. */ - -#define yyparse __gettextparse -#define yylex __gettextlex -#define yyerror __gettexterror -#define yylval __gettextlval -#define yychar __gettextchar -#define yydebug __gettextdebug -#define yynerrs __gettextnerrs -# define EQUOP2 257 -# define CMPOP2 258 -# define ADDOP2 259 -# define MULOP2 260 -# define NUMBER 261 - -#line 1 "plural.y" - -/* Expression parsing for plural form selection. - Copyright (C) 2000, 2001 Free Software Foundation, Inc. - Written by Ulrich Drepper <drepper@cygnus.com>, 2000. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* The bison generated parser uses alloca. AIX 3 forces us to put this - declaration at the beginning of the file. The declaration in bison's - skeleton file comes too late. This must come before <config.h> - because <config.h> may include arbitrary system headers. */ -#if defined _AIX && !defined __GNUC__ - #pragma alloca -#endif - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stddef.h> -#include <stdlib.h> -#include "plural-exp.h" - -/* The main function generated by the parser is called __gettextparse, - but we want it to be called PLURAL_PARSE. */ -#ifndef _LIBC -# define __gettextparse PLURAL_PARSE -#endif - -#define YYLEX_PARAM &((struct parse_args *) arg)->cp -#define YYPARSE_PARAM arg - -#line 49 "plural.y" -#ifndef YYSTYPE -typedef union { - unsigned long int num; - enum operator op; - struct expression *exp; -} yystype; -# define YYSTYPE yystype -# define YYSTYPE_IS_TRIVIAL 1 -#endif -#line 55 "plural.y" - -/* Prototypes for local functions. */ -static struct expression *new_exp PARAMS ((int nargs, enum operator op, - struct expression * const *args)); -static inline struct expression *new_exp_0 PARAMS ((enum operator op)); -static inline struct expression *new_exp_1 PARAMS ((enum operator op, - struct expression *right)); -static struct expression *new_exp_2 PARAMS ((enum operator op, - struct expression *left, - struct expression *right)); -static inline struct expression *new_exp_3 PARAMS ((enum operator op, - struct expression *bexp, - struct expression *tbranch, - struct expression *fbranch)); -static int yylex PARAMS ((YYSTYPE *lval, const char **pexp)); -static void yyerror PARAMS ((const char *str)); - -/* Allocation of expressions. */ - -static struct expression * -new_exp (nargs, op, args) - int nargs; - enum operator op; - struct expression * const *args; -{ - int i; - struct expression *newp; - - /* If any of the argument could not be malloc'ed, just return NULL. */ - for (i = nargs - 1; i >= 0; i--) - if (args[i] == NULL) - goto fail; - - /* Allocate a new expression. */ - newp = (struct expression *) malloc (sizeof (*newp)); - if (newp != NULL) - { - newp->nargs = nargs; - newp->operation = op; - for (i = nargs - 1; i >= 0; i--) - newp->val.args[i] = args[i]; - return newp; - } - - fail: - for (i = nargs - 1; i >= 0; i--) - FREE_EXPRESSION (args[i]); - - return NULL; -} - -static inline struct expression * -new_exp_0 (op) - enum operator op; -{ - return new_exp (0, op, NULL); -} - -static inline struct expression * -new_exp_1 (op, right) - enum operator op; - struct expression *right; -{ - struct expression *args[1]; - - args[0] = right; - return new_exp (1, op, args); -} - -static struct expression * -new_exp_2 (op, left, right) - enum operator op; - struct expression *left; - struct expression *right; -{ - struct expression *args[2]; - - args[0] = left; - args[1] = right; - return new_exp (2, op, args); -} - -static inline struct expression * -new_exp_3 (op, bexp, tbranch, fbranch) - enum operator op; - struct expression *bexp; - struct expression *tbranch; - struct expression *fbranch; -{ - struct expression *args[3]; - - args[0] = bexp; - args[1] = tbranch; - args[2] = fbranch; - return new_exp (3, op, args); -} - -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - - - -#define YYFINAL 27 -#define YYFLAG -32768 -#define YYNTBASE 16 - -/* YYTRANSLATE(YYLEX) -- Bison token number corresponding to YYLEX. */ -#define YYTRANSLATE(x) ((unsigned)(x) <= 261 ? yytranslate[x] : 18) - -/* YYTRANSLATE[YYLEX] -- Bison token number corresponding to YYLEX. */ -static const char yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 10, 2, 2, 2, 2, 5, 2, - 14, 15, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 12, 2, - 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 13, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 6, 7, 8, - 9, 11 -}; - -#if YYDEBUG -static const short yyprhs[] = -{ - 0, 0, 2, 8, 12, 16, 20, 24, 28, 32, - 35, 37, 39 -}; -static const short yyrhs[] = -{ - 17, 0, 17, 3, 17, 12, 17, 0, 17, 4, - 17, 0, 17, 5, 17, 0, 17, 6, 17, 0, - 17, 7, 17, 0, 17, 8, 17, 0, 17, 9, - 17, 0, 10, 17, 0, 13, 0, 11, 0, 14, - 17, 15, 0 -}; - -#endif - -#if YYDEBUG -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -static const short yyrline[] = -{ - 0, 174, 182, 186, 190, 194, 198, 202, 206, 210, - 214, 218, 223 -}; -#endif - - -#if (YYDEBUG) || defined YYERROR_VERBOSE - -/* YYTNAME[TOKEN_NUM] -- String name of the token TOKEN_NUM. */ -static const char *const yytname[] = -{ - "$", "error", "$undefined.", "'?'", "'|'", "'&'", "EQUOP2", "CMPOP2", - "ADDOP2", "MULOP2", "'!'", "NUMBER", "':'", "'n'", "'('", "')'", - "start", "exp", 0 -}; -#endif - -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const short yyr1[] = -{ - 0, 16, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17 -}; - -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const short yyr2[] = -{ - 0, 1, 5, 3, 3, 3, 3, 3, 3, 2, - 1, 1, 3 -}; - -/* YYDEFACT[S] -- default rule to reduce with in state S when YYTABLE - doesn't specify something else to do. Zero means the default is an - error. */ -static const short yydefact[] = -{ - 0, 0, 11, 10, 0, 1, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 12, 0, 3, 4, 5, - 6, 7, 8, 0, 2, 0, 0, 0 -}; - -static const short yydefgoto[] = -{ - 25, 5 -}; - -static const short yypact[] = -{ - -9, -9,-32768,-32768, -9, 34,-32768, 11, -9, -9, - -9, -9, -9, -9, -9,-32768, 24, 39, 43, 16, - 26, -3,-32768, -9, 34, 21, 53,-32768 -}; - -static const short yypgoto[] = -{ - -32768, -1 -}; - - -#define YYLAST 53 - - -static const short yytable[] = -{ - 6, 1, 2, 7, 3, 4, 14, 16, 17, 18, - 19, 20, 21, 22, 8, 9, 10, 11, 12, 13, - 14, 26, 24, 12, 13, 14, 15, 8, 9, 10, - 11, 12, 13, 14, 13, 14, 23, 8, 9, 10, - 11, 12, 13, 14, 10, 11, 12, 13, 14, 11, - 12, 13, 14, 27 -}; - -static const short yycheck[] = -{ - 1, 10, 11, 4, 13, 14, 9, 8, 9, 10, - 11, 12, 13, 14, 3, 4, 5, 6, 7, 8, - 9, 0, 23, 7, 8, 9, 15, 3, 4, 5, - 6, 7, 8, 9, 8, 9, 12, 3, 4, 5, - 6, 7, 8, 9, 5, 6, 7, 8, 9, 6, - 7, 8, 9, 0 -}; -#define YYPURE 1 - -/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ -#line 3 "/usr/local/share/bison/bison.simple" - -/* Skeleton output parser for bison, - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software - Foundation, Inc. - - 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; either version 2, or (at your option) - any later version. - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/* As a special exception, when this file is copied by Bison into a - Bison output file, you may use that output file without restriction. - This special exception was added by the Free Software Foundation - in version 1.24 of Bison. */ - -/* This is the parser code that is written into each bison parser when - the %semantic_parser declaration is not specified in the grammar. - It was written by Richard Stallman by simplifying the hairy parser - used when %semantic_parser is specified. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -#if ! defined (yyoverflow) || defined (YYERROR_VERBOSE) - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# if YYSTACK_USE_ALLOCA -# define YYSTACK_ALLOC alloca -# else -# ifndef YYSTACK_USE_ALLOCA -# if defined (alloca) || defined (_ALLOCA_H) -# define YYSTACK_ALLOC alloca -# else -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# else -# if defined (__STDC__) || defined (__cplusplus) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -# define YYSTACK_ALLOC malloc -# define YYSTACK_FREE free -# endif -#endif /* ! defined (yyoverflow) || defined (YYERROR_VERBOSE) */ - - -#if (! defined (yyoverflow) \ - && (! defined (__cplusplus) \ - || (YYLTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - short yyss; - YYSTYPE yyvs; -# if YYLSP_NEEDED - YYLTYPE yyls; -# endif -}; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAX (sizeof (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# if YYLSP_NEEDED -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (short) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ - + 2 * YYSTACK_GAP_MAX) -# else -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAX) -# endif - -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - register YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (0) -# endif -# endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAX; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (0) - -#endif - - -#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) -# define YYSIZE_T __SIZE_TYPE__ -#endif -#if ! defined (YYSIZE_T) && defined (size_t) -# define YYSIZE_T size_t -#endif -#if ! defined (YYSIZE_T) -# if defined (__STDC__) || defined (__cplusplus) -# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -#endif -#if ! defined (YYSIZE_T) -# define YYSIZE_T unsigned int -#endif - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY -2 -#define YYEOF 0 -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrlab1 -/* Like YYERROR except do call yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ -#define YYFAIL goto yyerrlab -#define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yychar1 = YYTRANSLATE (yychar); \ - YYPOPSTACK; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror ("syntax error: cannot back up"); \ - YYERROR; \ - } \ -while (0) - -#define YYTERROR 1 -#define YYERRCODE 256 - - -/* YYLLOC_DEFAULT -- Compute the default location (before the actions - are run). - - When YYLLOC_DEFAULT is run, CURRENT is set the location of the - first token. By default, to implement support for ranges, extend - its range to the last symbol. */ - -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - Current.last_line = Rhs[N].last_line; \ - Current.last_column = Rhs[N].last_column; -#endif - - -/* YYLEX -- calling `yylex' with the right arguments. */ - -#if YYPURE -# if YYLSP_NEEDED -# ifdef YYLEX_PARAM -# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) -# else -# define YYLEX yylex (&yylval, &yylloc) -# endif -# else /* !YYLSP_NEEDED */ -# ifdef YYLEX_PARAM -# define YYLEX yylex (&yylval, YYLEX_PARAM) -# else -# define YYLEX yylex (&yylval) -# endif -# endif /* !YYLSP_NEEDED */ -#else /* !YYPURE */ -# define YYLEX yylex () -#endif /* !YYPURE */ - - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (0) -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -#endif /* !YYDEBUG */ - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#if YYMAXDEPTH == 0 -# undef YYMAXDEPTH -#endif - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - -#ifdef YYERROR_VERBOSE - -# ifndef yystrlen -# if defined (__GLIBC__) && defined (_STRING_H) -# define yystrlen strlen -# else -/* Return the length of YYSTR. */ -static YYSIZE_T -# if defined (__STDC__) || defined (__cplusplus) -yystrlen (const char *yystr) -# else -yystrlen (yystr) - const char *yystr; -# endif -{ - register const char *yys = yystr; - - while (*yys++ != '\0') - continue; - - return yys - yystr - 1; -} -# endif -# endif - -# ifndef yystpcpy -# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -static char * -# if defined (__STDC__) || defined (__cplusplus) -yystpcpy (char *yydest, const char *yysrc) -# else -yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -# endif -{ - register char *yyd = yydest; - register const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -# endif -#endif - -#line 315 "/usr/local/share/bison/bison.simple" - - -/* The user can define YYPARSE_PARAM as the name of an argument to be passed - into yyparse. The argument should have type void *. - It should actually point to an object. - Grammar actions can access the variable by casting it - to the proper pointer type. */ - -#ifdef YYPARSE_PARAM -# if defined (__STDC__) || defined (__cplusplus) -# define YYPARSE_PARAM_ARG void *YYPARSE_PARAM -# define YYPARSE_PARAM_DECL -# else -# define YYPARSE_PARAM_ARG YYPARSE_PARAM -# define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; -# endif -#else /* !YYPARSE_PARAM */ -# define YYPARSE_PARAM_ARG -# define YYPARSE_PARAM_DECL -#endif /* !YYPARSE_PARAM */ - -/* Prevent warning if -Wstrict-prototypes. */ -#ifdef __GNUC__ -# ifdef YYPARSE_PARAM -int yyparse (void *); -# else -int yyparse (void); -# endif -#endif - -/* YY_DECL_VARIABLES -- depending whether we use a pure parser, - variables are global, or local to YYPARSE. */ - -#define YY_DECL_NON_LSP_VARIABLES \ -/* The lookahead symbol. */ \ -int yychar; \ - \ -/* The semantic value of the lookahead symbol. */ \ -YYSTYPE yylval; \ - \ -/* Number of parse errors so far. */ \ -int yynerrs; - -#if YYLSP_NEEDED -# define YY_DECL_VARIABLES \ -YY_DECL_NON_LSP_VARIABLES \ - \ -/* Location data for the lookahead symbol. */ \ -YYLTYPE yylloc; -#else -# define YY_DECL_VARIABLES \ -YY_DECL_NON_LSP_VARIABLES -#endif - - -/* If nonreentrant, generate the variables here. */ - -#if !YYPURE -YY_DECL_VARIABLES -#endif /* !YYPURE */ - -int -yyparse (YYPARSE_PARAM_ARG) - YYPARSE_PARAM_DECL -{ - /* If reentrant, generate the variables here. */ -#if YYPURE - YY_DECL_VARIABLES -#endif /* !YYPURE */ - - register int yystate; - register int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Lookahead token as an internal (translated) token number. */ - int yychar1 = 0; - - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - short yyssa[YYINITDEPTH]; - short *yyss = yyssa; - register short *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - register YYSTYPE *yyvsp; - -#if YYLSP_NEEDED - /* The location stack. */ - YYLTYPE yylsa[YYINITDEPTH]; - YYLTYPE *yyls = yylsa; - YYLTYPE *yylsp; -#endif - -#if YYLSP_NEEDED -# define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) -#else -# define YYPOPSTACK (yyvsp--, yyssp--) -#endif - - YYSIZE_T yystacksize = YYINITDEPTH; - - - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; -#if YYLSP_NEEDED - YYLTYPE yyloc; -#endif - - /* When reducing, the number of symbols on the RHS of the reduced - rule. */ - int yylen; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yystate = 0; - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - - yyssp = yyss; - yyvsp = yyvs; -#if YYLSP_NEEDED - yylsp = yyls; -#endif - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | -`------------------------------------------------------------*/ - yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. so pushing a state here evens the stacks. - */ - yyssp++; - - yysetstate: - *yyssp = yystate; - - if (yyssp >= yyss + yystacksize - 1) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; - -#ifdef yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - short *yyss1 = yyss; - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. */ -# if YYLSP_NEEDED - YYLTYPE *yyls1 = yyls; - /* This used to be a conditional around just the two extra args, - but that might be undefined if yyoverflow is a macro. */ - yyoverflow ("parser stack overflow", - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - &yyls1, yysize * sizeof (*yylsp), - &yystacksize); - yyls = yyls1; -# else - yyoverflow ("parser stack overflow", - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); -# endif - yyss = yyss1; - yyvs = yyvs1; - } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyoverflowlab; -# else - /* Extend the stack our own way. */ - if (yystacksize >= YYMAXDEPTH) - goto yyoverflowlab; - yystacksize *= 2; - if (yystacksize > YYMAXDEPTH) - yystacksize = YYMAXDEPTH; - - { - short *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyoverflowlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); -# if YYLSP_NEEDED - YYSTACK_RELOCATE (yyls); -# endif -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif -#endif /* no yyoverflow */ - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; -#if YYLSP_NEEDED - yylsp = yyls + yysize - 1; -#endif - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); - - if (yyssp >= yyss + yystacksize - 1) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - - goto yybackup; - - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - -/* Do appropriate processing given the current state. */ -/* Read a lookahead token if we need one and don't already have one. */ -/* yyresume: */ - - /* First try to decide what to do without reference to lookahead token. */ - - yyn = yypact[yystate]; - if (yyn == YYFLAG) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* yychar is either YYEMPTY or YYEOF - or a valid token in external form. */ - - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - yychar = YYLEX; - } - - /* Convert token to internal form (in yychar1) for indexing tables with */ - - if (yychar <= 0) /* This means end of input. */ - { - yychar1 = 0; - yychar = YYEOF; /* Don't call YYLEX any more */ - - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - yychar1 = YYTRANSLATE (yychar); - -#if YYDEBUG - /* We have to keep this `#if YYDEBUG', since we use variables - which are defined only if `YYDEBUG' is set. */ - if (yydebug) - { - YYFPRINTF (stderr, "Next token is %d (%s", - yychar, yytname[yychar1]); - /* Give the individual parser a way to print the precise - meaning of a token, for further debugging info. */ -# ifdef YYPRINT - YYPRINT (stderr, yychar, yylval); -# endif - YYFPRINTF (stderr, ")\n"); - } -#endif - } - - yyn += yychar1; - if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) - goto yydefault; - - yyn = yytable[yyn]; - - /* yyn is what to do for this token type in this state. - Negative => reduce, -yyn is rule number. - Positive => shift, yyn is new state. - New state is final state => don't bother to shift, - just return success. - 0, or most negative number => error. */ - - if (yyn < 0) - { - if (yyn == YYFLAG) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - else if (yyn == 0) - goto yyerrlab; - - if (yyn == YYFINAL) - YYACCEPT; - - /* Shift the lookahead token. */ - YYDPRINTF ((stderr, "Shifting token %d (%s), ", - yychar, yytname[yychar1])); - - /* Discard the token being shifted unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; - - *++yyvsp = yylval; -#if YYLSP_NEEDED - *++yylsp = yylloc; -#endif - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - yystate = yyn; - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- Do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. - - Otherwise, the following line sets YYVAL to the semantic value of - the lookahead token. This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - -#if YYLSP_NEEDED - /* Similarly for the default location. Let the user run additional - commands if for instance locations are ranges. */ - yyloc = yylsp[1-yylen]; - YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); -#endif - -#if YYDEBUG - /* We have to keep this `#if YYDEBUG', since we use variables which - are defined only if `YYDEBUG' is set. */ - if (yydebug) - { - int yyi; - - YYFPRINTF (stderr, "Reducing via rule %d (line %d), ", - yyn, yyrline[yyn]); - - /* Print the symbols being reduced, and their result. */ - for (yyi = yyprhs[yyn]; yyrhs[yyi] > 0; yyi++) - YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]); - YYFPRINTF (stderr, " -> %s\n", yytname[yyr1[yyn]]); - } -#endif - - switch (yyn) { - -case 1: -#line 175 "plural.y" -{ - if (yyvsp[0].exp == NULL) - YYABORT; - ((struct parse_args *) arg)->res = yyvsp[0].exp; - } - break; -case 2: -#line 183 "plural.y" -{ - yyval.exp = new_exp_3 (qmop, yyvsp[-4].exp, yyvsp[-2].exp, yyvsp[0].exp); - } - break; -case 3: -#line 187 "plural.y" -{ - yyval.exp = new_exp_2 (lor, yyvsp[-2].exp, yyvsp[0].exp); - } - break; -case 4: -#line 191 "plural.y" -{ - yyval.exp = new_exp_2 (land, yyvsp[-2].exp, yyvsp[0].exp); - } - break; -case 5: -#line 195 "plural.y" -{ - yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp); - } - break; -case 6: -#line 199 "plural.y" -{ - yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp); - } - break; -case 7: -#line 203 "plural.y" -{ - yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp); - } - break; -case 8: -#line 207 "plural.y" -{ - yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp); - } - break; -case 9: -#line 211 "plural.y" -{ - yyval.exp = new_exp_1 (lnot, yyvsp[0].exp); - } - break; -case 10: -#line 215 "plural.y" -{ - yyval.exp = new_exp_0 (var); - } - break; -case 11: -#line 219 "plural.y" -{ - if ((yyval.exp = new_exp_0 (num)) != NULL) - yyval.exp->val.num = yyvsp[0].num; - } - break; -case 12: -#line 224 "plural.y" -{ - yyval.exp = yyvsp[-1].exp; - } - break; -} - -#line 705 "/usr/local/share/bison/bison.simple" - - - yyvsp -= yylen; - yyssp -= yylen; -#if YYLSP_NEEDED - yylsp -= yylen; -#endif - -#if YYDEBUG - if (yydebug) - { - short *yyssp1 = yyss - 1; - YYFPRINTF (stderr, "state stack now"); - while (yyssp1 != yyssp) - YYFPRINTF (stderr, " %d", *++yyssp1); - YYFPRINTF (stderr, "\n"); - } -#endif - - *++yyvsp = yyval; -#if YYLSP_NEEDED - *++yylsp = yyloc; -#endif - - /* Now `shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTBASE] + *yyssp; - if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTBASE]; - - goto yynewstate; - - -/*------------------------------------. -| yyerrlab -- here on detecting error | -`------------------------------------*/ -yyerrlab: - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; - -#ifdef YYERROR_VERBOSE - yyn = yypact[yystate]; - - if (yyn > YYFLAG && yyn < YYLAST) - { - YYSIZE_T yysize = 0; - char *yymsg; - int yyx, yycount; - - yycount = 0; - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - for (yyx = yyn < 0 ? -yyn : 0; - yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) - if (yycheck[yyx + yyn] == yyx) - yysize += yystrlen (yytname[yyx]) + 15, yycount++; - yysize += yystrlen ("parse error, unexpected ") + 1; - yysize += yystrlen (yytname[YYTRANSLATE (yychar)]); - yymsg = (char *) YYSTACK_ALLOC (yysize); - if (yymsg != 0) - { - char *yyp = yystpcpy (yymsg, "parse error, unexpected "); - yyp = yystpcpy (yyp, yytname[YYTRANSLATE (yychar)]); - - if (yycount < 5) - { - yycount = 0; - for (yyx = yyn < 0 ? -yyn : 0; - yyx < (int) (sizeof (yytname) / sizeof (char *)); - yyx++) - if (yycheck[yyx + yyn] == yyx) - { - const char *yyq = ! yycount ? ", expecting " : " or "; - yyp = yystpcpy (yyp, yyq); - yyp = yystpcpy (yyp, yytname[yyx]); - yycount++; - } - } - yyerror (yymsg); - YYSTACK_FREE (yymsg); - } - else - yyerror ("parse error; also virtual memory exhausted"); - } - else -#endif /* defined (YYERROR_VERBOSE) */ - yyerror ("parse error"); - } - goto yyerrlab1; - - -/*--------------------------------------------------. -| yyerrlab1 -- error raised explicitly by an action | -`--------------------------------------------------*/ -yyerrlab1: - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - /* return failure if at end of input */ - if (yychar == YYEOF) - YYABORT; - YYDPRINTF ((stderr, "Discarding token %d (%s).\n", - yychar, yytname[yychar1])); - yychar = YYEMPTY; - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - - yyerrstatus = 3; /* Each real token shifted decrements this */ - - goto yyerrhandle; - - -/*-------------------------------------------------------------------. -| yyerrdefault -- current state does not do anything special for the | -| error token. | -`-------------------------------------------------------------------*/ -yyerrdefault: -#if 0 - /* This is wrong; only states that explicitly want error tokens - should shift them. */ - - /* If its default is to accept any token, ok. Otherwise pop it. */ - yyn = yydefact[yystate]; - if (yyn) - goto yydefault; -#endif - - -/*---------------------------------------------------------------. -| yyerrpop -- pop the current state because it cannot handle the | -| error token | -`---------------------------------------------------------------*/ -yyerrpop: - if (yyssp == yyss) - YYABORT; - yyvsp--; - yystate = *--yyssp; -#if YYLSP_NEEDED - yylsp--; -#endif - -#if YYDEBUG - if (yydebug) - { - short *yyssp1 = yyss - 1; - YYFPRINTF (stderr, "Error: state stack now"); - while (yyssp1 != yyssp) - YYFPRINTF (stderr, " %d", *++yyssp1); - YYFPRINTF (stderr, "\n"); - } -#endif - -/*--------------. -| yyerrhandle. | -`--------------*/ -yyerrhandle: - yyn = yypact[yystate]; - if (yyn == YYFLAG) - goto yyerrdefault; - - yyn += YYTERROR; - if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) - goto yyerrdefault; - - yyn = yytable[yyn]; - if (yyn < 0) - { - if (yyn == YYFLAG) - goto yyerrpop; - yyn = -yyn; - goto yyreduce; - } - else if (yyn == 0) - goto yyerrpop; - - if (yyn == YYFINAL) - YYACCEPT; - - YYDPRINTF ((stderr, "Shifting error token, ")); - - *++yyvsp = yylval; -#if YYLSP_NEEDED - *++yylsp = yylloc; -#endif - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturn; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturn; - -/*---------------------------------------------. -| yyoverflowab -- parser overflow comes here. | -`---------------------------------------------*/ -yyoverflowlab: - yyerror ("parser stack overflow"); - yyresult = 2; - /* Fall through. */ - -yyreturn: -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif - return yyresult; -} -#line 229 "plural.y" - - -void -internal_function -FREE_EXPRESSION (exp) - struct expression *exp; -{ - if (exp == NULL) - return; - - /* Handle the recursive case. */ - switch (exp->nargs) - { - case 3: - FREE_EXPRESSION (exp->val.args[2]); - /* FALLTHROUGH */ - case 2: - FREE_EXPRESSION (exp->val.args[1]); - /* FALLTHROUGH */ - case 1: - FREE_EXPRESSION (exp->val.args[0]); - /* FALLTHROUGH */ - default: - break; - } - - free (exp); -} - - -static int -yylex (lval, pexp) - YYSTYPE *lval; - const char **pexp; -{ - const char *exp = *pexp; - int result; - - while (1) - { - if (exp[0] == '\0') - { - *pexp = exp; - return YYEOF; - } - - if (exp[0] != ' ' && exp[0] != '\t') - break; - - ++exp; - } - - result = *exp++; - switch (result) - { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - unsigned long int n = result - '0'; - while (exp[0] >= '0' && exp[0] <= '9') - { - n *= 10; - n += exp[0] - '0'; - ++exp; - } - lval->num = n; - result = NUMBER; - } - break; - - case '=': - if (exp[0] == '=') - { - ++exp; - lval->op = equal; - result = EQUOP2; - } - else - result = YYERRCODE; - break; - - case '!': - if (exp[0] == '=') - { - ++exp; - lval->op = not_equal; - result = EQUOP2; - } - break; - - case '&': - case '|': - if (exp[0] == result) - ++exp; - else - result = YYERRCODE; - break; - - case '<': - if (exp[0] == '=') - { - ++exp; - lval->op = less_or_equal; - } - else - lval->op = less_than; - result = CMPOP2; - break; - - case '>': - if (exp[0] == '=') - { - ++exp; - lval->op = greater_or_equal; - } - else - lval->op = greater_than; - result = CMPOP2; - break; - - case '*': - lval->op = mult; - result = MULOP2; - break; - - case '/': - lval->op = divide; - result = MULOP2; - break; - - case '%': - lval->op = module; - result = MULOP2; - break; - - case '+': - lval->op = plus; - result = ADDOP2; - break; - - case '-': - lval->op = minus; - result = ADDOP2; - break; - - case 'n': - case '?': - case ':': - case '(': - case ')': - /* Nothing, just return the character. */ - break; - - case ';': - case '\n': - case '\0': - /* Be safe and let the user call this function again. */ - --exp; - result = YYEOF; - break; - - default: - result = YYERRCODE; -#if YYDEBUG != 0 - --exp; -#endif - break; - } - - *pexp = exp; - - return result; -} - - -static void -yyerror (str) - const char *str; -{ - /* Do nothing. We don't print error messages here. */ -} diff --git a/src/audacious/intl/plural.y b/src/audacious/intl/plural.y deleted file mode 100644 index 75a1c01..0000000 --- a/src/audacious/intl/plural.y +++ /dev/null @@ -1,409 +0,0 @@ -%{ -/* Expression parsing for plural form selection. - Copyright (C) 2000, 2001 Free Software Foundation, Inc. - Written by Ulrich Drepper <drepper@cygnus.com>, 2000. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* The bison generated parser uses alloca. AIX 3 forces us to put this - declaration at the beginning of the file. The declaration in bison's - skeleton file comes too late. This must come before <config.h> - because <config.h> may include arbitrary system headers. */ -#if defined _AIX && !defined __GNUC__ - #pragma alloca -#endif - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stddef.h> -#include <stdlib.h> -#include "plural-exp.h" - -/* The main function generated by the parser is called __gettextparse, - but we want it to be called PLURAL_PARSE. */ -#ifndef _LIBC -# define __gettextparse PLURAL_PARSE -#endif - -#define YYLEX_PARAM &((struct parse_args *) arg)->cp -#define YYPARSE_PARAM arg -%} -%pure_parser -%expect 7 - -%union { - unsigned long int num; - enum operator op; - struct expression *exp; -} - -%{ -/* Prototypes for local functions. */ -static struct expression *new_exp PARAMS ((int nargs, enum operator op, - struct expression * const *args)); -static inline struct expression *new_exp_0 PARAMS ((enum operator op)); -static inline struct expression *new_exp_1 PARAMS ((enum operator op, - struct expression *right)); -static struct expression *new_exp_2 PARAMS ((enum operator op, - struct expression *left, - struct expression *right)); -static inline struct expression *new_exp_3 PARAMS ((enum operator op, - struct expression *bexp, - struct expression *tbranch, - struct expression *fbranch)); -static int yylex PARAMS ((YYSTYPE *lval, const char **pexp)); -static void yyerror PARAMS ((const char *str)); - -/* Allocation of expressions. */ - -static struct expression * -new_exp (nargs, op, args) - int nargs; - enum operator op; - struct expression * const *args; -{ - int i; - struct expression *newp; - - /* If any of the argument could not be malloc'ed, just return NULL. */ - for (i = nargs - 1; i >= 0; i--) - if (args[i] == NULL) - goto fail; - - /* Allocate a new expression. */ - newp = (struct expression *) malloc (sizeof (*newp)); - if (newp != NULL) - { - newp->nargs = nargs; - newp->operation = op; - for (i = nargs - 1; i >= 0; i--) - newp->val.args[i] = args[i]; - return newp; - } - - fail: - for (i = nargs - 1; i >= 0; i--) - FREE_EXPRESSION (args[i]); - - return NULL; -} - -static inline struct expression * -new_exp_0 (op) - enum operator op; -{ - return new_exp (0, op, NULL); -} - -static inline struct expression * -new_exp_1 (op, right) - enum operator op; - struct expression *right; -{ - struct expression *args[1]; - - args[0] = right; - return new_exp (1, op, args); -} - -static struct expression * -new_exp_2 (op, left, right) - enum operator op; - struct expression *left; - struct expression *right; -{ - struct expression *args[2]; - - args[0] = left; - args[1] = right; - return new_exp (2, op, args); -} - -static inline struct expression * -new_exp_3 (op, bexp, tbranch, fbranch) - enum operator op; - struct expression *bexp; - struct expression *tbranch; - struct expression *fbranch; -{ - struct expression *args[3]; - - args[0] = bexp; - args[1] = tbranch; - args[2] = fbranch; - return new_exp (3, op, args); -} - -%} - -/* This declares that all operators have the same associativity and the - precedence order as in C. See [Harbison, Steele: C, A Reference Manual]. - There is no unary minus and no bitwise operators. - Operators with the same syntactic behaviour have been merged into a single - token, to save space in the array generated by bison. */ -%right '?' /* ? */ -%left '|' /* || */ -%left '&' /* && */ -%left EQUOP2 /* == != */ -%left CMPOP2 /* < > <= >= */ -%left ADDOP2 /* + - */ -%left MULOP2 /* * / % */ -%right '!' /* ! */ - -%token <op> EQUOP2 CMPOP2 ADDOP2 MULOP2 -%token <num> NUMBER -%type <exp> exp - -%% - -start: exp - { - if ($1 == NULL) - YYABORT; - ((struct parse_args *) arg)->res = $1; - } - ; - -exp: exp '?' exp ':' exp - { - $$ = new_exp_3 (qmop, $1, $3, $5); - } - | exp '|' exp - { - $$ = new_exp_2 (lor, $1, $3); - } - | exp '&' exp - { - $$ = new_exp_2 (land, $1, $3); - } - | exp EQUOP2 exp - { - $$ = new_exp_2 ($2, $1, $3); - } - | exp CMPOP2 exp - { - $$ = new_exp_2 ($2, $1, $3); - } - | exp ADDOP2 exp - { - $$ = new_exp_2 ($2, $1, $3); - } - | exp MULOP2 exp - { - $$ = new_exp_2 ($2, $1, $3); - } - | '!' exp - { - $$ = new_exp_1 (lnot, $2); - } - | 'n' - { - $$ = new_exp_0 (var); - } - | NUMBER - { - if (($$ = new_exp_0 (num)) != NULL) - $$->val.num = $1; - } - | '(' exp ')' - { - $$ = $2; - } - ; - -%% - -void -internal_function -FREE_EXPRESSION (exp) - struct expression *exp; -{ - if (exp == NULL) - return; - - /* Handle the recursive case. */ - switch (exp->nargs) - { - case 3: - FREE_EXPRESSION (exp->val.args[2]); - /* FALLTHROUGH */ - case 2: - FREE_EXPRESSION (exp->val.args[1]); - /* FALLTHROUGH */ - case 1: - FREE_EXPRESSION (exp->val.args[0]); - /* FALLTHROUGH */ - default: - break; - } - - free (exp); -} - - -static int -yylex (lval, pexp) - YYSTYPE *lval; - const char **pexp; -{ - const char *exp = *pexp; - int result; - - while (1) - { - if (exp[0] == '\0') - { - *pexp = exp; - return YYEOF; - } - - if (exp[0] != ' ' && exp[0] != '\t') - break; - - ++exp; - } - - result = *exp++; - switch (result) - { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - unsigned long int n = result - '0'; - while (exp[0] >= '0' && exp[0] <= '9') - { - n *= 10; - n += exp[0] - '0'; - ++exp; - } - lval->num = n; - result = NUMBER; - } - break; - - case '=': - if (exp[0] == '=') - { - ++exp; - lval->op = equal; - result = EQUOP2; - } - else - result = YYERRCODE; - break; - - case '!': - if (exp[0] == '=') - { - ++exp; - lval->op = not_equal; - result = EQUOP2; - } - break; - - case '&': - case '|': - if (exp[0] == result) - ++exp; - else - result = YYERRCODE; - break; - - case '<': - if (exp[0] == '=') - { - ++exp; - lval->op = less_or_equal; - } - else - lval->op = less_than; - result = CMPOP2; - break; - - case '>': - if (exp[0] == '=') - { - ++exp; - lval->op = greater_or_equal; - } - else - lval->op = greater_than; - result = CMPOP2; - break; - - case '*': - lval->op = mult; - result = MULOP2; - break; - - case '/': - lval->op = divide; - result = MULOP2; - break; - - case '%': - lval->op = module; - result = MULOP2; - break; - - case '+': - lval->op = plus; - result = ADDOP2; - break; - - case '-': - lval->op = minus; - result = ADDOP2; - break; - - case 'n': - case '?': - case ':': - case '(': - case ')': - /* Nothing, just return the character. */ - break; - - case ';': - case '\n': - case '\0': - /* Be safe and let the user call this function again. */ - --exp; - result = YYEOF; - break; - - default: - result = YYERRCODE; -#if YYDEBUG != 0 - --exp; -#endif - break; - } - - *pexp = exp; - - return result; -} - - -static void -yyerror (str) - const char *str; -{ - /* Do nothing. We don't print error messages here. */ -} diff --git a/src/audacious/intl/ref-add.sin b/src/audacious/intl/ref-add.sin deleted file mode 100644 index 3678c28..0000000 --- a/src/audacious/intl/ref-add.sin +++ /dev/null @@ -1,31 +0,0 @@ -# Add this package to a list of references stored in a text file. -# -# Copyright (C) 2000 Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU Library General Public License as published -# by the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# Library General Public License for more details. -# -# You should have received a copy of the GNU Library General Public -# License along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. -# -# Written by Bruno Haible <haible@clisp.cons.org>. -# -/^# Packages using this file: / { - s/# Packages using this file:// - ta - :a - s/ @PACKAGE@ / @PACKAGE@ / - tb - s/ $/ @PACKAGE@ / - :b - s/^/# Packages using this file:/ -} diff --git a/src/audacious/intl/ref-del.sin b/src/audacious/intl/ref-del.sin deleted file mode 100644 index 0c12d8e..0000000 --- a/src/audacious/intl/ref-del.sin +++ /dev/null @@ -1,26 +0,0 @@ -# Remove this package from a list of references stored in a text file. -# -# Copyright (C) 2000 Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU Library General Public License as published -# by the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# Library General Public License for more details. -# -# You should have received a copy of the GNU Library General Public -# License along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. -# -# Written by Bruno Haible <haible@clisp.cons.org>. -# -/^# Packages using this file: / { - s/# Packages using this file:// - s/ @PACKAGE@ / / - s/^/# Packages using this file:/ -} diff --git a/src/audacious/intl/relocatable.c b/src/audacious/intl/relocatable.c deleted file mode 100644 index ef522c4..0000000 --- a/src/audacious/intl/relocatable.c +++ /dev/null @@ -1,439 +0,0 @@ -/* Provide relocatable packages. - Copyright (C) 2003 Free Software Foundation, Inc. - Written by Bruno Haible <bruno@clisp.org>, 2003. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - - -/* Tell glibc's <stdio.h> to provide a prototype for getline(). - This must come before <config.h> because <config.h> may include - <features.h>, and once <features.h> has been included, it's too late. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -/* Specification. */ -#include "relocatable.h" - -#if ENABLE_RELOCATABLE - -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#ifdef NO_XMALLOC -# define xmalloc malloc -#else -# include "xmalloc.h" -#endif - -#if DEPENDS_ON_LIBCHARSET -# include <libcharset.h> -#endif -#if DEPENDS_ON_LIBICONV && HAVE_ICONV -# include <iconv.h> -#endif -#if DEPENDS_ON_LIBINTL && ENABLE_NLS -# include <libintl.h> -#endif - -/* Faked cheap 'bool'. */ -#undef bool -#undef false -#undef true -#define bool int -#define false 0 -#define true 1 - -/* Pathname support. - ISSLASH(C) tests whether C is a directory separator character. - IS_PATH_WITH_DIR(P) tests whether P contains a directory specification. - */ -#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ - /* Win32, OS/2, DOS */ -# define ISSLASH(C) ((C) == '/' || (C) == '\\') -# define HAS_DEVICE(P) \ - ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ - && (P)[1] == ':') -# define IS_PATH_WITH_DIR(P) \ - (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P)) -# define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0) -#else - /* Unix */ -# define ISSLASH(C) ((C) == '/') -# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL) -# define FILESYSTEM_PREFIX_LEN(P) 0 -#endif - -/* Original installation prefix. */ -static char *orig_prefix; -static size_t orig_prefix_len; -/* Current installation prefix. */ -static char *curr_prefix; -static size_t curr_prefix_len; -/* These prefixes do not end in a slash. Anything that will be concatenated - to them must start with a slash. */ - -/* Sets the original and the current installation prefix of this module. - Relocation simply replaces a pathname starting with the original prefix - by the corresponding pathname with the current prefix instead. Both - prefixes should be directory names without trailing slash (i.e. use "" - instead of "/"). */ -static void -set_this_relocation_prefix (const char *orig_prefix_arg, - const char *curr_prefix_arg) -{ - if (orig_prefix_arg != NULL && curr_prefix_arg != NULL - /* Optimization: if orig_prefix and curr_prefix are equal, the - relocation is a nop. */ - && strcmp (orig_prefix_arg, curr_prefix_arg) != 0) - { - /* Duplicate the argument strings. */ - char *memory; - - orig_prefix_len = strlen (orig_prefix_arg); - curr_prefix_len = strlen (curr_prefix_arg); - memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1); -#ifdef NO_XMALLOC - if (memory != NULL) -#endif - { - memcpy (memory, orig_prefix_arg, orig_prefix_len + 1); - orig_prefix = memory; - memory += orig_prefix_len + 1; - memcpy (memory, curr_prefix_arg, curr_prefix_len + 1); - curr_prefix = memory; - return; - } - } - orig_prefix = NULL; - curr_prefix = NULL; - /* Don't worry about wasted memory here - this function is usually only - called once. */ -} - -/* Sets the original and the current installation prefix of the package. - Relocation simply replaces a pathname starting with the original prefix - by the corresponding pathname with the current prefix instead. Both - prefixes should be directory names without trailing slash (i.e. use "" - instead of "/"). */ -void -set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg) -{ - set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg); - - /* Now notify all dependent libraries. */ -#if DEPENDS_ON_LIBCHARSET - libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); -#endif -#if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109 - libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); -#endif -#if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix - libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); -#endif -} - -/* Convenience function: - Computes the current installation prefix, based on the original - installation prefix, the original installation directory of a particular - file, and the current pathname of this file. Returns NULL upon failure. */ -#ifdef IN_LIBRARY -#define compute_curr_prefix local_compute_curr_prefix -static -#endif -const char * -compute_curr_prefix (const char *orig_installprefix, - const char *orig_installdir, - const char *curr_pathname) -{ - const char *curr_installdir; - const char *rel_installdir; - - if (curr_pathname == NULL) - return NULL; - - /* Determine the relative installation directory, relative to the prefix. - This is simply the difference between orig_installprefix and - orig_installdir. */ - if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix)) - != 0) - /* Shouldn't happen - nothing should be installed outside $(prefix). */ - return NULL; - rel_installdir = orig_installdir + strlen (orig_installprefix); - - /* Determine the current installation directory. */ - { - const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname); - const char *p = curr_pathname + strlen (curr_pathname); - char *q; - - while (p > p_base) - { - p--; - if (ISSLASH (*p)) - break; - } - - q = (char *) xmalloc (p - curr_pathname + 1); -#ifdef NO_XMALLOC - if (q == NULL) - return NULL; -#endif - memcpy (q, curr_pathname, p - curr_pathname); - q[p - curr_pathname] = '\0'; - curr_installdir = q; - } - - /* Compute the current installation prefix by removing the trailing - rel_installdir from it. */ - { - const char *rp = rel_installdir + strlen (rel_installdir); - const char *cp = curr_installdir + strlen (curr_installdir); - const char *cp_base = - curr_installdir + FILESYSTEM_PREFIX_LEN (curr_installdir); - - while (rp > rel_installdir && cp > cp_base) - { - bool same = false; - const char *rpi = rp; - const char *cpi = cp; - - while (rpi > rel_installdir && cpi > cp_base) - { - rpi--; - cpi--; - if (ISSLASH (*rpi) || ISSLASH (*cpi)) - { - if (ISSLASH (*rpi) && ISSLASH (*cpi)) - same = true; - break; - } -#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__ - /* Win32, OS/2, DOS - case insignificant filesystem */ - if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi) - != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi)) - break; -#else - if (*rpi != *cpi) - break; -#endif - } - if (!same) - break; - /* The last pathname component was the same. opi and cpi now point - to the slash before it. */ - rp = rpi; - cp = cpi; - } - - if (rp > rel_installdir) - /* Unexpected: The curr_installdir does not end with rel_installdir. */ - return NULL; - - { - size_t curr_prefix_len = cp - curr_installdir; - char *curr_prefix; - - curr_prefix = (char *) xmalloc (curr_prefix_len + 1); -#ifdef NO_XMALLOC - if (curr_prefix == NULL) - return NULL; -#endif - memcpy (curr_prefix, curr_installdir, curr_prefix_len); - curr_prefix[curr_prefix_len] = '\0'; - - return curr_prefix; - } - } -} - -#if defined PIC && defined INSTALLDIR - -/* Full pathname of shared library, or NULL. */ -static char *shared_library_fullname; - -#if defined _WIN32 || defined __WIN32__ - -/* Determine the full pathname of the shared library when it is loaded. */ - -BOOL WINAPI -DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved) -{ - (void) reserved; - - if (event == DLL_PROCESS_ATTACH) - { - /* The DLL is being loaded into an application's address range. */ - static char location[MAX_PATH]; - - if (!GetModuleFileName (module_handle, location, sizeof (location))) - /* Shouldn't happen. */ - return FALSE; - - if (!IS_PATH_WITH_DIR (location)) - /* Shouldn't happen. */ - return FALSE; - - shared_library_fullname = strdup (location); - } - - return TRUE; -} - -#else /* Unix */ - -static void -find_shared_library_fullname () -{ -#ifdef __linux__ - FILE *fp; - - /* Open the current process' maps file. It describes one VMA per line. */ - fp = fopen ("/proc/self/maps", "r"); - if (fp) - { - unsigned long address = (unsigned long) &find_shared_library_fullname; - for (;;) - { - unsigned long start, end; - int c; - - if (fscanf (fp, "%lx-%lx", &start, &end) != 2) - break; - if (address >= start && address <= end - 1) - { - /* Found it. Now see if this line contains a filename. */ - while (c = getc (fp), c != EOF && c != '\n' && c != '/') - continue; - if (c == '/') - { - size_t size; - int len; - - ungetc (c, fp); - shared_library_fullname = NULL; size = 0; - len = getline (&shared_library_fullname, &size, fp); - if (len >= 0) - { - /* Success: filled shared_library_fullname. */ - if (len > 0 && shared_library_fullname[len - 1] == '\n') - shared_library_fullname[len - 1] = '\0'; - } - } - break; - } - while (c = getc (fp), c != EOF && c != '\n') - continue; - } - fclose (fp); - } -#endif -} - -#endif /* WIN32 / Unix */ - -/* Return the full pathname of the current shared library. - Return NULL if unknown. - Guaranteed to work only on Linux and Woe32. */ -static char * -get_shared_library_fullname () -{ -#if !(defined _WIN32 || defined __WIN32__) - static bool tried_find_shared_library_fullname; - if (!tried_find_shared_library_fullname) - { - find_shared_library_fullname (); - tried_find_shared_library_fullname = true; - } -#endif - return shared_library_fullname; -} - -#endif /* PIC */ - -/* Returns the pathname, relocated according to the current installation - directory. */ -const char * -relocate (const char *pathname) -{ -#if defined PIC && defined INSTALLDIR - static int initialized; - - /* Initialization code for a shared library. */ - if (!initialized) - { - /* At this point, orig_prefix and curr_prefix likely have already been - set through the main program's set_program_name_and_installdir - function. This is sufficient in the case that the library has - initially been installed in the same orig_prefix. But we can do - better, to also cover the cases that 1. it has been installed - in a different prefix before being moved to orig_prefix and (later) - to curr_prefix, 2. unlike the program, it has not moved away from - orig_prefix. */ - const char *orig_installprefix = INSTALLPREFIX; - const char *orig_installdir = INSTALLDIR; - const char *curr_prefix_better; - - curr_prefix_better = - compute_curr_prefix (orig_installprefix, orig_installdir, - get_shared_library_fullname ()); - if (curr_prefix_better == NULL) - curr_prefix_better = curr_prefix; - - set_relocation_prefix (orig_installprefix, curr_prefix_better); - - initialized = 1; - } -#endif - - /* Note: It is not necessary to perform case insensitive comparison here, - even for DOS-like filesystems, because the pathname argument was - typically created from the same Makefile variable as orig_prefix came - from. */ - if (orig_prefix != NULL && curr_prefix != NULL - && strncmp (pathname, orig_prefix, orig_prefix_len) == 0) - { - if (pathname[orig_prefix_len] == '\0') - /* pathname equals orig_prefix. */ - return curr_prefix; - if (ISSLASH (pathname[orig_prefix_len])) - { - /* pathname starts with orig_prefix. */ - const char *pathname_tail = &pathname[orig_prefix_len]; - char *result = - (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1); - -#ifdef NO_XMALLOC - if (result != NULL) -#endif - { - memcpy (result, curr_prefix, curr_prefix_len); - g_strlcpy (result + curr_prefix_len, pathname_tail, strlen(pathname_tail) + 1); - return result; - } - } - } - /* Nothing to relocate. */ - return pathname; -} - -#endif diff --git a/src/audacious/intl/relocatable.h b/src/audacious/intl/relocatable.h deleted file mode 100644 index 9ce6362..0000000 --- a/src/audacious/intl/relocatable.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Provide relocatable packages. - Copyright (C) 2003 Free Software Foundation, Inc. - Written by Bruno Haible <bruno@clisp.org>, 2003. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifndef _RELOCATABLE_H -#define _RELOCATABLE_H - -/* This can be enabled through the configure --enable-relocatable option. */ -#if ENABLE_RELOCATABLE - -/* When building a DLL, we must export some functions. Note that because - this is a private .h file, we don't need to use __declspec(dllimport) - in any case. */ -#if defined _MSC_VER && BUILDING_DLL -# define RELOCATABLE_DLL_EXPORTED __declspec(dllexport) -#else -# define RELOCATABLE_DLL_EXPORTED -#endif - -/* Sets the original and the current installation prefix of the package. - Relocation simply replaces a pathname starting with the original prefix - by the corresponding pathname with the current prefix instead. Both - prefixes should be directory names without trailing slash (i.e. use "" - instead of "/"). */ -extern RELOCATABLE_DLL_EXPORTED void - set_relocation_prefix (const char *orig_prefix, - const char *curr_prefix); - -/* Returns the pathname, relocated according to the current installation - directory. */ -extern const char * relocate (const char *pathname); - -/* Memory management: relocate() leaks memory, because it has to construct - a fresh pathname. If this is a problem because your program calls - relocate() frequently, think about caching the result. */ - -/* Convenience function: - Computes the current installation prefix, based on the original - installation prefix, the original installation directory of a particular - file, and the current pathname of this file. Returns NULL upon failure. */ -extern const char * compute_curr_prefix (const char *orig_installprefix, - const char *orig_installdir, - const char *curr_pathname); - -#else - -/* By default, we use the hardwired pathnames. */ -#define relocate(pathname) (pathname) - -#endif - -#endif /* _RELOCATABLE_H */ diff --git a/src/audacious/intl/textdomain.c b/src/audacious/intl/textdomain.c deleted file mode 100644 index 6d3c192..0000000 --- a/src/audacious/intl/textdomain.c +++ /dev/null @@ -1,142 +0,0 @@ -/* Implementation of the textdomain(3) function. - Copyright (C) 1995-1998, 2000, 2001, 2002 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stdlib.h> -#include <string.h> - -#ifdef _LIBC -# include <libintl.h> -#else -# include "libgnuintl.h" -#endif -#include "gettextP.h" - -#ifdef _LIBC -/* We have to handle multi-threaded applications. */ -# include <bits/libc-lock.h> -#else -/* Provide dummy implementation if this is outside glibc. */ -# define __libc_rwlock_define(CLASS, NAME) -# define __libc_rwlock_wrlock(NAME) -# define __libc_rwlock_unlock(NAME) -#endif - -/* The internal variables in the standalone libintl.a must have different - names than the internal variables in GNU libc, otherwise programs - using libintl.a cannot be linked statically. */ -#if !defined _LIBC -# define _nl_default_default_domain libintl_nl_default_default_domain -# define _nl_current_default_domain libintl_nl_current_default_domain -#endif - -/* @@ end of prolog @@ */ - -/* Name of the default text domain. */ -extern const char _nl_default_default_domain[] attribute_hidden; - -/* Default text domain in which entries for gettext(3) are to be found. */ -extern const char *_nl_current_default_domain attribute_hidden; - - -/* Names for the libintl functions are a problem. They must not clash - with existing names and they should follow ANSI C. But this source - code is also used in GNU C Library where the names have a __ - prefix. So we have to make a difference here. */ -#ifdef _LIBC -# define TEXTDOMAIN __textdomain -# ifndef strdup -# define strdup(str) __strdup (str) -# endif -#else -# define TEXTDOMAIN libintl_textdomain -#endif - -/* Lock variable to protect the global data in the gettext implementation. */ -__libc_rwlock_define (extern, _nl_state_lock attribute_hidden) - -/* Set the current default message catalog to DOMAINNAME. - If DOMAINNAME is null, return the current default. - If DOMAINNAME is "", reset to the default of "messages". */ -char * -TEXTDOMAIN (domainname) - const char *domainname; -{ - char *new_domain; - char *old_domain; - - /* A NULL pointer requests the current setting. */ - if (domainname == NULL) - return (char *) _nl_current_default_domain; - - __libc_rwlock_wrlock (_nl_state_lock); - - old_domain = (char *) _nl_current_default_domain; - - /* If domain name is the null string set to default domain "messages". */ - if (domainname[0] == '\0' - || strcmp (domainname, _nl_default_default_domain) == 0) - { - _nl_current_default_domain = _nl_default_default_domain; - new_domain = (char *) _nl_current_default_domain; - } - else if (strcmp (domainname, old_domain) == 0) - /* This can happen and people will use it to signal that some - environment variable changed. */ - new_domain = old_domain; - else - { - /* If the following malloc fails `_nl_current_default_domain' - will be NULL. This value will be returned and so signals we - are out of core. */ -#if defined _LIBC || defined HAVE_STRDUP - new_domain = strdup (domainname); -#else - size_t len = strlen (domainname) + 1; - new_domain = (char *) malloc (len); - if (new_domain != NULL) - memcpy (new_domain, domainname, len); -#endif - - if (new_domain != NULL) - _nl_current_default_domain = new_domain; - } - - /* We use this possibility to signal a change of the loaded catalogs - since this is most likely the case and there is no other easy we - to do it. Do it only when the call was successful. */ - if (new_domain != NULL) - { - ++_nl_msg_cat_cntr; - - if (old_domain != new_domain && old_domain != _nl_default_default_domain) - free (old_domain); - } - - __libc_rwlock_unlock (_nl_state_lock); - - return new_domain; -} - -#ifdef _LIBC -/* Alias for function name in GNU C Library. */ -weak_alias (__textdomain, textdomain); -#endif diff --git a/src/audacious/main.c b/src/audacious/main.c index 3ce7c91..1d0ffce 100644 --- a/src/audacious/main.c +++ b/src/audacious/main.c @@ -1,5 +1,5 @@ /* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2007 Audacious development team. + * Copyright (C) 2005-2011 Audacious development team. * * Based on BMP: * Copyright (C) 2003-2004 BMP development team. @@ -23,53 +23,57 @@ * Audacious or using our public API to be a derived work. */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif +#include <errno.h> +#include <limits.h> #include <gtk/gtk.h> -#include "main.h" - -#include <glib/gprintf.h> - #include <libaudcore/audstrings.h> #include <libaudcore/hook.h> #include <libaudtag/audtag.h> +#include "config.h" + #ifdef USE_DBUS -# include "dbus-service.h" -# include "audctrl.h" +#include "audctrl.h" +#include "dbus-service.h" #endif #ifdef USE_EGGSM -#include "eggsmclient.h" #include "eggdesktopfile.h" +#include "eggsmclient.h" #endif #include "audconfig.h" -#include "chardet.h" -#include "compatibility.h" #include "configdb.h" #include "debug.h" #include "drct.h" #include "equalizer.h" +#include "glib-compat.h" #include "i18n.h" #include "interface.h" -#include "output.h" +#include "misc.h" #include "playback.h" #include "playlist.h" -#include "pluginenum.h" -#include "signals.h" +#include "plugins.h" #include "util.h" -#include "visualization.h" -#define AUTOSAVE_INTERVAL 300 /* seconds */ +/* chardet.c */ +void chardet_init (void); -static const gchar *application_name = N_("Audacious"); +/* mpris-signals.c */ +void mpris_signals_init (void); +void mpris_signals_cleanup (void); -struct _AudCmdLineOpt -{ +/* signals.c */ +void signals_init (void); + +/* smclient.c */ +void smclient_init (void); + +#define AUTOSAVE_INTERVAL 300 /* seconds */ + +static struct { gchar **filenames; gint session; gboolean play, stop, pause, fwd, rew, play_pause, show_jump_box; @@ -77,78 +81,166 @@ struct _AudCmdLineOpt gboolean enqueue_to_temp; gboolean version; gchar *previous_session_id; -}; -typedef struct _AudCmdLineOpt AudCmdLineOpt; +} options; -static AudCmdLineOpt options; +static gchar * aud_paths[AUD_PATH_COUNT]; -gchar * aud_paths[BMP_PATH_COUNT]; - -#ifdef USE_DBUS -MprisPlayer *mpris; -MprisTrackList *mpris_tracklist; +static void make_dirs(void) +{ +#ifdef S_IRGRP + const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; +#else + const mode_t mode755 = S_IRWXU; #endif -static void print_version(void) + make_directory(aud_paths[AUD_PATH_USER_DIR], mode755); + make_directory(aud_paths[AUD_PATH_USER_PLUGIN_DIR], mode755); + make_directory(aud_paths[AUD_PATH_PLAYLISTS_DIR], mode755); +} + +static void normalize_path (gchar * path) { - g_printf("%s %s (%s)\n", _(application_name), VERSION, BUILDSTAMP); +#ifdef _WIN32 + string_replace_char (path, '/', '\\'); +#endif + gint len = strlen (path); +#ifdef _WIN32 + if (len > 3 && path[len - 1] == '\\') /* leave "C:\" */ +#else + if (len > 1 && path[len - 1] == '/') /* leave leading "/" */ +#endif + path[len - 1] = 0; } -static void aud_make_user_dir(void) +static gchar * last_path_element (gchar * path) { - const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + gchar * slash = strrchr (path, G_DIR_SEPARATOR); + return (slash && slash[1]) ? slash + 1 : NULL; +} - make_directory(aud_paths[BMP_PATH_USER_DIR], mode755); - make_directory(aud_paths[BMP_PATH_USER_PLUGIN_DIR], mode755); - make_directory(aud_paths[BMP_PATH_USER_SKIN_DIR], mode755); - make_directory(aud_paths[BMP_PATH_SKIN_THUMB_DIR], mode755); - make_directory(aud_paths[BMP_PATH_PLAYLISTS_DIR], mode755); +static void strip_path_element (gchar * path, gchar * elem) +{ +#ifdef _WIN32 + if (elem > path + 3) +#else + if (elem > path + 1) +#endif + elem[-1] = 0; /* overwrite slash */ + else + elem[0] = 0; /* leave [drive letter and] leading slash */ } -static void aud_free_paths(void) +static void relocate_path (gchar * * pathp, const gchar * old, const gchar * new) { - gint i; + gchar * path = * pathp; + gint len = strlen (old); - for (i = 0; i < BMP_PATH_COUNT; i++) +#ifdef _WIN32 + if (strncasecmp (path, old, len)) +#else + if (strncmp (path, old, len)) +#endif { - g_free(aud_paths[i]); - aud_paths[i] = 0; + fprintf (stderr, "Failed to relocate a data path. Falling back to " + "compile-time path: %s\n", path); + return; } + + * pathp = g_strconcat (new, path + len, NULL); + g_free (path); } -static void aud_init_paths() +static void relocate_paths (void) { - gchar *xdg_config_home; - gchar *xdg_data_home; - gchar *xdg_cache_home; - - xdg_config_home = (getenv ("XDG_CONFIG_HOME") == NULL) ? g_build_filename - (getenv ("HOME"), ".config", NULL) : g_strdup (getenv ("XDG_CONFIG_HOME")); - xdg_data_home = (getenv ("XDG_DATA_HOME") == NULL) ? g_build_filename - (getenv ("HOME"), ".local", "share", NULL) : g_strdup (getenv - ("XDG_DATA_HOME")); - xdg_cache_home = (getenv ("XDG_CACHE_HOME") == NULL) ? g_build_filename - (getenv ("HOME"), ".cache", NULL) : g_strdup (getenv ("XDG_CACHE_HOME")); - - aud_paths[BMP_PATH_USER_DIR] = g_build_filename(xdg_config_home, "audacious", NULL); - aud_paths[BMP_PATH_USER_SKIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Skins", NULL); - aud_paths[BMP_PATH_USER_PLUGIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Plugins", NULL); + /* Start with the paths hard coded at compile time. */ + aud_paths[AUD_PATH_BIN_DIR] = g_strdup (HARDCODE_BINDIR); + aud_paths[AUD_PATH_DATA_DIR] = g_strdup (HARDCODE_DATADIR); + aud_paths[AUD_PATH_PLUGIN_DIR] = g_strdup (HARDCODE_PLUGINDIR); + aud_paths[AUD_PATH_LOCALE_DIR] = g_strdup (HARDCODE_LOCALEDIR); + aud_paths[AUD_PATH_DESKTOP_FILE] = g_strdup (HARDCODE_DESKTOPFILE); + aud_paths[AUD_PATH_ICON_FILE] = g_strdup (HARDCODE_ICONFILE); + normalize_path (aud_paths[AUD_PATH_BIN_DIR]); + normalize_path (aud_paths[AUD_PATH_DATA_DIR]); + normalize_path (aud_paths[AUD_PATH_PLUGIN_DIR]); + normalize_path (aud_paths[AUD_PATH_LOCALE_DIR]); + normalize_path (aud_paths[AUD_PATH_DESKTOP_FILE]); + normalize_path (aud_paths[AUD_PATH_ICON_FILE]); + + /* Compare the compile-time path to the executable and the actual path to + * see if we have been moved. */ + gchar * old = g_strdup (aud_paths[AUD_PATH_BIN_DIR]); + gchar * new = get_path_to_self (); + if (! new) + { +ERR: + g_free (old); + g_free (new); + return; + } + normalize_path (new); + + /* Strip the name of the executable file, leaving the path. */ + gchar * base = last_path_element (new); + if (! base) + goto ERR; + strip_path_element (new, base); + + /* Strip innermost folder names from both paths as long as they match. This + * leaves a compile-time prefix and a run-time one to replace it with. */ + gchar * a, * b; + while ((a = last_path_element (old)) && (b = last_path_element (new)) && +#ifdef _WIN32 + ! strcasecmp (a, b)) +#else + ! strcmp (a, b)) +#endif + { + strip_path_element (old, a); + strip_path_element (new, b); + } - aud_paths[BMP_PATH_SKIN_THUMB_DIR] = g_build_filename(xdg_cache_home, "audacious", "thumbs", NULL); + /* Do the replacements. */ + relocate_path (& aud_paths[AUD_PATH_BIN_DIR], old, new); + relocate_path (& aud_paths[AUD_PATH_DATA_DIR], old, new); + relocate_path (& aud_paths[AUD_PATH_PLUGIN_DIR], old, new); + relocate_path (& aud_paths[AUD_PATH_LOCALE_DIR], old, new); + relocate_path (& aud_paths[AUD_PATH_DESKTOP_FILE], old, new); + relocate_path (& aud_paths[AUD_PATH_ICON_FILE], old, new); - aud_paths[BMP_PATH_PLAYLISTS_DIR] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "playlists", NULL); + g_free (old); + g_free (new); +} - aud_paths[BMP_PATH_CONFIG_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "config", NULL); - aud_paths[BMP_PATH_PLAYLIST_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "playlist.xspf", NULL); - aud_paths[BMP_PATH_ACCEL_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "accels", NULL); +static void init_paths (void) +{ + relocate_paths (); + + const gchar * xdg_config_home = g_get_user_config_dir (); + const gchar * xdg_data_home = g_get_user_data_dir (); + +#ifdef _WIN32 + /* Some libraries (libmcs) and plugins (filewriter) use these variables, + * which are generally not set on Windows. */ + g_setenv ("HOME", g_get_home_dir (), TRUE); + g_setenv ("XDG_CONFIG_HOME", xdg_config_home, TRUE); + g_setenv ("XDG_DATA_HOME", xdg_data_home, TRUE); + g_setenv ("XDG_CACHE_HOME", g_get_user_cache_dir (), TRUE); +#endif - aud_paths[BMP_PATH_GTKRC_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "gtkrc", NULL); + aud_paths[AUD_PATH_USER_DIR] = g_build_filename(xdg_config_home, "audacious", NULL); + aud_paths[AUD_PATH_USER_PLUGIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Plugins", NULL); + aud_paths[AUD_PATH_PLAYLISTS_DIR] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "playlists", NULL); + aud_paths[AUD_PATH_PLAYLIST_FILE] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "playlist.xspf", NULL); + aud_paths[AUD_PATH_GTKRC_FILE] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "gtkrc", NULL); - g_free(xdg_config_home); - g_free(xdg_data_home); - g_free(xdg_cache_home); + for (gint i = 0; i < AUD_PATH_COUNT; i ++) + AUDDBG ("Data path: %s\n", aud_paths[i]); +} - g_atexit(aud_free_paths); +const gchar * get_path (gint id) +{ + g_return_val_if_fail (id >= 0 && id < AUD_PATH_COUNT, NULL); + return aud_paths[id]; } static GOptionEntry cmd_entries[] = { @@ -169,12 +261,12 @@ static GOptionEntry cmd_entries[] = { {NULL}, }; -static void parse_cmd_line_options(gint * argc, gchar *** argv) +static void parse_options (gint * argc, gchar *** argv) { GOptionContext *context; GError *error = NULL; - memset(&options, '\0', sizeof(AudCmdLineOpt)); + memset (& options, 0, sizeof options); options.session = -1; context = g_option_context_new(_("- play multimedia files")); @@ -185,123 +277,159 @@ static void parse_cmd_line_options(gint * argc, gchar *** argv) #endif if (!g_option_context_parse(context, argc, argv, &error)) - /* checking for MacOS X -psn_0_* errors */ - if (error->message && !g_strrstr(error->message, "-psn_0_")) - { - g_printerr(_("%s: %s\nTry `%s --help' for more information.\n"), (*argv)[0], error->message, (*argv)[0]); - exit(EXIT_FAILURE); - } + { + fprintf (stderr, + _("%s: %s\nTry `%s --help' for more information.\n"), (* argv)[0], + error->message, (* argv)[0]); + exit (EXIT_FAILURE); + } g_option_context_free (context); } -static void handle_cmd_line_filenames(gboolean is_running) +static gboolean get_lock (void) { - gint i; - gchar *working, **filenames = options.filenames; - GList * fns = NULL; -#ifdef USE_DBUS - DBusGProxy *session = audacious_get_dbus_proxy(); -#endif + gchar path[PATH_MAX]; + snprintf (path, sizeof path, "%s" G_DIR_SEPARATOR_S "lock", + aud_paths[AUD_PATH_USER_DIR]); - if (filenames == NULL) - return; + int handle = open (path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - working = g_get_current_dir(); - for (i = 0; filenames[i] != NULL; i++) + if (handle < 0) { - gchar * uri; + if (errno != EEXIST) + fprintf (stderr, "Cannot create %s: %s.\n", path, strerror (errno)); + return FALSE; + } - if (strstr (filenames[i], "://")) - uri = g_strdup (filenames[i]); - else if (g_path_is_absolute (filenames[i])) - uri = filename_to_uri (filenames[i]); - else - { - gchar * absolute = g_build_filename (working, filenames[i], NULL); - uri = filename_to_uri (absolute); - g_free (absolute); - } + close (handle); + return TRUE; +} - fns = g_list_prepend(fns, uri); - } - fns = g_list_reverse(fns); - g_free(working); +static void release_lock (void) +{ + gchar path[PATH_MAX]; + snprintf (path, sizeof path, "%s" G_DIR_SEPARATOR_S "lock", + aud_paths[AUD_PATH_USER_DIR]); -#ifdef USE_DBUS - if (is_running) - { - if (options.enqueue_to_temp) - audacious_remote_playlist_open_list_to_temp (session, fns); - else if (options.enqueue) - audacious_remote_playlist_add (session, fns); - else - audacious_remote_playlist_open_list (session, fns); - } - else /* !is_running */ -#endif + unlink (path); +} + +static GList * convert_filenames (void) +{ + if (! options.filenames) + return NULL; + + gchar * * f = options.filenames; + GList * list = NULL; + gchar * cur = g_get_current_dir (); + + for (gint i = 0; f[i]; i ++) { - if (options.enqueue_to_temp) - { - drct_pl_open_temp_list (fns); - cfg.resume_state = 0; - } - else if (options.enqueue) - drct_pl_add_list (fns, -1); + gchar * uri; + + if (strstr (f[i], "://")) + uri = g_strdup (f[i]); + else if (g_path_is_absolute (f[i])) + uri = filename_to_uri (f[i]); else { - drct_pl_open_list (fns); - cfg.resume_state = 0; + gchar * tmp = g_build_filename (cur, f[i], NULL); + uri = filename_to_uri (tmp); + g_free (tmp); } - } /* !is_running */ - g_list_foreach(fns, (GFunc) g_free, NULL); - g_list_free(fns); + list = g_list_prepend (list, uri); + } + + g_free (cur); + return g_list_reverse (list); } -static void handle_cmd_line_options_first(void) +static void do_remote (void) { #ifdef USE_DBUS - DBusGProxy *session; -#endif + DBusGProxy * session = audacious_get_dbus_proxy (); - if (options.version) + if (session && audacious_remote_is_running (session)) { - print_version(); - exit(EXIT_SUCCESS); - } + GList * list = convert_filenames (); + + if (list) + { + if (options.enqueue_to_temp) + audacious_remote_playlist_open_list_to_temp (session, list); + else if (options.enqueue) + audacious_remote_playlist_add (session, list); + else + audacious_remote_playlist_open_list (session, list); + + g_list_foreach (list, (GFunc) g_free, NULL); + g_list_free (list); + } -#ifdef USE_DBUS - session = audacious_get_dbus_proxy(); - if (audacious_remote_is_running(session)) - { - handle_cmd_line_filenames(TRUE); - if (options.rew) - audacious_remote_playlist_prev(session); if (options.play) - audacious_remote_play(session); + audacious_remote_play (session); if (options.pause) - audacious_remote_pause(session); + audacious_remote_pause (session); + if (options.play_pause) + audacious_remote_play_pause (session); if (options.stop) - audacious_remote_stop(session); + audacious_remote_stop (session); + if (options.rew) + audacious_remote_playlist_prev (session); if (options.fwd) - audacious_remote_playlist_next(session); - if (options.play_pause) - audacious_remote_play_pause(session); + audacious_remote_playlist_next (session); if (options.show_jump_box) - audacious_remote_show_jtf_box(session); - if (options.mainwin) - audacious_remote_main_win_toggle(session, 1); + audacious_remote_show_jtf_box (session); if (options.activate) - audacious_remote_activate(session); - exit(EXIT_SUCCESS); + audacious_remote_activate (session); + if (options.mainwin) + audacious_remote_main_win_toggle (session, TRUE); + + exit (EXIT_SUCCESS); } #endif + + GtkWidget * dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK_CANCEL, _("Audacious seems to be already running but is " + "not responding. You can start another instance of the program, but " + "please be warned that this can cause data loss. If Audacious is not " + "running, you can safely ignore this message. Press OK to start " + "Audacious or Cancel to quit.")); + + g_signal_connect (dialog, "destroy", (GCallback) gtk_widget_destroyed, + & dialog); + + if (gtk_dialog_run ((GtkDialog *) dialog) != GTK_RESPONSE_OK) + exit (EXIT_FAILURE); + + if (dialog) + gtk_widget_destroy (dialog); } -static void handle_cmd_line_options(void) +static void do_commands (void) { - handle_cmd_line_filenames(FALSE); + GList * list = convert_filenames (); + + if (list) + { + if (options.enqueue_to_temp) + { + drct_pl_open_temp_list (list); + cfg.resume_state = 0; + } + else if (options.enqueue) + drct_pl_add_list (list, -1); + else + { + drct_pl_open_list (list); + cfg.resume_state = 0; + } + + g_list_foreach (list, (GFunc) g_free, NULL); + g_list_free (list); + } if (cfg.resume_playback_on_startup && cfg.resume_state > 0) playback_play (cfg.resume_playback_on_startup_time, cfg.resume_state == @@ -321,199 +449,129 @@ static void handle_cmd_line_options(void) interface_toggle_visibility (); } -void aud_quit (void) +static void init_one (gint * p_argc, gchar * * * p_argv) { - AUDDBG ("Ending main loop.\n"); - gtk_main_quit (); -} - -static void shut_down (void) -{ - AUDDBG ("Saving configuration.\n"); - aud_config_save(); - save_playlists (); - - if (playback_get_playing ()) - playback_stop (); - - AUDDBG ("Shutting down user interface subsystem.\n"); - interface_unload (); - - output_cleanup (); - - AUDDBG ("Plugin subsystem shutdown.\n"); - plugin_system_cleanup(); + init_paths (); + make_dirs (); - cfg_db_flush (); /* must be after plugin cleanup */ + bindtextdomain (PACKAGE_NAME, aud_paths[AUD_PATH_LOCALE_DIR]); + bind_textdomain_codeset (PACKAGE_NAME, "UTF-8"); + bindtextdomain (PACKAGE_NAME "-plugins", aud_paths[AUD_PATH_LOCALE_DIR]); + bind_textdomain_codeset (PACKAGE_NAME "-plugins", "UTF-8"); + textdomain (PACKAGE_NAME); - AUDDBG ("Playlist cleanup.\n"); - playlist_end(); -} + mowgli_init (); + chardet_init (); -#ifdef USE_DBUS -static void mpris_status_cb1(gpointer hook_data, gpointer user_data) -{ - mpris_emit_status_change(mpris, GPOINTER_TO_INT(user_data)); -} + g_thread_init (NULL); + gdk_threads_init (); + gdk_threads_enter (); -static void mpris_status_cb2(gpointer hook_data, gpointer user_data) -{ - mpris_emit_status_change(mpris, -1); -} + gtk_rc_add_default_file (aud_paths[AUD_PATH_GTKRC_FILE]); + gtk_init (p_argc, p_argv); -void init_playback_hooks(void) -{ - hook_associate("playback begin", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_PLAY)); - hook_associate("playback pause", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_PAUSE)); - hook_associate("playback unpause", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_PLAY)); - hook_associate("playback stop", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_STOP)); - - hook_associate("playback shuffle", mpris_status_cb2, NULL); - hook_associate("playback repeat", mpris_status_cb2, NULL); - hook_associate("playback no playlist advance", mpris_status_cb2, NULL); -} +#ifdef USE_EGGSM + egg_sm_client_set_mode (EGG_SM_CLIENT_MODE_NORMAL); + egg_set_desktop_file (aud_paths[AUD_PATH_DESKTOP_FILE]); #endif - -static gboolean autosave_cb (void * unused) -{ - AUDDBG ("Saving configuration.\n"); - aud_config_save (); - cfg_db_flush (); - save_playlists (); - return TRUE; -} - -static PluginHandle * current_iface = NULL; - -PluginHandle * iface_plugin_get_active (void) -{ - return current_iface; } -void iface_plugin_set_active (PluginHandle * plugin) +static void init_two (void) { - AUDDBG ("Unloading visualizers.\n"); - vis_cleanup (); - - AUDDBG ("Unloading %s.\n", plugin_get_name (current_iface)); - interface_unload (); + hook_init (); + tag_init (); - current_iface = plugin; - interface_set_default (plugin); + aud_config_load (); + tag_set_verbose (cfg.verbose); + vfs_set_verbose (cfg.verbose); - AUDDBG ("Starting %s.\n", plugin_get_name (plugin)); - if (! interface_load (plugin)) - { - fprintf (stderr, "%s failed to start.\n", plugin_get_name (plugin)); - exit (EXIT_FAILURE); - } - - AUDDBG ("Loading visualizers.\n"); - vis_init (); -} - -gint main(gint argc, gchar ** argv) -{ - /* glib-2.13.0 requires g_thread_init() to be called before all - other GLib functions */ - g_thread_init(NULL); - if (!g_thread_supported()) - { - g_printerr(_("Sorry, threads aren't supported on your platform.\n")); - exit(EXIT_FAILURE); - } + eq_init (); + register_interface_hooks (); - gdk_threads_init(); - mowgli_init(); - chardet_init(); - tag_init(); +#ifdef HAVE_SIGWAIT + signals_init (); +#endif +#ifdef USE_EGGSM + smclient_init (); +#endif - hook_init(); - hook_associate ("quit", (HookFunction) gtk_main_quit, NULL); + AUDDBG ("Loading lowlevel plugins.\n"); + start_plugins_one (); - /* Setup l10n early so we can print localized error messages */ - gtk_set_locale(); - bindtextdomain(PACKAGE_NAME, LOCALEDIR); - bind_textdomain_codeset(PACKAGE_NAME, "UTF-8"); - bindtextdomain(PACKAGE_NAME "-plugins", LOCALEDIR); - bind_textdomain_codeset(PACKAGE_NAME "-plugins", "UTF-8"); - textdomain(PACKAGE_NAME); + playlist_init (); + load_playlists (); -#if !defined(_WIN32) && defined(USE_EGGSM) - egg_set_desktop_file(AUDACIOUS_DESKTOP_FILE); +#ifdef USE_DBUS + init_dbus (); #endif - aud_init_paths(); - aud_make_user_dir(); - gtk_rc_add_default_file(aud_paths[BMP_PATH_GTKRC_FILE]); + do_commands (); - parse_cmd_line_options(&argc, &argv); + AUDDBG ("Loading highlevel plugins.\n"); + start_plugins_two (); - if (!gtk_init_check(&argc, &argv)) - { /* XXX */ - /* GTK check failed, and no arguments passed to indicate - that user is intending to only remote control a running - session */ - g_printerr(_("%s: Unable to open display, exiting.\n"), argv[0]); - exit(EXIT_FAILURE); - } + mpris_signals_init (); +} - AUDDBG ("Loading configuration.\n"); - aud_config_load(); - atexit (aud_config_free); +static void shut_down (void) +{ + mpris_signals_cleanup (); - AUDDBG ("Initializing signal handlers.\n"); - signal_handlers_init(); + AUDDBG ("Capturing state.\n"); + aud_config_save (); + save_playlists (); - AUDDBG ("Handling commandline options, part #1.\n"); - handle_cmd_line_options_first(); + AUDDBG ("Unloading highlevel plugins.\n"); + stop_plugins_two (); - output_init (); + AUDDBG ("Stopping playback.\n"); + if (playback_get_playing ()) + playback_stop (); -#ifdef USE_DBUS - AUDDBG ("Initializing D-Bus.\n"); - init_dbus(); - init_playback_hooks(); -#endif + playlist_end (); - AUDDBG ("Initializing plugin subsystems.\n"); - plugin_system_init(); + AUDDBG ("Unloading lowlevel plugins.\n"); + stop_plugins_one (); - playlist_init (); - load_playlists (); - eq_init (); + AUDDBG ("Saving configuration.\n"); + cfg_db_flush (); - AUDDBG ("Handling commandline options, part #2.\n"); - handle_cmd_line_options(); + gdk_threads_leave (); +} - AUDDBG ("Registering interface hooks.\n"); - register_interface_hooks(); +static gboolean autosave_cb (void * unused) +{ + AUDDBG ("Saving configuration.\n"); + aud_config_save (); + cfg_db_flush (); + save_playlists (); + return TRUE; +} - g_timeout_add_seconds (AUTOSAVE_INTERVAL, autosave_cb, NULL); +gint main(gint argc, gchar ** argv) +{ + init_one (& argc, & argv); + parse_options (& argc, & argv); - if ((current_iface = interface_get_default ()) == NULL) + if (options.version) { - fprintf (stderr, "No interface plugin found.\n"); - return EXIT_FAILURE; + printf ("%s %s (%s)\n", _("Audacious"), VERSION, BUILDSTAMP); + return EXIT_SUCCESS; } - AUDDBG ("Starting %s.\n", plugin_get_name (current_iface)); - if (! interface_load (current_iface)) - { - fprintf (stderr, "%s failed to start.\n", plugin_get_name (current_iface)); - return EXIT_FAILURE; - } + if (! get_lock ()) + do_remote (); /* may exit */ - AUDDBG ("Loading visualizers.\n"); - vis_init (); + AUDDBG ("No remote session; starting up.\n"); + init_two (); - AUDDBG ("Starting main loop.\n"); - gtk_main (); + AUDDBG ("Startup complete.\n"); + g_timeout_add_seconds (AUTOSAVE_INTERVAL, autosave_cb, NULL); + hook_associate ("quit", (HookFunction) gtk_main_quit, NULL); - AUDDBG ("Unloading visualizers.\n"); - vis_cleanup (); + gtk_main (); - AUDDBG ("Shutting down.\n"); shut_down (); + release_lock (); return EXIT_SUCCESS; } diff --git a/src/audacious/main.h b/src/audacious/main.h deleted file mode 100644 index e280066..0000000 --- a/src/audacious/main.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2007 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ - -#ifndef AUDACIOUS_MAIN_H -#define AUDACIOUS_MAIN_H - -#include <glib.h> - -#define NOT_ALPHA_RELEASE - -#ifdef USE_DBUS -#include "dbus-service.h" -#endif - -enum { - BMP_PATH_USER_DIR, - BMP_PATH_USER_PLUGIN_DIR, - BMP_PATH_USER_SKIN_DIR, - BMP_PATH_SKIN_THUMB_DIR, - BMP_PATH_PLAYLISTS_DIR, - BMP_PATH_ACCEL_FILE, - BMP_PATH_CONFIG_FILE, - BMP_PATH_PLAYLIST_FILE, - BMP_PATH_GTKRC_FILE, - BMP_PATH_COUNT -}; - -extern gchar *aud_paths[]; - -#ifdef USE_DBUS -extern MprisPlayer *mpris; -extern MprisTrackList *mpris_tracklist; -#endif - -void aud_quit(void); - -#endif /* AUDACIOUS_MAIN_H */ diff --git a/src/audacious/misc-api.h b/src/audacious/misc-api.h index ec045c5..7dd2a3e 100644 --- a/src/audacious/misc-api.h +++ b/src/audacious/misc-api.h @@ -36,9 +36,8 @@ AUD_FUNC2 (gboolean, save_preset_file, EqualizerPreset *, preset, const gchar *, filename) AUD_FUNC1 (GList *, import_winamp_eqf, VFSFile *, file) -/* playlist_container.c */ -AUD_FUNC1 (void, playlist_container_register, PlaylistContainer *, container) -AUD_FUNC1 (void, playlist_container_unregister, PlaylistContainer *, container) +/* main.c */ +AUD_FUNC1 (const gchar *, get_path, gint, path) /* playlist-utils.c */ AUD_FUNC0 (const gchar *, get_gentitle_format) @@ -49,17 +48,17 @@ AUD_FUNC2 (void, uri_set_plugin, const gchar *, scheme, InputPlugin *, ip) AUD_FUNC2 (void, mime_set_plugin, const gchar *, mimetype, InputPlugin *, ip) /* probe.c */ -AUD_FUNC2 (InputPlugin *, file_find_decoder, const gchar *, filename, gboolean, +AUD_FUNC2 (PluginHandle *, file_find_decoder, const gchar *, filename, gboolean, fast) -AUD_FUNC2 (Tuple *, file_read_tuple, const gchar *, filename, InputPlugin *, +AUD_FUNC2 (Tuple *, file_read_tuple, const gchar *, filename, PluginHandle *, decoder) -AUD_FUNC4 (gboolean, file_read_image, const gchar *, filename, InputPlugin *, +AUD_FUNC4 (gboolean, file_read_image, const gchar *, filename, PluginHandle *, decoder, void * *, data, gint *, size) AUD_FUNC2 (gboolean, file_can_write_tuple, const gchar *, filename, - InputPlugin *, decoder) -AUD_FUNC3 (gboolean, file_write_tuple, const gchar *, filename, InputPlugin *, + PluginHandle *, decoder) +AUD_FUNC3 (gboolean, file_write_tuple, const gchar *, filename, PluginHandle *, decoder, const Tuple *, tuple) -AUD_FUNC2 (gboolean, custom_infowin, const gchar *, filename, InputPlugin *, +AUD_FUNC2 (gboolean, custom_infowin, const gchar *, filename, PluginHandle *, decoder) /* ui_albumart.c */ @@ -92,3 +91,7 @@ AUD_FUNC3 (void, calc_mono_pcm, VisPCMData, buffer, const VisPCMData, data, gint, channels) AUD_FUNC3 (void, calc_stereo_pcm, VisPCMData, buffer, const VisPCMData, data, gint, channels) + +/* New in 2.5-alpha2 */ +AUD_FUNC1 (void, interface_install_toolbar, void *, button) +AUD_FUNC1 (void, interface_uninstall_toolbar, void *, button) diff --git a/src/audacious/misc.h b/src/audacious/misc.h index 6a4b673..386f974 100644 --- a/src/audacious/misc.h +++ b/src/audacious/misc.h @@ -38,17 +38,26 @@ enum { AUDACIOUS_MENU_PLAYLIST_MISC, TOTAL_PLUGIN_MENUS}; +enum { + AUD_PATH_BIN_DIR, + AUD_PATH_DATA_DIR, + AUD_PATH_PLUGIN_DIR, + AUD_PATH_LOCALE_DIR, + AUD_PATH_DESKTOP_FILE, + AUD_PATH_ICON_FILE, + AUD_PATH_USER_DIR, + AUD_PATH_USER_PLUGIN_DIR, + AUD_PATH_PLAYLISTS_DIR, + AUD_PATH_PLAYLIST_FILE, + AUD_PATH_GTKRC_FILE, + AUD_PATH_COUNT +}; + typedef struct { gchar * name; gfloat preamp, bands[10]; } EqualizerPreset; -typedef struct { - const gchar * name, * ext; - void (* plc_read) (const gchar * filename, gint at); - void (* plc_write) (const gchar * filename, gint at); -} PlaylistContainer; - typedef gint16 VisFreqData[2][256]; typedef gint16 VisPCMData[2][512]; diff --git a/src/audacious/mpris-signals.c b/src/audacious/mpris-signals.c new file mode 100644 index 0000000..51dda6b --- /dev/null +++ b/src/audacious/mpris-signals.c @@ -0,0 +1,71 @@ +/* Audacious - Cross-platform multimedia player + * Copyright (C) 2005-2011 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 <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#ifdef USE_DBUS + +#include "dbus-service.h" + +static void mpris_status_cb1 (void * hook_data, void * user_data) +{ + mpris_emit_status_change (mpris, GPOINTER_TO_INT (user_data)); +} + +static void mpris_status_cb2 (void * hook_data, void * user_data) +{ + mpris_emit_status_change (mpris, -1); +} +#endif + +void mpris_signals_init (void) +{ +#ifdef USE_DBUS + hook_associate ("playback begin", mpris_status_cb1, GINT_TO_POINTER + (MPRIS_STATUS_PLAY)); + hook_associate ("playback pause", mpris_status_cb1, GINT_TO_POINTER + (MPRIS_STATUS_PAUSE)); + hook_associate ("playback unpause", mpris_status_cb1, GINT_TO_POINTER + (MPRIS_STATUS_PLAY)); + hook_associate ("playback stop", mpris_status_cb1, GINT_TO_POINTER + (MPRIS_STATUS_STOP)); + + hook_associate ("toggle shuffle", mpris_status_cb2, NULL); + hook_associate ("toggle repeat", mpris_status_cb2, NULL); +/* hook_associate ("toggle no playlist advance", mpris_status_cb2, NULL); */ +#endif +} + +void mpris_signals_cleanup (void) +{ +#ifdef USE_DBUS + hook_dissociate ("playback begin", mpris_status_cb1); + hook_dissociate ("playback pause", mpris_status_cb1); + hook_dissociate ("playback unpause", mpris_status_cb1); + hook_dissociate ("playback stop", mpris_status_cb1); + + hook_dissociate ("toggle shuffle", mpris_status_cb2, NULL); + hook_dissociate ("toggle repeat", mpris_status_cb2, NULL); +/* hook_dissociate ("toggle no playlist advance", mpris_status_cb2, NULL); */ +#endif +} diff --git a/src/audacious/output.c b/src/audacious/output.c index cfa8572..a38eae3 100644 --- a/src/audacious/output.c +++ b/src/audacious/output.c @@ -20,7 +20,6 @@ */ #include <math.h> - #include <libaudcore/audio.h> #include "audconfig.h" @@ -34,30 +33,7 @@ #define SW_VOLUME_RANGE 40 /* decibels */ -OutputPlugin * current_output_plugin = NULL; -#define COP current_output_plugin - -static gboolean plugin_list_func (PluginHandle * plugin, GList * * list) -{ - OutputPlugin * op = plugin_get_header (plugin); - g_return_val_if_fail (op != NULL, TRUE); - * list = g_list_prepend (* list, op); - return TRUE; -} - -GList * get_output_list (void) -{ - static GList * list = NULL; - - if (list == NULL) - { - plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) - plugin_list_func, & list); - list = g_list_reverse (list); - } - - return list; -} +static OutputPlugin * cop = NULL; void output_get_volume (gint * l, gint * r) { @@ -66,8 +42,8 @@ void output_get_volume (gint * l, gint * r) * l = cfg.sw_volume_left; * r = cfg.sw_volume_right; } - else if (COP != NULL && COP->get_volume != NULL) - COP->get_volume (l, r); + else if (cop != NULL && cop->get_volume != NULL) + cop->get_volume (l, r); else { * l = 0; @@ -82,98 +58,80 @@ void output_set_volume (gint l, gint r) cfg.sw_volume_left = l; cfg.sw_volume_right = r; } - else if (COP != NULL && COP->set_volume != NULL) - COP->set_volume (l, r); + else if (cop != NULL && cop->set_volume != NULL) + cop->set_volume (l, r); } -static GMutex * output_mutex; -static gboolean output_opened, output_aborted, output_leave_open, output_paused; +static GStaticMutex mutex = G_STATIC_MUTEX_INIT; +static gboolean locked = FALSE; -static gint decoder_format, output_format; -static gint decoder_channels, decoder_rate, effect_channels, effect_rate, - output_channels, output_rate; +#define LOCK do {g_static_mutex_lock (& mutex); locked = TRUE;} while (0) +#define UNLOCK do {locked = FALSE; g_static_mutex_unlock (& mutex);} while (0) +#define LOCKED g_return_if_fail (locked) +#define LOCKED_RET(a) g_return_val_if_fail (locked, a) +#define LOCK_VIS do {vis_runner_lock (); LOCK;} while (0) +#define UNLOCK_VIS do {UNLOCK; vis_runner_unlock ();} while (0) +#define LOCKED_VIS g_return_if_fail (locked && vis_runner_locked ()) +#define LOCKED_VIS_RET(a) g_return_val_if_fail (locked && vis_runner_locked (), a) + +static gboolean opened = FALSE; +static gboolean leave_open = FALSE; + +static gboolean waiting, aborted, paused; +static gint decoder_format, decoder_channels, decoder_rate, effect_channels, + effect_rate, output_format, output_channels, output_rate; static gint64 frames_written; static gboolean have_replay_gain; static ReplayGainInfo replay_gain_info; -#define REMOVE_SOURCE(s) \ -do { \ - if (s != 0) { \ - g_source_remove (s); \ - s = 0; \ - } \ -} while (0) - -#define LOCK g_mutex_lock (output_mutex) -#define UNLOCK g_mutex_unlock (output_mutex) - -static void write_buffers (void); -static void drain (void); - -/* output_mutex must be locked */ -static void real_close (void) +static void reset_time (void) { - vis_runner_start_stop (FALSE, FALSE); - COP->close_audio (); - output_opened = FALSE; - output_leave_open = FALSE; + LOCKED_VIS; + g_return_if_fail (cop->set_written_time != NULL); + vis_runner_time_offset (- cop->written_time ()); + cop->set_written_time (0); } -void output_init (void) +static void drain (void) { - output_mutex = g_mutex_new (); - output_opened = FALSE; - output_leave_open = FALSE; + LOCKED; + g_return_if_fail (cop->drain != NULL); + cop->drain (); } -void output_cleanup (void) +static void real_close (void) { - LOCK; - - if (output_leave_open) - real_close (); - - UNLOCK; - - g_mutex_free (output_mutex); + LOCKED_VIS; + vis_runner_start_stop (FALSE, FALSE); + cop->close_audio (); + opened = FALSE; + leave_open = FALSE; } -static gboolean output_open_audio (gint format, gint rate, gint channels) +static gboolean open_audio (gint format, gint rate, gint channels) { - if (COP == NULL) - { - fprintf (stderr, "No output plugin selected.\n"); - return FALSE; - } - - LOCK; - - if (output_leave_open && COP->set_written_time != NULL) - { - vis_runner_time_offset (- COP->written_time ()); - COP->set_written_time (0); - } + LOCKED_VIS_RET (FALSE); + g_return_val_if_fail (! opened, FALSE); decoder_format = format; decoder_channels = channels; decoder_rate = rate; - frames_written = 0; - effect_channels = channels; effect_rate = rate; effect_start (& effect_channels, & effect_rate); eq_set_format (effect_channels, effect_rate); - if (output_leave_open && COP->set_written_time != NULL && effect_channels == - output_channels && effect_rate == output_rate) - output_opened = TRUE; + if (leave_open && effect_channels == output_channels && effect_rate == + output_rate) + { + reset_time (); + opened = TRUE; + } else { - if (output_leave_open) + if (leave_open) { - UNLOCK; drain (); - LOCK; real_close (); } @@ -183,90 +141,37 @@ static gboolean output_open_audio (gint format, gint rate, gint channels) output_channels = effect_channels; output_rate = effect_rate; - if (COP->open_audio (output_format, output_rate, output_channels) > 0) + if (cop->open_audio (output_format, output_rate, output_channels)) { vis_runner_start_stop (TRUE, FALSE); - output_opened = TRUE; + opened = TRUE; } } - output_aborted = FALSE; - output_leave_open = FALSE; - output_paused = FALSE; - - UNLOCK; - return output_opened; -} - -static void output_close_audio (void) -{ - LOCK; - - output_opened = FALSE; - - if (! output_leave_open) - { - effect_flush (); - real_close (); - } - - UNLOCK; -} - -static void output_flush (gint time) -{ - LOCK; - - frames_written = time * (gint64) decoder_rate / 1000; - output_aborted = FALSE; - - vis_runner_flush (); - effect_flush (); - COP->flush (effect_decoder_to_output_time (time)); + leave_open = FALSE; + waiting = FALSE; + aborted = FALSE; + paused = FALSE; + frames_written = 0; + have_replay_gain = FALSE; - UNLOCK; + return opened; } -static void output_pause (gboolean pause) +static gboolean output_open_audio (gint format, gint rate, gint channels) { - LOCK; - COP->pause (pause); - vis_runner_start_stop (TRUE, pause); - output_paused = pause; - UNLOCK; + g_return_val_if_fail (cop != NULL, FALSE); + LOCK_VIS; + gboolean success = open_audio (format, rate, channels); + UNLOCK_VIS; + return success; } -static gint get_written_time (void) +static void set_gain (ReplayGainInfo * info) { - gint time = 0; + LOCKED; + g_return_if_fail (opened && ! waiting); - LOCK; - - if (output_opened) - time = frames_written * (gint64) 1000 / decoder_rate; - - UNLOCK; - return time; -} - -static gboolean output_buffer_playing (void) -{ - LOCK; - - if (! output_paused) - { - UNLOCK; - write_buffers (); - LOCK; - output_leave_open = TRUE; - } - - UNLOCK; - return FALSE; -} - -static void output_set_replaygain_info (ReplayGainInfo * info) -{ AUDDBG ("Replay Gain info:\n"); AUDDBG (" album gain: %f dB\n", info->album_gain); AUDDBG (" album peak: %f\n", info->album_peak); @@ -277,6 +182,14 @@ static void output_set_replaygain_info (ReplayGainInfo * info) memcpy (& replay_gain_info, info, sizeof (ReplayGainInfo)); } +static void output_set_replaygain_info (ReplayGainInfo * info) +{ + g_return_if_fail (cop != NULL); + LOCK; + set_gain (info); + UNLOCK; +} + static void apply_replay_gain (gfloat * data, gint samples) { gfloat factor = powf (10, (gfloat) cfg.replay_gain_preamp / 20); @@ -339,80 +252,70 @@ static void apply_software_volume (gfloat * data, gint channels, gint frames) audio_amplify (data, channels, frames, factors); } -static void do_write (void * data, gint samples) +static void write_processed (void * data, gint samples) { + LOCKED_VIS; + if (! samples) return; - void * allocated = NULL; - - vis_runner_pass_audio (COP->written_time (), data, samples, output_channels, + vis_runner_pass_audio (cop->written_time (), data, samples, output_channels, output_rate); eq_filter (data, samples); apply_software_volume (data, output_channels, samples / output_channels); + void * allocated = NULL; + if (output_format != FMT_FLOAT) { void * new = g_malloc (FMT_SIZEOF (output_format) * samples); - audio_to_int (data, new, output_format, samples); - data = new; g_free (allocated); allocated = new; } - while (1) + while (! aborted) { - gint ready; - - if (COP->buffer_free) - ready = COP->buffer_free () / FMT_SIZEOF (output_format); - else - ready = output_channels * (output_rate / 50); - - LOCK; - - if (output_aborted) - { - UNLOCK; - break; - } - - UNLOCK; - + gint ready = (cop->buffer_free != NULL) ? cop->buffer_free () / + FMT_SIZEOF (output_format) : output_channels * (output_rate / 50); ready = MIN (ready, samples); - COP->write_audio (data, FMT_SIZEOF (output_format) * ready); + cop->write_audio (data, FMT_SIZEOF (output_format) * ready); data = (char *) data + FMT_SIZEOF (output_format) * ready; samples -= ready; if (! samples) break; - if (COP->period_wait) - COP->period_wait (); - else if (COP->buffer_free) + waiting = TRUE; + UNLOCK_VIS; + + if (cop->period_wait != NULL) + cop->period_wait (); + else if (cop->buffer_free != NULL) g_usleep (20000); + + LOCK_VIS; + waiting = FALSE; } g_free (allocated); } -static void output_write_audio (void * data, gint size) +static void write_audio (void * data, gint size) { - gint samples = size / FMT_SIZEOF (decoder_format); - void * allocated = NULL; + LOCKED; + g_return_if_fail (opened && ! waiting); - LOCK; + gint samples = size / FMT_SIZEOF (decoder_format); frames_written += samples / decoder_channels; - UNLOCK; + + void * allocated = NULL; if (decoder_format != FMT_FLOAT) { gfloat * new = g_malloc (sizeof (gfloat) * samples); - audio_from_int (data, decoder_format, new, samples); - data = new; g_free (allocated); allocated = new; @@ -429,39 +332,153 @@ static void output_write_audio (void * data, gint size) allocated = NULL; } - do_write (data, samples); + write_processed (data, samples); g_free (allocated); } -static void write_buffers (void) +static void output_write_audio (void * data, gint size) { - gfloat * data = NULL; - gint samples = 0; + g_return_if_fail (cop != NULL); + LOCK_VIS; + write_audio (data, size); + UNLOCK_VIS; +} - effect_finish (& data, & samples); - do_write (data, samples); +static void close_audio (void) +{ + LOCKED; + g_return_if_fail (opened && ! waiting); + opened = FALSE; + + if (! leave_open) + { + effect_flush (); + real_close (); + } } -static void abort_write (void) +static void output_close_audio (void) +{ + g_return_if_fail (cop != NULL); + LOCK_VIS; + close_audio (); + UNLOCK_VIS; +} + +static void do_pause (gboolean p) +{ + LOCKED_VIS; + g_return_if_fail (opened); + cop->pause (p); + vis_runner_start_stop (TRUE, p); + paused = p; +} + +static void output_pause (gboolean p) { + g_return_if_fail (cop != NULL); + LOCK_VIS; + do_pause (p); + UNLOCK_VIS; +} + +static void flush (gint time) +{ + LOCKED_VIS; + g_return_if_fail (opened); + + aborted = FALSE; + + /* When playback is started from the middle of a song, flush() is called + * before any audio is actually written in order to set the time counter. + * In this case, we do not want to cut off the end of the previous song, so + * we do not actually flush. */ + if (! frames_written) + { + g_return_if_fail (cop->set_written_time != NULL); + cop->set_written_time (time); + } + else + { + vis_runner_flush (); + effect_flush (); + cop->flush (effect_decoder_to_output_time (time)); + } + + frames_written = time * (gint64) decoder_rate / 1000; +} + +static void output_flush (gint time) +{ + g_return_if_fail (cop != NULL); + LOCK_VIS; + flush (time); + UNLOCK_VIS; +} + +static gint written_time (void) +{ + LOCKED_RET (0); + g_return_val_if_fail (opened && ! waiting, 0); + return frames_written * (gint64) 1000 / decoder_rate; +} + +static gint output_written_time (void) +{ + g_return_val_if_fail (cop != NULL, 0); LOCK; - output_aborted = TRUE; - COP->flush (COP->output_time ()); /* signal wait to return immediately */ + gint time = written_time (); UNLOCK; + return time; } -static void drain (void) +static void write_buffers (void) +{ + LOCKED; + gfloat * data = NULL; + gint samples = 0; + effect_finish (& data, & samples); + write_processed (data, samples); +} + +static void set_leave_open (void) { - if (COP->buffer_playing != NULL) + LOCKED; + g_return_if_fail (opened && ! waiting); + + if (! paused) { - while (COP->buffer_playing ()) - g_usleep (30000); + write_buffers (); + leave_open = TRUE; } - else - COP->drain (); } -struct OutputAPI output_api = +static gboolean output_buffer_playing (void) +{ + g_return_val_if_fail (cop != NULL, FALSE); + LOCK_VIS; + set_leave_open (); + UNLOCK_VIS; + return FALSE; +} + +static void abort_write (void) +{ + LOCKED; + g_return_if_fail (opened); + aborted = TRUE; + cop->flush (cop->output_time ()); +} + +static void output_abort_write (void) +{ + g_return_if_fail (cop != NULL); + LOCK; + abort_write (); + UNLOCK; +} + +const struct OutputAPI output_api = { .open_audio = output_open_audio, .set_replaygain_info = output_set_replaygain_info, @@ -470,20 +487,27 @@ struct OutputAPI output_api = .pause = output_pause, .flush = output_flush, - .written_time = get_written_time, + .written_time = output_written_time, .buffer_playing = output_buffer_playing, - .abort_write = abort_write, + .abort_write = output_abort_write, }; -gint get_output_time (void) +static gint output_time (void) { - gint time = 0; + LOCKED_RET (0); + g_return_val_if_fail (opened || leave_open, 0); + return cop->output_time (); +} +gint get_output_time (void) +{ + g_return_val_if_fail (cop != NULL, 0); LOCK; - if (output_opened) + gint time = 0; + if (opened) { - time = effect_output_to_decoder_time (COP->output_time ()); + time = effect_output_to_decoder_time (output_time ()); time = MAX (0, time); } @@ -491,61 +515,80 @@ gint get_output_time (void) return time; } -void output_drain (void) +gint get_raw_output_time (void) { + g_return_val_if_fail (cop != NULL, 0); LOCK; + gint time = output_time (); + UNLOCK; + return time; +} - if (output_leave_open) +void output_drain (void) +{ + g_return_if_fail (cop != NULL); + LOCK_VIS; + + if (leave_open) { - UNLOCK; - write_buffers (); /* tell effect plugins this is the last song */ + write_buffers (); drain (); - LOCK; real_close (); } - UNLOCK; + UNLOCK_VIS; } -void set_current_output_plugin (OutputPlugin * plugin) +static gboolean probe_cb (PluginHandle * p, PluginHandle * * pp) { - OutputPlugin * old = COP; - gboolean playing = playback_get_playing (); - gboolean paused = FALSE; - gint time = 0; + OutputPlugin * op = plugin_get_header (p); + g_return_val_if_fail (op != NULL && op->init != NULL, TRUE); - if (playing) - { - paused = playback_get_paused (); - time = playback_get_time (); - playback_stop (); - } + if (! op->init ()) + return TRUE; + + if (op->cleanup != NULL) + op->cleanup (); + + * pp = p; + return FALSE; +} - /* This function is also used to restart playback (for example, when - resampling is switched on or off), in which case we don't need to do an - init cycle. -jlindgren */ - if (plugin != COP) +PluginHandle * output_plugin_probe (void) +{ + PluginHandle * p = NULL; + plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) probe_cb, & p); + return p; +} + +PluginHandle * output_plugin_get_current (void) +{ + return (cop != NULL) ? plugin_by_header (cop) : NULL; +} + +gboolean output_plugin_set_current (PluginHandle * plugin) +{ + if (cop != NULL) { - COP = NULL; + if (playback_get_playing ()) + playback_stop (); - if (old != NULL && old->cleanup != NULL) - old->cleanup (); + if (cop->cleanup != NULL) + cop->cleanup (); - if (plugin->init () == OUTPUT_PLUGIN_INIT_FOUND_DEVICES) - COP = plugin; - else - { - fprintf (stderr, "Output plugin failed to load: %s\n", - plugin->description); + cop = NULL; + } - if (old == NULL || old->init () != OUTPUT_PLUGIN_INIT_FOUND_DEVICES) - return; + if (plugin != NULL) + { + OutputPlugin * op = plugin_get_header (plugin); + g_return_val_if_fail (op != NULL && op->init != NULL, FALSE); - fprintf (stderr, "Falling back to: %s\n", old->description); - COP = old; - } + if (! op->init ()) + return FALSE; + + cop = op; } - if (playing) - playback_play (time, paused); + return TRUE; } diff --git a/src/audacious/output.h b/src/audacious/output.h index 8399f5f..c7525d5 100644 --- a/src/audacious/output.h +++ b/src/audacious/output.h @@ -30,17 +30,17 @@ #include "plugin.h" -extern struct OutputAPI output_api; -extern OutputPlugin * current_output_plugin; +extern const struct OutputAPI output_api; -GList *get_output_list(void); void output_get_volume(gint * l, gint * r); void output_set_volume(gint l, gint r); -void output_init (void); -void output_cleanup (void); -void set_current_output_plugin (OutputPlugin * plugin); gint get_output_time (void); +gint get_raw_output_time (void); void output_drain (void); +PluginHandle * output_plugin_probe (void); +PluginHandle * output_plugin_get_current (void); +gboolean output_plugin_set_current (PluginHandle * plugin); + #endif /* AUDACIOUS_OUTPUT_H */ diff --git a/src/audacious/playback.c b/src/audacious/playback.c index 6d31652..9aa239b 100644 --- a/src/audacious/playback.c +++ b/src/audacious/playback.c @@ -1,29 +1,26 @@ -/* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2009 Audacious development team +/* + * playback.c + * Copyright 2005-2011 Audacious Development Team * - * Based on BMP: - * Copyright (C) 2003-2004 BMP development team. + * This file is part of Audacious. * - * Based on XMMS: - * Copyright (C) 1998-2003 XMMS development team. + * Audacious 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 or version 3 of the License. * - * 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. + * Audacious 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. * - * 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. */ #include <glib.h> +#include <pthread.h> #include <libaudcore/audstrings.h> #include <libaudcore/eventqueue.h> @@ -33,30 +30,42 @@ #include "config.h" #include "i18n.h" #include "interface.h" -#include "main.h" #include "output.h" #include "playback.h" #include "playlist.h" -static void set_params (InputPlayback * playback, const gchar * title, gint - length, gint bitrate, gint samplerate, gint channels); -static void set_tuple (InputPlayback * playback, Tuple * tuple); -static void set_gain_from_playlist (InputPlayback * playback); - -static void playback_free (InputPlayback * playback); -static gboolean playback_play_file (gint playlist, gint entry, gint seek_time, +static gboolean playback_start (gint playlist, gint entry, gint seek_time, gboolean pause); -InputPlayback * current_playback = NULL; +static InputPlayback playback_api; -static gint time_offset; -static gboolean paused; -static gboolean stopping; -static gint ready_source; +static gboolean playing = FALSE; +static gboolean playback_error; static gint failed_entries; + +static gint current_entry; +static const gchar * current_filename; +static InputPlugin * current_decoder; +static void * current_data; +static gint current_bitrate, current_samplerate, current_channels; +static gchar * current_title; +static gint current_length; + +static ReplayGainInfo gain_from_playlist; + +static gint time_offset, start_time, stop_time; +static gboolean paused; + +static pthread_t playback_thread_handle; +static gint end_source = 0; + +static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER; +static gboolean ready_flag; +static gint ready_source = 0; + static gint set_tuple_source = 0; static Tuple * tuple_to_be_set = NULL; -static ReplayGainInfo gain_from_playlist; static void cancel_set_tuple (void) { @@ -74,7 +83,7 @@ static void cancel_set_tuple (void) } /* clears gain info if tuple == NULL */ -static void read_gain_from_tuple (Tuple * tuple) +static void read_gain_from_tuple (const Tuple * tuple) { gint album_gain, album_peak, track_gain, track_peak, gain_unit, peak_unit; @@ -105,78 +114,86 @@ static void read_gain_from_tuple (Tuple * tuple) static gboolean ready_cb (void * unused) { - g_return_val_if_fail (current_playback != NULL, FALSE); - - g_mutex_lock (current_playback->pb_ready_mutex); - ready_source = 0; - g_mutex_unlock (current_playback->pb_ready_mutex); + g_return_val_if_fail (playing, FALSE); + hook_call ("playback ready", NULL); hook_call ("title change", NULL); + ready_source = 0; return FALSE; } -static gboolean playback_is_ready (void) +gboolean playback_get_ready (void) { - gboolean ready; - - g_return_val_if_fail (current_playback != NULL, FALSE); - - g_mutex_lock (current_playback->pb_ready_mutex); - ready = (current_playback->pb_ready_val && ! ready_source); - g_mutex_unlock (current_playback->pb_ready_mutex); + g_return_val_if_fail (playing, FALSE); + pthread_mutex_lock (& ready_mutex); + gboolean ready = ready_flag; + pthread_mutex_unlock (& ready_mutex); return ready; } -static gint -playback_set_pb_ready(InputPlayback *playback) +static void set_pb_ready (InputPlayback * p) { - g_mutex_lock(playback->pb_ready_mutex); - playback->pb_ready_val = 1; + g_return_if_fail (playing); + + pthread_mutex_lock (& ready_mutex); + ready_flag = TRUE; + pthread_cond_signal (& ready_cond); + pthread_mutex_unlock (& ready_mutex); + ready_source = g_timeout_add (0, ready_cb, NULL); - g_cond_signal(playback->pb_ready_cond); - g_mutex_unlock(playback->pb_ready_mutex); - return 0; } -static void update_cb (void * hook_data, void * user_data) +static void wait_until_ready (void) { - gint playlist, entry, length; - const gchar * title; + g_return_if_fail (playing); + pthread_mutex_lock (& ready_mutex); - g_return_if_fail (current_playback != NULL); + while (! ready_flag) + pthread_cond_wait (& ready_cond, & ready_mutex); + + pthread_mutex_unlock (& ready_mutex); +} + +static void update_cb (void * hook_data, void * user_data) +{ + g_return_if_fail (playing); if (GPOINTER_TO_INT (hook_data) < PLAYLIST_UPDATE_METADATA) return; - playlist = playlist_get_playing (); - entry = playlist_get_position (playlist); + gint playlist = playlist_get_playing (); + gint entry = playlist_get_position (playlist); + const gchar * title = playlist_entry_get_title (playlist, entry, FALSE); - if ((title = playlist_entry_get_title (playlist, entry, FALSE)) == NULL) + if (title == NULL) title = playlist_entry_get_filename (playlist, entry); - length = playlist_entry_get_length (playlist, entry, FALSE); + gint length = playlist_entry_get_length (playlist, entry, FALSE); - if (! strcmp (title, current_playback->title) && length == - current_playback->length) + if (entry == current_entry && ! strcmp (title, current_title) && length == + current_length) return; - g_free (current_playback->title); - current_playback->title = g_strdup (title); - current_playback->length = length; + current_entry = entry; + g_free (current_title); + current_title = g_strdup (title); + current_length = length; - if (playback_is_ready ()) + if (playback_get_ready ()) hook_call ("title change", NULL); } gint playback_get_time (void) { - if (! playback_is_ready ()) + g_return_val_if_fail (playing, 0); + + if (! playback_get_ready ()) return 0; gint time = -1; - if (current_playback->plugin->get_time != NULL) - time = current_playback->plugin->get_time (current_playback); + if (current_decoder->get_time != NULL) + time = current_decoder->get_time (& playback_api); if (time < 0) time = get_output_time (); @@ -186,9 +203,9 @@ gint playback_get_time (void) void playback_play (gint seek_time, gboolean pause) { - gint playlist, entry; + g_return_if_fail (! playing); - playlist = playlist_get_playing (); + gint playlist = playlist_get_playing (); if (playlist == -1) { @@ -196,7 +213,7 @@ void playback_play (gint seek_time, gboolean pause) playlist_set_playing (playlist); } - entry = playlist_get_position (playlist); + gint entry = playlist_get_position (playlist); if (entry == -1) { @@ -207,38 +224,35 @@ void playback_play (gint seek_time, gboolean pause) return; } - if (playback_get_playing()) - playback_stop(); - failed_entries = 0; - playback_play_file (playlist, entry, seek_time, pause); + playback_start (playlist, entry, seek_time, pause); } void playback_pause (void) { - if (! playback_is_ready ()) - return; + g_return_if_fail (playing); + wait_until_ready (); paused = ! paused; - g_return_if_fail (current_playback->plugin->pause != NULL); - current_playback->plugin->pause (current_playback, paused); + g_return_if_fail (current_decoder->pause != NULL); + current_decoder->pause (& playback_api, paused); if (paused) - hook_call("playback pause", NULL); + hook_call ("playback pause", NULL); else - hook_call("playback unpause", NULL); + hook_call ("playback unpause", NULL); } -static void playback_finalize (void) +static void playback_cleanup (void) { - hook_dissociate ("playlist update", update_cb); + g_return_if_fail (playing); - g_mutex_lock (current_playback->pb_ready_mutex); + pthread_join (playback_thread_handle, NULL); + playing = FALSE; + playback_error = FALSE; - while (! current_playback->pb_ready_val) - g_cond_wait (current_playback->pb_ready_cond, - current_playback->pb_ready_mutex); + g_free (current_title); if (ready_source) { @@ -246,17 +260,8 @@ static void playback_finalize (void) ready_source = 0; } - g_mutex_unlock (current_playback->pb_ready_mutex); - - current_playback->plugin->stop (current_playback); - - /* some plugins do this themselves */ - if (current_playback->thread != NULL) - g_thread_join (current_playback->thread); - cancel_set_tuple (); - playback_free (current_playback); - current_playback = NULL; + hook_dissociate ("playlist update", update_cb); } static void complete_stop (void) @@ -273,43 +278,45 @@ static void complete_stop (void) void playback_stop (void) { - g_return_if_fail (current_playback != NULL); - - stopping = TRUE; - playback_finalize (); - stopping = FALSE; + g_return_if_fail (playing); + wait_until_ready (); + current_decoder->stop (& playback_api); + playback_cleanup (); complete_stop (); + + if (end_source) + { + g_source_remove (end_source); + end_source = 0; + } } -static gboolean playback_ended (void * unused) +static gboolean end_cb (void * unused) { - gint playlist = playlist_get_playing (); - gboolean play; - - g_return_val_if_fail (current_playback != NULL, FALSE); + g_return_val_if_fail (playing, FALSE); hook_call ("playback end", NULL); - if (current_playback->error) + if (playback_error) failed_entries ++; else failed_entries = 0; - playback_finalize (); + playback_cleanup (); + + gint playlist = playlist_get_playing (); while (1) { + gboolean play; + if (cfg.no_playlist_advance) play = cfg.repeat && ! failed_entries; - else - { - if (! (play = playlist_next_song (playlist, cfg.repeat))) - playlist_set_position (playlist, -1); - - if (failed_entries >= 10) - play = FALSE; - } + else if (! (play = playlist_next_song (playlist, cfg.repeat))) + playlist_set_position (playlist, -1); + else if (failed_entries >= 10) + play = FALSE; if (cfg.stopaftersong) play = FALSE; @@ -321,192 +328,90 @@ static gboolean playback_ended (void * unused) break; } - if (playback_play_file (playlist, playlist_get_position (playlist), 0, - FALSE)) + if (playback_start (playlist, playlist_get_position (playlist), 0, FALSE)) break; failed_entries ++; } + end_source = 0; return FALSE; } -typedef struct +static void * playback_thread (void * unused) { - gint start_time, stop_time; - gboolean pause; -} -PlayParams; + gchar * real = filename_split_subtune (current_filename, NULL); + VFSFile * file = vfs_fopen (real, "r"); + g_free (real); -static void * playback_monitor_thread (void * data) -{ - if (current_playback->plugin->play != NULL) - { - PlayParams * params = data; - VFSFile * file = vfs_fopen (current_playback->filename, "r"); - - current_playback->error = ! current_playback->plugin->play - (current_playback, current_playback->filename, file, - params->start_time, params->stop_time, params->pause); - - if (file != NULL) - vfs_fclose (file); - } - else - { - fprintf (stderr, "%s should be updated to provide play().\n", - current_playback->plugin->description); - g_return_val_if_fail (current_playback->plugin->play_file != NULL, NULL); - current_playback->plugin->play_file (current_playback); - } + playback_error = ! current_decoder->play (& playback_api, current_filename, + file, start_time, stop_time, paused); - g_mutex_lock (current_playback->pb_ready_mutex); - current_playback->pb_ready_val = TRUE; + if (file != NULL) + vfs_fclose (file); - if (ready_source != 0) - { - g_source_remove (ready_source); - ready_source = 0; - } - - g_cond_signal (current_playback->pb_ready_cond); - g_mutex_unlock (current_playback->pb_ready_mutex); - - if (! stopping) - g_timeout_add (0, playback_ended, NULL); + if (! ready_flag) + set_pb_ready (& playback_api); + end_source = g_timeout_add (0, end_cb, NULL); return NULL; } -/* compatibility */ -static void playback_set_replaygain_info (InputPlayback * playback, - ReplayGainInfo * info) -{ - fprintf (stderr, "Plugin %s should be updated to use OutputAPI::" - "set_replaygain_info or (better) InputPlayback::set_gain_from_playlist.\n", - playback->plugin->description); - - playback->output->set_replaygain_info (info); -} - -/* compatibility */ -static void playback_pass_audio (InputPlayback * playback, gint format, gint - channels, gint size, void * data, gint * going) -{ - static gboolean warned = FALSE; - - if (! warned) - { - fprintf (stderr, "Plugin %s should be updated to use OutputAPI::" - "write_audio.\n", playback->plugin->description); - warned = TRUE; - } - - playback->output->write_audio (data, size); -} - -static InputPlayback * playback_new (void) -{ - InputPlayback *playback = (InputPlayback *) g_slice_new0(InputPlayback); - - playback->pb_ready_mutex = g_mutex_new(); - playback->pb_ready_cond = g_cond_new(); - playback->pb_ready_val = 0; - - playback->output = & output_api; - - /* init vtable functors */ - playback->set_pb_ready = playback_set_pb_ready; - playback->set_params = set_params; - playback->set_tuple = set_tuple; - playback->set_gain_from_playlist = set_gain_from_playlist; - - /* compatibility */ - playback->set_replaygain_info = playback_set_replaygain_info; - playback->pass_audio = playback_pass_audio; - - return playback; -} - -/** - * Destroys InputPlayback. - * - * Playback comes from playback_new() function but there can be also - * other sources for allocated playback data (like filename and title) - * and this tries to deallocate all that data. - */ -static void playback_free (InputPlayback * playback) +static gboolean playback_start (gint playlist, gint entry, gint seek_time, + gboolean pause) { - g_free(playback->filename); - g_free(playback->title); + g_return_val_if_fail (! playing, FALSE); - g_mutex_free(playback->pb_ready_mutex); - g_cond_free(playback->pb_ready_cond); - - g_slice_free(InputPlayback, playback); -} + current_entry = entry; + current_filename = playlist_entry_get_filename (playlist, entry); -static void playback_run (gint start_time, gint stop_time, gboolean pause) -{ - current_playback->playing = FALSE; - current_playback->eof = FALSE; - current_playback->error = FALSE; - - paused = pause; - stopping = FALSE; - ready_source = 0; + vfs_prepare_filename (current_filename); - static PlayParams params; - params.start_time = start_time; - params.stop_time = stop_time; - params.pause = pause; + PluginHandle * p = playlist_entry_get_decoder (playlist, entry, FALSE); + current_decoder = p ? plugin_get_header (p) : NULL; - current_playback->thread = g_thread_create (playback_monitor_thread, - & params, TRUE, NULL); -} - -static gboolean playback_play_file (gint playlist, gint entry, gint seek_time, - gboolean pause) -{ - const gchar * filename = playlist_entry_get_filename (playlist, entry); - const gchar * title = playlist_entry_get_title (playlist, entry, FALSE); - InputPlugin * decoder = playlist_entry_get_decoder (playlist, entry); - Tuple * tuple = (Tuple *) playlist_entry_get_tuple (playlist, entry, FALSE); - - g_return_val_if_fail (current_playback == NULL, FALSE); - - if (decoder == NULL) + if (current_decoder == NULL) { - gchar * error = g_strdup_printf (_("No decoder found for %s."), filename); - - interface_show_error_message (error); - g_free (error); + gchar * error = g_strdup_printf (_("No decoder found for %s."), + current_filename); + /* The interface may not be up yet at this point. --jlindgren */ + event_queue_with_data_free ("interface show error", error); return FALSE; } - read_gain_from_tuple (tuple); /* even if tuple == NULL */ + current_data = NULL; + current_bitrate = 0; + current_samplerate = 0; + current_channels = 0; - current_playback = playback_new (); - current_playback->plugin = decoder; - current_playback->filename = g_strdup (filename); - current_playback->title = g_strdup ((title != NULL) ? title : filename); - current_playback->length = playlist_entry_get_length (playlist, entry, FALSE); + const gchar * title = playlist_entry_get_title (playlist, entry, FALSE); + current_title = g_strdup ((title != NULL) ? title : current_filename); + + current_length = playlist_entry_get_length (playlist, entry, FALSE); + read_gain_from_tuple (playlist_entry_get_tuple (playlist, entry, FALSE)); - if (playlist_entry_is_segmented (playlist, entry)) + if (current_length > 0 && playlist_entry_is_segmented (playlist, entry)) { time_offset = playlist_entry_get_start_time (playlist, entry); - playback_run (time_offset + seek_time, playlist_entry_get_end_time - (playlist, entry), pause); + stop_time = playlist_entry_get_end_time (playlist, entry); } else { time_offset = 0; - playback_run (seek_time, -1, pause); + stop_time = -1; } -#ifdef USE_DBUS /* Fix me: Use a "playback begin" hook in dbus.c. */ - mpris_emit_track_change(mpris); -#endif + if (current_length > 0) + start_time = time_offset + seek_time; + else + start_time = 0; + + playing = TRUE; + playback_error = FALSE; + paused = pause; + ready_flag = FALSE; + + pthread_create (& playback_thread_handle, NULL, playback_thread, NULL); hook_associate ("playlist update", update_cb, NULL); hook_call ("playback begin", NULL); @@ -515,144 +420,158 @@ static gboolean playback_play_file (gint playlist, gint entry, gint seek_time, gboolean playback_get_playing (void) { - return (current_playback != NULL); + return playing; } gboolean playback_get_paused (void) { - g_return_val_if_fail (current_playback != NULL, FALSE); - + g_return_val_if_fail (playing, FALSE); return paused; } void playback_seek (gint time) { - g_return_if_fail (current_playback != NULL); + g_return_if_fail (playing); + wait_until_ready (); - if (! playback_is_ready ()) + if (current_decoder->mseek == NULL || playback_get_length () < 1) return; - time = CLAMP (time, 0, current_playback->length); - time += time_offset; - - if (current_playback->plugin->mseek != NULL) - current_playback->plugin->mseek (current_playback, time); - else if (current_playback->plugin->seek != NULL) - { - fprintf (stderr, "%s should be updated to provide mseek().\n", - current_playback->plugin->description); - current_playback->plugin->seek (current_playback, time / 1000); - } + current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0, + current_length)); hook_call ("playback seek", NULL); } -static void set_params (InputPlayback * playback, const gchar * title, gint - length, gint bitrate, gint samplerate, gint channels) +static void set_data (InputPlayback * p, void * data) +{ + g_return_if_fail (playing); + current_data = data; +} + +static void * get_data (InputPlayback * p) { - playback->rate = bitrate; - playback->freq = samplerate; - playback->nch = channels; + g_return_val_if_fail (playing, NULL); + return current_data; +} + +static void set_params (InputPlayback * p, gint bitrate, gint samplerate, + gint channels) +{ + g_return_if_fail (playing); + + current_bitrate = bitrate; + current_samplerate = samplerate; + current_channels = channels; event_queue ("info change", NULL); } static gboolean set_tuple_cb (void * unused) { - gint playlist = playlist_get_playing (); - - g_return_val_if_fail (current_playback != NULL, FALSE); - g_mutex_lock (current_playback->pb_ready_mutex); + g_return_val_if_fail (playing, FALSE); + pthread_mutex_lock (& ready_mutex); + gint playlist = playlist_get_playing (); playlist_entry_set_tuple (playlist, playlist_get_position (playlist), tuple_to_be_set); set_tuple_source = 0; tuple_to_be_set = NULL; - g_mutex_unlock (current_playback->pb_ready_mutex); - + pthread_mutex_unlock (& ready_mutex); return FALSE; } -static void set_tuple (InputPlayback * playback, Tuple * tuple) +static void set_tuple (InputPlayback * p, Tuple * tuple) { - g_mutex_lock (playback->pb_ready_mutex); + g_return_if_fail (playing); + pthread_mutex_lock (& ready_mutex); - /* playlist_entry_set_tuple must execute in main thread */ cancel_set_tuple (); set_tuple_source = g_timeout_add (0, set_tuple_cb, NULL); tuple_to_be_set = tuple; read_gain_from_tuple (tuple); - - g_mutex_unlock (playback->pb_ready_mutex); + pthread_mutex_unlock (& ready_mutex); } -static void set_gain_from_playlist (InputPlayback * playback) +static void set_gain_from_playlist (InputPlayback * p) { - playback->output->set_replaygain_info (& gain_from_playlist); + g_return_if_fail (playing); + p->output->set_replaygain_info (& gain_from_playlist); } +static InputPlayback playback_api = { + .output = & output_api, + .set_data = set_data, + .get_data = get_data, + .set_pb_ready = set_pb_ready, + .set_params = set_params, + .set_tuple = set_tuple, + .set_gain_from_playlist = set_gain_from_playlist, +}; + gchar * playback_get_title (void) { - gchar * suffix, * title; + g_return_val_if_fail (playing, NULL); - g_return_val_if_fail (current_playback != NULL, NULL); - - if (! playback_is_ready ()) + if (! playback_get_ready ()) return g_strdup (_("Buffering ...")); - suffix = (current_playback->length > 0) ? g_strdup_printf (" (%d:%02d)", - current_playback->length / 60000, current_playback->length / 1000 % 60) : - NULL; + gchar s[128]; - if (cfg.show_numbers_in_pl) - title = g_strdup_printf ("%d. %s%s", 1 + playlist_get_position - (playlist_get_playing ()), current_playback->title, (suffix != NULL) ? - suffix : ""); + if (current_length) + { + gint len = current_length / 1000; + + if (len < 3600) + snprintf (s, sizeof s, cfg.leading_zero ? " (%02d:%02d)" : + " (%d:%02d)", len / 60, len % 60); + else + snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) % + 60, len % 60); + } else - title = g_strdup_printf ("%s%s", current_playback->title, (suffix != - NULL) ? suffix : ""); + s[0] = 0; - g_free (suffix); - return title; + if (cfg.show_numbers_in_pl) + return g_strdup_printf ("%d. %s%s", 1 + playlist_get_position + (playlist_get_playing ()), current_title, s); + + return g_strdup_printf ("%s%s", current_title, s); } gint playback_get_length (void) { - g_return_val_if_fail (current_playback != NULL, 0); - - return current_playback->length; + g_return_val_if_fail (playing, 0); + return current_length; } void playback_get_info (gint * bitrate, gint * samplerate, gint * channels) { - g_return_if_fail (current_playback != NULL); - - * bitrate = current_playback->rate; - * samplerate = current_playback->freq; - * channels = current_playback->nch; + g_return_if_fail (playing); + * bitrate = current_bitrate; + * samplerate = current_samplerate; + * channels = current_channels; } -void -input_get_volume(gint * l, gint * r) +void playback_get_volume (gint * l, gint * r) { - if (current_playback && current_playback->plugin->get_volume && - current_playback->plugin->get_volume (l, r)) + if (playing && current_decoder->get_volume != NULL && + current_decoder->get_volume (l, r)) return; output_get_volume (l, r); } -void -input_set_volume(gint l, gint r) +void playback_set_volume(gint l, gint r) { gint h_vol[2] = {l, r}; - hook_call("volume set", h_vol); + hook_call ("volume set", h_vol); - if (current_playback && current_playback->plugin->set_volume && - current_playback->plugin->set_volume (l, r)) + if (playing && current_decoder->set_volume != NULL && + current_decoder->set_volume (l, r)) return; output_set_volume (l, r); diff --git a/src/audacious/playback.h b/src/audacious/playback.h index 96c1a5d..2bf7f0d 100644 --- a/src/audacious/playback.h +++ b/src/audacious/playback.h @@ -1,5 +1,5 @@ /* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2007 Audacious development team + * Copyright (C) 2005-2011 Audacious development team * * Based on BMP: * Copyright (C) 2003-2004 BMP development team @@ -30,6 +30,7 @@ gint playback_get_time(void); void playback_pause(void); void playback_stop(void); gboolean playback_get_playing(void); +gboolean playback_get_ready (void); gboolean playback_get_paused(void); void playback_seek(gint time); @@ -37,7 +38,7 @@ gchar * playback_get_title (void); gint playback_get_length (void); void playback_get_info (gint * bitrate, gint * samplerate, gint * channels); -void input_get_volume(gint * l, gint * r); -void input_set_volume(gint l, gint r); +void playback_get_volume (gint * l, gint * r); +void playback_set_volume (gint l, gint r); #endif /* AUDACIOUS_PLAYBACK_H */ diff --git a/src/audacious/playlist-api.h b/src/audacious/playlist-api.h index 8520261..39ee7bf 100644 --- a/src/audacious/playlist-api.h +++ b/src/audacious/playlist-api.h @@ -1,6 +1,6 @@ /* * playlist-api.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * This file is part of Audacious. * @@ -38,7 +38,11 @@ AUD_FUNC1 (void, playlist_insert, gint, at) AUD_FUNC3 (void, playlist_reorder, gint, from, gint, to, gint, count) /* Closes a playlist. CAUTION: The playlist is not saved, and no confirmation - * is presented to the user. */ + * is presented to the user. If <playlist> is the only playlist, a new playlist + * is added. If <playlist> is the active playlist, another playlist is marked + * active. If <playlist> is the one from which the last song played was taken, + * playback is stopped. In this case, calls to playlist_get_playing() will + * return -1, and the behavior of drct_play() is unspecified. */ AUD_FUNC1 (void, playlist_delete, gint, playlist) /* Sets the filename associated with a playlist. (Audacious currently makes no @@ -96,7 +100,9 @@ AUD_FUNC4 (void, playlist_entry_insert_batch, gint, playlist, gint, at, struct index *, filenames, struct index *, tuples) /* Removes a contiguous block of <number> entries starting from the one numbered - * <at> from a playlist. */ + * <at> from a playlist. If the last song played is in this block, playback is + * stopped. In this case, calls to playlist_get_position() will return -1, and + * the behavior of drct_play() is unspecified. */ AUD_FUNC3 (void, playlist_entry_delete, gint, playlist, gint, at, gint, number) /* Returns the filename of an entry. The returned string is valid until another @@ -104,6 +110,12 @@ AUD_FUNC3 (void, playlist_entry_delete, gint, playlist, gint, at, gint, number) AUD_FUNC2 (const gchar *, playlist_entry_get_filename, gint, playlist, gint, entry) +/* Returns a handle to the decoder plugin associated with an entry, or NULL if + * none can be found. If <fast> is nonzero, returns NULL if no decoder plugin + * has yet been found. */ +AUD_FUNC3 (PluginHandle *, playlist_entry_get_decoder, gint, playlist, gint, + entry, gboolean, fast) + /* Returns the tuple associated with an entry, or NULL if one is not available. * The returned tuple is read-only and valid until another playlist function is * called or control returns to the program's main loop. If <fast> is nonzero, @@ -115,10 +127,20 @@ AUD_FUNC3 (const Tuple *, playlist_entry_get_tuple, gint, playlist, gint, entry, /* Returns a formatted title string for an entry. This may include information * such as the filename, song title, and/or artist. The returned string is * valid until another playlist function is called or control returns to the - * program's main loop. <fast> is as in playlist_entry_get_tuple(). */ + * program's main loop. If <fast> is nonzero, returns the entry's filename if + * metadata for the entry has not yet been read. */ AUD_FUNC3 (const gchar *, playlist_entry_get_title, gint, playlist, gint, entry, gboolean, fast) +/* Returns three strings (title, artist, and album) describing an entry. The + * returned strings are valid until another playlist function is called or + * control returns to the program's main loop. If <fast> is nonzero, return's + * the entry's filename for <title> and NULL for <artist> and <album> if + * metadata for the entry has not yet been read. */ +AUD_FUNC6 (void, playlist_entry_describe, gint, playlist, gint, entry, + const gchar * *, title, const gchar * *, artist, const gchar * *, album, + gboolean, fast) + /* Returns the length in milliseconds of an entry, or -1 if the length is not * known. <fast> is as in playlist_entry_get_tuple(). */ AUD_FUNC3 (gint, playlist_entry_get_length, gint, playlist, gint, entry, @@ -148,33 +170,44 @@ AUD_FUNC1 (gint, playlist_selected_count, gint, playlist) /* Selects all (or none) of the entries in a playlist. */ AUD_FUNC2 (void, playlist_select_all, gint, playlist, gboolean, selected) -/* Moves an entry, along with selected entries near it, within a playlist, by an - * offset of <distance> entries. For an exact definition of "near it", read the - * source code. Returns the offset by which the entry was actually moved, which - * may be less (in absolute value) than the requested offset. */ +/* Moves a selected entry within a playlist by an offset of <distance> entries. + * Other selected entries are gathered around it. Returns the offset by which + * the entry was actually moved, which may be less in absolute value than the + * requested offset. */ AUD_FUNC3 (gint, playlist_shift, gint, playlist, gint, position, gint, distance) -/* Removes the selected entries from a playlist. */ +/* Removes the selected entries from a playlist. If the last song played is one + * of these entries, playback is stopped. In this case, calls to + * playlist_get_position() will return -1, and the behavior of drct_play() is + * unspecified. */ AUD_FUNC1 (void, playlist_delete_selected, gint, playlist) /* Sorts the entries in a playlist based on filename. The callback function * should return negative if the first filename comes before the second, * positive if it comes after, or zero if the two are indistinguishable. */ AUD_FUNC2 (void, playlist_sort_by_filename, gint, playlist, - PlaylistFilenameCompareFunc, compare) + PlaylistStringCompareFunc, compare) /* Sorts the entries in a playlist based on tuple. */ AUD_FUNC2 (void, playlist_sort_by_tuple, gint, playlist, PlaylistTupleCompareFunc, compare) +/* Sorts the entries in a playlist based on formatted title string. */ +AUD_FUNC2 (void, playlist_sort_by_title, gint, playlist, + PlaylistStringCompareFunc, compare) + /* Sorts only the selected entries in a playlist based on filename. */ AUD_FUNC2 (void, playlist_sort_selected_by_filename, gint, playlist, - PlaylistFilenameCompareFunc, compare) + PlaylistStringCompareFunc, compare) /* Sorts only the selected entries in a playlist based on tuple. */ AUD_FUNC2 (void, playlist_sort_selected_by_tuple, gint, playlist, PlaylistTupleCompareFunc, compare) +/* Sorts only the selected entries in a playlist based on formatted title string. */ +AUD_FUNC2 (void, playlist_sort_selected_by_title, gint, playlist, + PlaylistStringCompareFunc, compare) + /* Reverses the order of the entries in a playlist. */ AUD_FUNC1 (void, playlist_reverse, gint, playlist) @@ -185,6 +218,9 @@ AUD_FUNC1 (void, playlist_randomize, gint, playlist) * reading it afresh from the song files in the background. */ AUD_FUNC1 (void, playlist_rescan, gint, playlist) +/* Like playlist_rescan, but applies only to the selected entries in a playlist. */ +AUD_FUNC1 (void, playlist_rescan_selected, gint, playlist) + /* Discards the metadata stored for all the entries that refer to a particular * song file, in whatever playlist they appear, and starts reading it afresh * from that file in the background. */ @@ -231,6 +267,16 @@ AUD_FUNC1 (void, playlist_queue_delete_selected, gint, playlist) * "playlist update" hook. If called from within the hook, returns nonzero. */ AUD_FUNC0 (gboolean, playlist_update_pending) +/* May be called within the "playlist update" hook to determine what range of + * entries must be updated. If all entries in all playlists must be updated, + * returns zero. If a limited range in a single playlist must be updated, + * returns nonzero. In this case, stores the number of that playlist at + * <playlist>, the number of the first entry to be updated at <at>, and the + * number of contiguous entries to be updated at <count>. Note that entries may + * have been added or removed within this range. */ +AUD_FUNC3 (gboolean, playlist_update_range, gint *, playlist, gint *, at, + gint *, count) + /* --- PLAYLIST UTILITY API --- */ /* Sorts the entries in a playlist according to one of the schemes listed in @@ -277,3 +323,15 @@ AUD_FUNC2 (gboolean, playlist_save, gint, playlist, const gchar *, filename) * unexpected results. */ AUD_FUNC4 (void, playlist_insert_folder, gint, playlist, gint, at, const gchar *, folder, gboolean, play) + +/* --- ADDED IN AUDACIOUS 2.5-BETA2 --- */ + +/* Returns a unique non-negative integer which can be used to identify a given + * playlist even if its numbering changes (as when playlists are reordered). + * On error, returns -1. */ +AUD_FUNC1 (gint, playlist_get_unique_id, gint, playlist) + +/* Returns the number of the playlist identified by a given integer ID as + * returned by playlist_get_unique_id(). If the playlist no longer exists, + * returns -1. */ +AUD_FUNC1 (gint, playlist_by_unique_id, gint, id) diff --git a/src/audacious/playlist-files.c b/src/audacious/playlist-files.c new file mode 100644 index 0000000..d2ea3f3 --- /dev/null +++ b/src/audacious/playlist-files.c @@ -0,0 +1,84 @@ +/* + * playlist-files.c + * Copyright 2010 John Lindgren + * + * This file is part of Audacious. + * + * Audacious 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 or version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include "debug.h" +#include "playlist.h" +#include "plugin.h" +#include "plugins.h" + +static const gchar * get_extension (const gchar * filename, gboolean quiet) +{ + const gchar * s = strrchr (filename, '/'); + if (! s) + goto FAIL; + + const gchar * p = strrchr (s + 1, '.'); + if (! p) + goto FAIL; + + return p + 1; + +FAIL: + if (! quiet) + fprintf (stderr, "Failed to parse playlist filename %s.\n", filename); + return NULL; +} + +gboolean filename_is_playlist (const gchar * filename) +{ + const gchar * ext = get_extension (filename, TRUE); + if (! ext) + return FALSE; + + return playlist_plugin_for_extension (ext) ? TRUE : FALSE; +} + +static PlaylistPlugin * get_plugin (const gchar * filename) +{ + const gchar * ext = get_extension (filename, FALSE); + if (! ext) + return NULL; + + PluginHandle * plugin = playlist_plugin_for_extension (ext); + if (! plugin) + { + fprintf (stderr, "Unrecognized playlist file type \"%s\".\n", ext); + return NULL; + } + + return plugin_get_header (plugin); +} + +gboolean playlist_insert_playlist (gint list, gint at, const gchar * filename) +{ + AUDDBG ("Loading playlist %s.\n", filename); + PlaylistPlugin * pp = get_plugin (filename); + g_return_val_if_fail (pp && pp->load, FALSE); + return pp->load (filename, list, at); +} + +gboolean playlist_save (gint list, const gchar * filename) +{ + AUDDBG ("Saving playlist %s.\n", filename); + PlaylistPlugin * pp = get_plugin (filename); + g_return_val_if_fail (pp && pp->save, FALSE); + return pp->save (filename, list); +} diff --git a/src/audacious/playlist-new.c b/src/audacious/playlist-new.c index 56fd746..0c26db2 100644 --- a/src/audacious/playlist-new.c +++ b/src/audacious/playlist-new.c @@ -1,6 +1,6 @@ /* * playlist-new.c - * Copyright 2009-2010 John Lindgren + * Copyright 2009-2011 John Lindgren * * This file is part of Audacious. * @@ -20,7 +20,6 @@ */ #include <assert.h> -#include <inttypes.h> #include <stdlib.h> #include <time.h> @@ -28,92 +27,76 @@ #include <libaudcore/audstrings.h> #include <libaudcore/hook.h> +#include <libaudcore/stringpool.h> #include <libaudcore/tuple_formatter.h> #include "audconfig.h" #include "config.h" #include "i18n.h" -#include "main.h" #include "misc.h" #include "playback.h" #include "playlist.h" #include "playlist-utils.h" -#include "plugin.h" - -#define SCAN_DEBUG(...) +#include "plugins.h" +#include "util.h" #define SCAN_THREADS 4 #define STATE_FILE "playlist-state" #define DECLARE_PLAYLIST \ - struct playlist * playlist + Playlist * playlist #define DECLARE_PLAYLIST_ENTRY \ - struct playlist * playlist; \ - struct entry * entry + Playlist * playlist; \ + Entry * entry -#define LOOKUP_PLAYLIST \ -{ \ +#define LOOKUP_PLAYLIST do { \ playlist = lookup_playlist (playlist_num); \ g_return_if_fail (playlist != NULL); \ -} +} while (0) -#define LOOKUP_PLAYLIST_RET(ret) \ -{ \ +#define LOOKUP_PLAYLIST_RET(ret) do { \ playlist = lookup_playlist (playlist_num); \ g_return_val_if_fail (playlist != NULL, ret); \ -} +} while (0) -#define LOOKUP_PLAYLIST_ENTRY \ -{ \ +#define LOOKUP_PLAYLIST_ENTRY do { \ playlist = lookup_playlist (playlist_num); \ g_return_if_fail (playlist != NULL); \ entry = lookup_entry (playlist, entry_num); \ g_return_if_fail (entry != NULL); \ -} +} while (0) -#define LOOKUP_PLAYLIST_ENTRY_RET(ret) \ -{ \ +#define LOOKUP_PLAYLIST_ENTRY_RET(ret) do { \ playlist = lookup_playlist (playlist_num); \ g_return_val_if_fail (playlist != NULL, ret); \ entry = lookup_entry (playlist, entry_num); \ g_return_val_if_fail (entry != NULL, ret); \ -} +} while (0) -#define SELECTION_HAS_CHANGED \ -{ \ - queue_update (PLAYLIST_UPDATE_SELECTION); \ -} +#define SELECTION_HAS_CHANGED(p, a, c) \ + queue_update (PLAYLIST_UPDATE_SELECTION, p, a, c) -#define METADATA_WILL_CHANGE \ -{ \ - scan_stop (); \ -} +#define METADATA_WILL_CHANGE scan_stop () -#define METADATA_HAS_CHANGED \ -{ \ +#define METADATA_HAS_CHANGED(p, a, c) do { \ scan_reset (); \ - queue_update (PLAYLIST_UPDATE_METADATA); \ -} + queue_update (PLAYLIST_UPDATE_METADATA, p, a, c); \ +} while (0) -#define PLAYLIST_WILL_CHANGE \ -{ \ - scan_stop (); \ -} +#define PLAYLIST_WILL_CHANGE scan_stop () -#define PLAYLIST_HAS_CHANGED \ -{ \ +#define PLAYLIST_HAS_CHANGED(p, a, c) do { \ scan_reset (); \ - queue_update (PLAYLIST_UPDATE_STRUCTURE); \ -} + queue_update (PLAYLIST_UPDATE_STRUCTURE, p, a, c); \ +} while (0) -struct entry -{ +typedef struct { gint number; gchar *filename; - InputPlugin *decoder; + PluginHandle * decoder; Tuple *tuple; - gchar *title; + gchar * formatted, * title, * artist, * album; gint length; gboolean failed; gboolean selected; @@ -122,37 +105,41 @@ struct entry gboolean segmented; gint start; gint end; -}; +} Entry; -struct playlist -{ +typedef struct { gint number; + gint unique_id; gchar *filename; gchar *title; struct index *entries; - struct entry *position; + Entry * position; gint selected_count; gint last_shuffle_num; GList *queued; gint64 total_length; gint64 selected_length; -}; +} Playlist; + +static gint next_unique_id = 1000; -static struct index *playlists; -static struct playlist *active_playlist; -static struct playlist *playing_playlist; +static struct index * playlists = NULL; +static Playlist * active_playlist = NULL; +static Playlist * playing_playlist = NULL; static gint update_source, update_level; +static Playlist * update_playlist; +static gint unchanged_before, unchanged_after; + static gint scan_source; static GMutex * scan_mutex; static GCond * scan_conds[SCAN_THREADS]; static const gchar * scan_filenames[SCAN_THREADS]; -static InputPlugin * scan_decoders[SCAN_THREADS]; +static PluginHandle * scan_decoders[SCAN_THREADS]; static Tuple * scan_tuples[SCAN_THREADS]; static gboolean scan_quit; static GThread * scan_threads[SCAN_THREADS]; static gint scan_positions[SCAN_THREADS]; -gint updated_ago; static void * scanner (void * unused); @@ -166,34 +153,37 @@ static gchar *title_from_tuple(Tuple * tuple) return tuple_formatter_make_title_string(tuple, format); } -static void entry_set_tuple_real (struct entry * entry, Tuple * tuple) +static void entry_set_tuple_real (Entry * entry, Tuple * tuple) { /* Hack: We cannot refresh segmented entries (since their info is read from * the cue sheet when it is first loaded), so leave them alone. -jlindgren */ - if (entry->segmented) - { - if (tuple != NULL) - tuple_free (tuple); - + if (entry->segmented && ! tuple) return; - } if (entry->tuple != NULL) tuple_free (entry->tuple); - - g_free (entry->title); entry->tuple = tuple; + g_free (entry->formatted); + stringpool_unref (entry->title); + stringpool_unref (entry->artist); + stringpool_unref (entry->album); + if (tuple == NULL) { + entry->formatted = NULL; entry->title = NULL; + entry->artist = NULL; + entry->album = NULL; entry->length = 0; entry->segmented = FALSE; entry->start = entry->end = -1; } else { - entry->title = title_from_tuple (tuple); + entry->formatted = title_from_tuple (tuple); + describe_song (entry->filename, tuple, & entry->title, & entry->artist, + & entry->album); entry->length = tuple_get_int (tuple, FIELD_LENGTH, NULL); entry->length = MAX (entry->length, 0); @@ -213,8 +203,7 @@ static void entry_set_tuple_real (struct entry * entry, Tuple * tuple) } } -static void entry_set_tuple (struct playlist * playlist, struct entry * entry, - Tuple * tuple) +static void entry_set_tuple (Playlist * playlist, Entry * entry, Tuple * tuple) { if (entry->tuple != NULL) { @@ -235,20 +224,24 @@ static void entry_set_tuple (struct playlist * playlist, struct entry * entry, } } -static void entry_set_failed (struct playlist * playlist, struct entry * entry) +static void entry_set_failed (Playlist * playlist, Entry * entry) { entry_set_tuple (playlist, entry, tuple_new_from_filename (entry->filename)); entry->failed = TRUE; } -static struct entry *entry_new(gchar * filename, InputPlugin * decoder, Tuple * tuple) +static Entry * entry_new (gchar * filename, PluginHandle * decoder, Tuple * + tuple) { - struct entry *entry = g_malloc(sizeof(struct entry)); + Entry * entry = g_malloc (sizeof (Entry)); entry->filename = filename; entry->decoder = decoder; entry->tuple = NULL; + entry->formatted = NULL; entry->title = NULL; + entry->artist = NULL; + entry->album = NULL; entry->failed = FALSE; entry->number = -1; entry->selected = FALSE; @@ -261,19 +254,21 @@ static struct entry *entry_new(gchar * filename, InputPlugin * decoder, Tuple * return entry; } -static void entry_free(struct entry *entry) +static void entry_free (Entry * entry) { g_free(entry->filename); if (entry->tuple != NULL) tuple_free(entry->tuple); - g_free(entry->title); - g_free(entry); + g_free (entry->formatted); + stringpool_unref (entry->title); + stringpool_unref (entry->artist); + stringpool_unref (entry->album); + g_free (entry); } -static void entry_check_has_decoder (struct playlist * playlist, struct entry * - entry) +static void entry_check_has_decoder (Playlist * playlist, Entry * entry) { if (entry->decoder != NULL || entry->failed) return; @@ -283,11 +278,12 @@ static void entry_check_has_decoder (struct playlist * playlist, struct entry * entry_set_failed (playlist, entry); } -static struct playlist *playlist_new(void) +static Playlist * playlist_new (void) { - struct playlist *playlist = g_malloc(sizeof(struct playlist)); + Playlist * playlist = g_malloc (sizeof (Playlist)); playlist->number = -1; + playlist->unique_id = next_unique_id ++; playlist->filename = NULL; playlist->title = g_strdup(_("Untitled Playlist")); playlist->entries = index_new(); @@ -301,7 +297,7 @@ static struct playlist *playlist_new(void) return playlist; } -static void playlist_free(struct playlist *playlist) +static void playlist_free (Playlist * playlist) { gint count; @@ -322,33 +318,35 @@ static void number_playlists(gint at, gint length) for (count = 0; count < length; count++) { - struct playlist *playlist = index_get(playlists, at + count); - + Playlist * playlist = index_get (playlists, at + count); playlist->number = at + count; } } -static struct playlist *lookup_playlist(gint playlist_num) +static Playlist * lookup_playlist (gint playlist_num) { + /* Not initted or already shut down */ + if (! playlists) + return NULL; + if (playlist_num < 0 || playlist_num >= index_count(playlists)) return NULL; return index_get(playlists, playlist_num); } -static void number_entries(struct playlist *playlist, gint at, gint length) +static void number_entries (Playlist * playlist, gint at, gint length) { gint count; for (count = 0; count < length; count++) { - struct entry *entry = index_get(playlist->entries, at + count); - + Entry * entry = index_get (playlist->entries, at + count); entry->number = at + count; } } -static struct entry *lookup_entry(struct playlist *playlist, gint entry_num) +static Entry * lookup_entry (Playlist * playlist, gint entry_num) { if (entry_num < 0 || entry_num >= index_count(playlist->entries)) return NULL; @@ -359,19 +357,55 @@ static struct entry *lookup_entry(struct playlist *playlist, gint entry_num) static gboolean update (void * unused) { hook_call ("playlist update", GINT_TO_POINTER (update_level)); - update_source = 0; - update_level = 0; return FALSE; } -static void queue_update (gint level) +static void queue_update (gint level, Playlist * list, gint at, gint count) { - update_level = MAX (update_level, level); - - if (update_source == 0) + if (! update_source) + { update_source = g_idle_add_full (G_PRIORITY_HIGH_IDLE, update, NULL, NULL); + update_level = 0; + update_playlist = list; + unchanged_before = list ? index_count (list->entries) : 0; + unchanged_after = list ? index_count (list->entries) : 0; + } + + update_level = MAX (update_level, level); + + if (list && list == update_playlist) + { + unchanged_before = MIN (unchanged_before, at); + unchanged_after = MIN (unchanged_after, index_count (list->entries) - at + - count); + } + else + { + update_playlist = NULL; + unchanged_before = 0; + unchanged_after = 0; + } +} + +gboolean playlist_update_pending (void) +{ + return update_source ? TRUE : FALSE; +} + +gboolean playlist_update_range (gint * playlist, gint * at, gint * count) +{ + g_return_val_if_fail (update_source, FALSE); + + if (! update_playlist) + return FALSE; + + * playlist = update_playlist->number; + * at = unchanged_before; + * count = index_count (update_playlist->entries) - unchanged_before - + unchanged_after; + return TRUE; } /* scan_mutex must be locked! */ @@ -379,13 +413,10 @@ void scan_receive (void) { for (gint i = 0; i < SCAN_THREADS; i ++) { - struct entry * entry; - if (! scan_filenames[i] || scan_decoders[i]) continue; /* thread not in use or still working */ - SCAN_DEBUG ("receive (#%d): %d\n", i, scan_positions[i]); - entry = index_get (active_playlist->entries, scan_positions[i]); + Entry * entry = index_get (active_playlist->entries, scan_positions[i]); if (scan_tuples[i]) entry_set_tuple (active_playlist, entry, scan_tuples[i]); @@ -395,7 +426,8 @@ void scan_receive (void) scan_filenames[i] = NULL; scan_tuples[i] = NULL; - updated_ago ++; + queue_update (PLAYLIST_UPDATE_METADATA, active_playlist, + scan_positions[i], 1); } } @@ -424,7 +456,7 @@ static gboolean scan_next (void * unused) for (; search < entries; search ++) { - struct entry * entry = index_get (active_playlist->entries, search); + Entry * entry = index_get (active_playlist->entries, search); if (entry->tuple) continue; @@ -433,7 +465,8 @@ static gboolean scan_next (void * unused) if (entry->failed) continue; - SCAN_DEBUG ("start (#%d): %d\n", i, search); + vfs_prepare_filename (entry->filename); + scan_positions[i] = search; scan_filenames[i] = entry->filename; scan_decoders[i] = entry->decoder; @@ -444,41 +477,29 @@ static gboolean scan_next (void * unused) } } - if (updated_ago >= 10 || (search == entries && updated_ago > 0)) - { - SCAN_DEBUG ("queue update\n"); - queue_update (PLAYLIST_UPDATE_METADATA); - updated_ago = 0; - } - g_mutex_unlock (scan_mutex); return FALSE; } static void scan_continue (void) { - SCAN_DEBUG ("scan_continue\n"); if (! scan_source) scan_source = g_idle_add_full (G_PRIORITY_LOW, scan_next, NULL, NULL); } static void scan_reset (void) { - SCAN_DEBUG ("scan_reset\n"); - for (gint i = 0; i < SCAN_THREADS; i ++) { assert (! scan_filenames[i]); /* scan in progress == very, very bad */ scan_positions[i] = -1; } - updated_ago = 0; scan_continue (); } static void scan_stop (void) { - SCAN_DEBUG ("scan_stop\n"); g_mutex_lock (scan_mutex); if (scan_source != 0) @@ -493,10 +514,7 @@ static void scan_stop (void) continue; while (scan_decoders[i]) - { - SCAN_DEBUG ("wait for stop (#%d)\n", i); g_cond_wait (scan_conds[i], scan_mutex); - } } scan_receive (); @@ -512,26 +530,20 @@ static void * scanner (void * data) while (1) { - SCAN_DEBUG ("scanner (#%d): wait\n", i); g_cond_wait (scan_conds[i], scan_mutex); if (scan_quit) break; if (! scan_filenames[i]) - { - SCAN_DEBUG ("scanner (#%d): idle\n", i); continue; - } - SCAN_DEBUG ("scanner (#%d): scan %s\n", i, scan_filenames[i]); scan_tuples[i] = file_read_tuple (scan_filenames[i], scan_decoders[i]); scan_decoders[i] = NULL; g_cond_signal (scan_conds[i]); scan_continue (); } - SCAN_DEBUG ("scanner (#%d): exit\n", i); g_mutex_unlock (scan_mutex); return NULL; } @@ -541,7 +553,7 @@ static void * scanner (void * data) * we are concerned with in the main thread, this is better in the long run * because the scanner can work on the following entries while the caller is * processing this one. */ -static gboolean scan_threaded (struct playlist * playlist, struct entry * entry) +static gboolean scan_threaded (Playlist * playlist, Entry * entry) { gint i; @@ -559,17 +571,14 @@ static gboolean scan_threaded (struct playlist * playlist, struct entry * entry) goto FOUND; } - SCAN_DEBUG ("manual scan of %d\n", entry->number); return FALSE; FOUND: - SCAN_DEBUG ("threaded scan (#%d) of %d\n", i, entry->number); g_mutex_lock (scan_mutex); scan_receive (); while (scan_filenames[i]) { - SCAN_DEBUG ("wait (#%d) for %d\n", i, entry->number); g_cond_wait (scan_conds[i], scan_mutex); scan_receive (); } @@ -578,7 +587,7 @@ FOUND: return TRUE; } -static void check_scanned (struct playlist * playlist, struct entry * entry) +static void check_scanned (Playlist * playlist, Entry * entry) { if (entry->tuple) return; @@ -594,21 +603,21 @@ static void check_scanned (struct playlist * playlist, struct entry * entry) if (! entry->tuple) entry_set_failed (playlist, entry); - queue_update (PLAYLIST_UPDATE_METADATA); + queue_update (PLAYLIST_UPDATE_METADATA, playlist, entry->number, 1); } -static void check_selected_scanned (struct playlist * playlist) +static void check_selected_scanned (Playlist * playlist) { gint entries = index_count (playlist->entries); for (gint count = 0; count < entries; count++) { - struct entry * entry = index_get (playlist->entries, count); + Entry * entry = index_get (playlist->entries, count); if (entry->selected) check_scanned (playlist, entry); } } -static void check_all_scanned (struct playlist * playlist) +static void check_all_scanned (Playlist * playlist) { gint entries = index_count (playlist->entries); for (gint count = 0; count < entries; count++) @@ -617,19 +626,16 @@ static void check_all_scanned (struct playlist * playlist) void playlist_init (void) { - struct playlist * playlist; - - srandom (time (NULL)); + srand (time (NULL)); playlists = index_new (); - playlist = playlist_new (); + Playlist * playlist = playlist_new (); index_append (playlists, playlist); playlist->number = 0; active_playlist = playlist; playing_playlist = NULL; update_source = 0; - update_level = 0; scan_mutex = g_mutex_new (); memset (scan_filenames, 0, sizeof scan_filenames); @@ -678,6 +684,9 @@ void playlist_end(void) playlist_free(index_get(playlists, count)); index_free(playlists); + playlists = NULL; + active_playlist = NULL; + playing_playlist = NULL; } gint playlist_count(void) @@ -699,8 +708,7 @@ void playlist_insert(gint at) number_playlists(at, index_count(playlists) - at); - PLAYLIST_HAS_CHANGED; - hook_call ("playlist insert", GINT_TO_POINTER (at)); + PLAYLIST_HAS_CHANGED (NULL, 0, 0); } void playlist_reorder (gint from, gint to, gint count) @@ -735,7 +743,7 @@ void playlist_reorder (gint from, gint to, gint count) index_free (displaced); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (NULL, 0, 0); } void playlist_delete (gint playlist_num) @@ -744,8 +752,6 @@ void playlist_delete (gint playlist_num) LOOKUP_PLAYLIST; - hook_call ("playlist delete", GINT_TO_POINTER (playlist_num)); - if (playlist == playing_playlist) { if (playback_get_playing ()) @@ -767,7 +773,29 @@ void playlist_delete (gint playlist_num) active_playlist = index_get (playlists, MIN (playlist_num, index_count (playlists) - 1)); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (NULL, 0, 0); +} + +gint playlist_get_unique_id (gint playlist_num) +{ + DECLARE_PLAYLIST; + LOOKUP_PLAYLIST_RET (-1); + return playlist->unique_id; +} + +gint playlist_by_unique_id (gint id) +{ + g_return_val_if_fail (playlists, -1); + gint n = index_count (playlists); + + for (gint i = 0; i < n; i ++) + { + Playlist * p = index_get (playlists, i); + if (p->unique_id == id) + return p->number; + } + + return -1; } void playlist_set_filename(gint playlist_num, const gchar * filename) @@ -780,7 +808,7 @@ void playlist_set_filename(gint playlist_num, const gchar * filename) g_free(playlist->filename); playlist->filename = g_strdup(filename); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (NULL, 0, 0); } const gchar *playlist_get_filename(gint playlist_num) @@ -802,7 +830,7 @@ void playlist_set_title(gint playlist_num, const gchar * title) g_free(playlist->title); playlist->title = g_strdup(title); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (NULL, 0, 0); } const gchar *playlist_get_title(gint playlist_num) @@ -823,7 +851,7 @@ void playlist_set_active(gint playlist_num) active_playlist = playlist; - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (NULL, 0, 0); } gint playlist_get_active(void) @@ -853,7 +881,7 @@ gint playlist_get_playing(void) /* If we are already at the song or it is already at the top of the shuffle * list, we let it be. Otherwise, we move it to the top. */ -static void set_position (struct playlist * playlist, struct entry * entry) +static void set_position (Playlist * playlist, Entry * entry) { if (entry == playlist->position) return; @@ -879,16 +907,16 @@ gint playlist_entry_count(gint playlist_num) return index_count(playlist->entries); } -static void make_entries (gchar * filename, InputPlugin * decoder, Tuple * +static void make_entries (gchar * filename, PluginHandle * decoder, Tuple * tuple, struct index * list) { uri_check_utf8 (& filename, TRUE); - if (tuple == NULL && decoder == NULL) + if (! tuple && ! decoder) decoder = file_find_decoder (filename, TRUE); - if (tuple == NULL && decoder != NULL && decoder->have_subtune && strchr - (filename, '?') == NULL) + if (! tuple && decoder && input_plugin_has_subtunes (decoder) && ! strchr + (filename, '?')) tuple = file_read_tuple (filename, decoder); if (tuple != NULL && tuple->nsubtunes > 0) @@ -916,53 +944,60 @@ void playlist_entry_insert(gint playlist_num, gint at, gchar * filename, Tuple * index_append(filenames, filename); index_append(tuples, tuple); - playlist_entry_insert_batch(playlist_num, at, filenames, tuples); + playlist_entry_insert_batch_with_decoders (playlist_num, at, filenames, + NULL, tuples); } -void playlist_entry_insert_batch(gint playlist_num, gint at, struct index *filenames, struct index *tuples) +void playlist_entry_insert_batch (gint playlist_num, gint at, + struct index * filenames, struct index * tuples) { - DECLARE_PLAYLIST; - gint entries, number, count; - struct index *add; + playlist_entry_insert_batch_with_decoders (playlist_num, at, filenames, + NULL, tuples); +} +void playlist_entry_insert_batch_with_decoders (gint playlist_num, gint at, + struct index * filenames, struct index * decoders, struct index * tuples) +{ + DECLARE_PLAYLIST; LOOKUP_PLAYLIST; PLAYLIST_WILL_CHANGE; - entries = index_count (playlist->entries); + gint entries = index_count (playlist->entries); if (at < 0 || at > entries) at = entries; - number = index_count(filenames); - add = index_new(); - - for (count = 0; count < number; count++) - make_entries(index_get(filenames, count), NULL, (tuples == NULL) ? NULL : index_get(tuples, count), add); - - index_free(filenames); + gint number = index_count (filenames); + struct index * add = index_new (); - if (tuples != NULL) - index_free(tuples); + /* Preallocate space to avoid reallocs. (The actual number of entries may + * turn out to be greater due to subtunes.) */ + index_allocate (add, number); - number = index_count(add); + for (gint count = 0; count < number; count ++) + make_entries (index_get (filenames, count), decoders ? index_get + (decoders, count) : NULL, tuples ? index_get (tuples, count) : NULL, + add); - if (at == entries) - index_merge_append(playlist->entries, add); - else - index_merge_insert(playlist->entries, at, add); + index_free (filenames); + if (decoders) + index_free (decoders); + if (tuples) + index_free (tuples); - index_free(add); + number = index_count (add); + index_merge_insert (playlist->entries, at, add); + index_free (add); number_entries(playlist, at, entries + number - at); - for (count = 0; count < number; count++) + for (gint count = 0; count < number; count ++) { - struct entry *entry = index_get(playlist->entries, at + count); - + Entry * entry = index_get (playlist->entries, at + count); playlist->total_length += entry->length; } - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (playlist, at, number); } void playlist_entry_delete(gint playlist_num, gint at, gint number) @@ -983,7 +1018,7 @@ void playlist_entry_delete(gint playlist_num, gint at, gint number) for (count = 0; count < number; count++) { - struct entry *entry = index_get(playlist->entries, at + count); + Entry * entry = index_get (playlist->entries, at + count); if (entry == playlist->position) { @@ -1005,13 +1040,13 @@ void playlist_entry_delete(gint playlist_num, gint at, gint number) entry_free(entry); } - index_delete(playlist->entries, at, number); - number_entries(playlist, at, entries - number - at); + index_delete (playlist->entries, at, number); + number_entries (playlist, at, entries - at - number); if (stop && playback_get_playing ()) playback_stop (); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (playlist, at, 0); } const gchar *playlist_entry_get_filename(gint playlist_num, gint entry_num) @@ -1023,13 +1058,14 @@ const gchar *playlist_entry_get_filename(gint playlist_num, gint entry_num) return entry->filename; } -InputPlugin *playlist_entry_get_decoder(gint playlist_num, gint entry_num) +PluginHandle * playlist_entry_get_decoder (gint playlist_num, gint entry_num, + gboolean fast) { DECLARE_PLAYLIST_ENTRY; - LOOKUP_PLAYLIST_ENTRY_RET (NULL); - entry_check_has_decoder (playlist, entry); + if (! fast) + entry_check_has_decoder (playlist, entry); return entry->decoder; } @@ -1043,7 +1079,7 @@ void playlist_entry_set_tuple (gint playlist_num, gint entry_num, Tuple * tuple) entry_set_tuple (playlist, entry, tuple); - METADATA_HAS_CHANGED; + METADATA_HAS_CHANGED (playlist, entry_num, 1); } const Tuple * playlist_entry_get_tuple (gint playlist_num, gint entry_num, @@ -1067,7 +1103,28 @@ const gchar * playlist_entry_get_title (gint playlist_num, gint entry_num, if (! fast) check_scanned (playlist, entry); - return (entry->title == NULL) ? entry->filename : entry->title; + return (entry->formatted == NULL) ? entry->filename : entry->formatted; +} + +void playlist_entry_describe (gint playlist_num, gint entry_num, + const gchar * * title, const gchar * * artist, const gchar * * album, + gboolean fast) +{ + * title = * artist = * album = NULL; + DECLARE_PLAYLIST_ENTRY; + LOOKUP_PLAYLIST_ENTRY; + + if (! fast) + check_scanned (playlist, entry); + + if (entry->title) + { + * title = entry->title; + * artist = entry->artist; + * album = entry->album; + } + else + * title = entry->filename; } gint playlist_entry_get_length (gint playlist_num, gint entry_num, gboolean fast) @@ -1125,8 +1182,6 @@ void playlist_set_position (gint playlist_num, gint entry_num) set_position (playlist, entry); - SELECTION_HAS_CHANGED; - hook_call ("playlist position", GINT_TO_POINTER (playlist_num)); } @@ -1161,7 +1216,7 @@ void playlist_entry_set_selected(gint playlist_num, gint entry_num, gboolean sel playlist->selected_length -= entry->length; } - SELECTION_HAS_CHANGED; + SELECTION_HAS_CHANGED (playlist, entry_num, 1); } gboolean playlist_entry_get_selected(gint playlist_num, gint entry_num) @@ -1182,20 +1237,24 @@ gint playlist_selected_count(gint playlist_num) return playlist->selected_count; } -void playlist_select_all(gint playlist_num, gboolean selected) +void playlist_select_all (gint playlist_num, gboolean selected) { DECLARE_PLAYLIST; - gint entries, count; - LOOKUP_PLAYLIST; - entries = index_count(playlist->entries); + gint entries = index_count (playlist->entries); + gint first = entries, last = 0; - for (count = 0; count < entries; count++) + for (gint count = 0; count < entries; count ++) { - struct entry *entry = index_get(playlist->entries, count); + Entry * entry = index_get (playlist->entries, count); - entry->selected = selected; + if ((selected && ! entry->selected) || (entry->selected && ! selected)) + { + entry->selected = selected; + first = MIN (first, entry->number); + last = entry->number; + } } if (selected) @@ -1209,80 +1268,86 @@ void playlist_select_all(gint playlist_num, gboolean selected) playlist->selected_length = 0; } - SELECTION_HAS_CHANGED; + if (first < entries) + SELECTION_HAS_CHANGED (playlist, first, last + 1 - first); } gint playlist_shift (gint playlist_num, gint entry_num, gint distance) { DECLARE_PLAYLIST_ENTRY; - gint entries, first, last, shift, count; - struct index *move, *others; - LOOKUP_PLAYLIST_ENTRY_RET (0); - if (! entry->selected) + if (! entry->selected || ! distance) return 0; PLAYLIST_WILL_CHANGE; - entries = index_count(playlist->entries); - shift = 0; + gint entries = index_count (playlist->entries); + gint shift = 0, center, top, bottom; - for (first = entry_num; first > 0; first--) + if (distance < 0) { - entry = index_get(playlist->entries, first - 1); - - if (!entry->selected) + for (center = entry_num; center > 0 && shift > distance; ) { - if (shift <= distance) - break; - - shift--; + entry = index_get (playlist->entries, -- center); + if (! entry->selected) + shift --; } } - - for (last = entry_num; last < entries - 1; last++) + else { - entry = index_get(playlist->entries, last + 1); - - if (!entry->selected) + for (center = entry_num + 1; center < entries && shift < distance; ) { - if (shift >= distance) - break; - - shift++; + entry = index_get (playlist->entries, center ++); + if (! entry->selected) + shift ++; } } - move = index_new(); - others = index_new(); + top = bottom = center; - for (count = first; count <= last; count++) + for (gint i = 0; i < top; i ++) { - entry = index_get(playlist->entries, count); - index_append(entry->selected ? move : others, entry); + entry = index_get (playlist->entries, i); + if (entry->selected) + top = i; } - if (shift < 0) + for (gint i = entries; i > bottom; i --) { - index_merge_append(move, others); - index_free(others); + entry = index_get (playlist->entries, i - 1); + if (entry->selected) + bottom = i; } - else + + struct index * temp = index_new (); + + for (gint i = top; i < center; i ++) + { + entry = index_get (playlist->entries, i); + if (! entry->selected) + index_append (temp, entry); + } + + for (gint i = top; i < bottom; i ++) { - index_merge_append(others, move); - index_free(move); - move = others; + entry = index_get (playlist->entries, i); + if (entry->selected) + index_append (temp, entry); } - for (count = first; count <= last; count++) - index_set(playlist->entries, count, index_get(move, count - first)); + for (gint i = center; i < bottom; i ++) + { + entry = index_get (playlist->entries, i); + if (! entry->selected) + index_append (temp, entry); + } - index_free(move); + index_copy_set (temp, 0, playlist->entries, top, bottom - top); - number_entries(playlist, first, 1 + last - first); + number_entries (playlist, top, bottom - top); + PLAYLIST_HAS_CHANGED (playlist, top, bottom - top); - PLAYLIST_HAS_CHANGED; return shift; } @@ -1301,7 +1366,7 @@ void playlist_delete_selected(gint playlist_num) for (count = 0; count < entries; count++) { - struct entry *entry = index_get(playlist->entries, count); + Entry * entry = index_get (playlist->entries, count); if (entry->selected) { @@ -1332,7 +1397,7 @@ void playlist_delete_selected(gint playlist_num) if (stop && playback_get_playing ()) playback_stop (); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (playlist, 0, index_count (playlist->entries)); } void playlist_reverse(gint playlist_num) @@ -1356,7 +1421,7 @@ void playlist_reverse(gint playlist_num) number_entries(playlist, 0, entries); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (playlist, 0, entries); } void playlist_randomize (gint playlist_num) @@ -1369,7 +1434,7 @@ void playlist_randomize (gint playlist_num) for (gint i = 0; i < entries; i ++) { - gint j = i + random () % (entries - i); + gint j = i + rand () % (entries - i); struct entry * entry = index_get (playlist->entries, j); index_set (playlist->entries, j, index_get (playlist->entries, i)); @@ -1377,12 +1442,12 @@ void playlist_randomize (gint playlist_num) } number_entries (playlist, 0, entries); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (playlist, 0, entries); } static gint filename_compare (const void * _a, const void * _b, void * _compare) { - const struct entry * a = _a, * b = _b; + const Entry * a = _a, * b = _b; gint (* compare) (const gchar * a, const gchar * b) = _compare; gint diff = compare (a->filename, b->filename); @@ -1395,7 +1460,7 @@ static gint filename_compare (const void * _a, const void * _b, void * _compare) static gint tuple_compare (const void * _a, const void * _b, void * _compare) { - const struct entry * a = _a, * b = _b; + const Entry * a = _a, * b = _b; gint (* compare) (const Tuple * a, const Tuple * b) = _compare; if (a->tuple == NULL) @@ -1411,7 +1476,21 @@ static gint tuple_compare (const void * _a, const void * _b, void * _compare) return a->number - b->number; } -static void sort (struct playlist * playlist, gint (* compare) (const void * a, +static gint title_compare (const void * _a, const void * _b, void * _compare) +{ + const Entry * a = _a, * b = _b; + gint (* compare) (const gchar * a, const gchar * b) = _compare; + + gint diff = compare (a->formatted ? a->formatted : a->filename, b->formatted + ? b->formatted : b->filename); + if (diff) + return diff; + + /* preserve order of "equal" entries */ + return a->number - b->number; +} + +static void sort (Playlist * playlist, gint (* compare) (const void * a, const void * b, void * inner), void * inner) { PLAYLIST_WILL_CHANGE; @@ -1419,11 +1498,11 @@ static void sort (struct playlist * playlist, gint (* compare) (const void * a, index_sort_with_data (playlist->entries, compare, inner); number_entries (playlist, 0, index_count (playlist->entries)); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (playlist, 0, index_count (playlist->entries)); } -static void sort_selected (struct playlist * playlist, gint (* compare) - (const void * a, const void * b, void * inner), void * inner) +static void sort_selected (Playlist * playlist, gint (* compare) (const void * + a, const void * b, void * inner), void * inner) { gint entries, count, count2; struct index *selected; @@ -1435,8 +1514,7 @@ static void sort_selected (struct playlist * playlist, gint (* compare) for (count = 0; count < entries; count++) { - struct entry *entry = index_get(playlist->entries, count); - + Entry * entry = index_get (playlist->entries, count); if (entry->selected) index_append(selected, entry); } @@ -1447,8 +1525,7 @@ static void sort_selected (struct playlist * playlist, gint (* compare) for (count = 0; count < entries; count++) { - struct entry *entry = index_get(playlist->entries, count); - + Entry * entry = index_get (playlist->entries, count); if (entry->selected) index_set(playlist->entries, count, index_get(selected, count2++)); } @@ -1456,7 +1533,7 @@ static void sort_selected (struct playlist * playlist, gint (* compare) index_free(selected); number_entries(playlist, 0, entries); - PLAYLIST_HAS_CHANGED; + PLAYLIST_HAS_CHANGED (playlist, 0, entries); } void playlist_sort_by_filename (gint playlist_num, gint (* compare) @@ -1478,6 +1555,15 @@ void playlist_sort_by_tuple (gint playlist_num, gint (* compare) sort (playlist, tuple_compare, compare); } +void playlist_sort_by_title (gint playlist_num, gint (* compare) (const gchar * + a, const gchar * b)) +{ + DECLARE_PLAYLIST; + LOOKUP_PLAYLIST; + check_all_scanned (playlist); + sort (playlist, title_compare, compare); +} + void playlist_sort_selected_by_filename (gint playlist_num, gint (* compare) (const gchar * a, const gchar * b)) { @@ -1497,6 +1583,15 @@ void playlist_sort_selected_by_tuple (gint playlist_num, gint (* compare) sort_selected (playlist, tuple_compare, compare); } +void playlist_sort_selected_by_title (gint playlist_num, gint (* compare) + (const gchar * a, const gchar * b)) +{ + DECLARE_PLAYLIST; + LOOKUP_PLAYLIST; + check_selected_scanned (playlist); + sort (playlist, title_compare, compare); +} + void playlist_reformat_titles (void) { gint playlist_num; @@ -1505,23 +1600,22 @@ void playlist_reformat_titles (void) for (playlist_num = 0; playlist_num < index_count(playlists); playlist_num++) { - struct playlist *playlist = index_get(playlists, playlist_num); + Playlist * playlist = index_get (playlists, playlist_num); gint entries = index_count(playlist->entries); gint count; for (count = 0; count < entries; count++) { - struct entry *entry = index_get(playlist->entries, count); - - g_free(entry->title); - entry->title = (entry->tuple == NULL) ? NULL : title_from_tuple(entry->tuple); + Entry * entry = index_get (playlist->entries, count); + g_free(entry->formatted); + entry->formatted = (entry->tuple == NULL) ? NULL : title_from_tuple(entry->tuple); } } - METADATA_HAS_CHANGED; + METADATA_HAS_CHANGED (NULL, 0, 0); } -void playlist_rescan(gint playlist_num) +void playlist_rescan_real (gint playlist_num, gboolean onlyselected) { DECLARE_PLAYLIST; gint entries, count; @@ -1533,13 +1627,25 @@ void playlist_rescan(gint playlist_num) for (count = 0; count < entries; count++) { - struct entry *entry = index_get(playlist->entries, count); - - entry_set_tuple (playlist, entry, NULL); - entry->failed = FALSE; + Entry * entry = index_get (playlist->entries, count); + if (! onlyselected || entry->selected) + { + entry_set_tuple (playlist, entry, NULL); + entry->failed = FALSE; + } } - METADATA_HAS_CHANGED; + METADATA_HAS_CHANGED (playlist, 0, entries); +} + +void playlist_rescan (gint playlist_num) +{ + playlist_rescan_real (playlist_num, FALSE); +} + +void playlist_rescan_selected (gint playlist_num) +{ + playlist_rescan_real (playlist_num, TRUE); } void playlist_rescan_file (const gchar * filename) @@ -1555,13 +1661,13 @@ void playlist_rescan_file (const gchar * filename) for (playlist_num = 0; playlist_num < num_playlists; playlist_num ++) { - struct playlist * playlist = index_get (playlists, playlist_num); + Playlist * playlist = index_get (playlists, playlist_num); gint num_entries = index_count (playlist->entries); gint entry_num; for (entry_num = 0; entry_num < num_entries; entry_num ++) { - struct entry * entry = index_get (playlist->entries, entry_num); + Entry * entry = index_get (playlist->entries, entry_num); if (! strcmp (entry->filename, filename)) { @@ -1573,7 +1679,7 @@ void playlist_rescan_file (const gchar * filename) g_free (copy); - METADATA_HAS_CHANGED; + METADATA_HAS_CHANGED (NULL, 0, 0); } gint64 playlist_get_total_length (gint playlist_num, gboolean fast) @@ -1623,20 +1729,20 @@ void playlist_queue_insert(gint playlist_num, gint at, gint entry_num) entry->queued = TRUE; - SELECTION_HAS_CHANGED; + SELECTION_HAS_CHANGED (playlist, entry_num, 1); } void playlist_queue_insert_selected (gint playlist_num, gint at) { DECLARE_PLAYLIST; - gint entries, count; - LOOKUP_PLAYLIST; - entries = index_count(playlist->entries); - for (count = 0; count < entries; count++) + gint entries = index_count(playlist->entries); + gint first = entries, last = 0; + + for (gint count = 0; count < entries; count++) { - struct entry *entry = index_get(playlist->entries, count); + Entry * entry = index_get (playlist->entries, count); if (!entry->selected || entry->queued) continue; @@ -1647,16 +1753,18 @@ void playlist_queue_insert_selected (gint playlist_num, gint at) playlist->queued = g_list_insert(playlist->queued, entry, at++); entry->queued = TRUE; + first = MIN (first, entry->number); + last = entry->number; } - SELECTION_HAS_CHANGED; + if (first < entries) + SELECTION_HAS_CHANGED (playlist, first, last + 1 - first); } gint playlist_queue_get_entry(gint playlist_num, gint at) { DECLARE_PLAYLIST; GList *node; - struct entry *entry; LOOKUP_PLAYLIST_RET (-1); node = g_list_nth(playlist->queued, at); @@ -1664,8 +1772,7 @@ gint playlist_queue_get_entry(gint playlist_num, gint at) if (node == NULL) return -1; - entry = node->data; - return entry->number; + return ((Entry *) node->data)->number; } gint playlist_queue_find_entry(gint playlist_num, gint entry_num) @@ -1683,18 +1790,22 @@ gint playlist_queue_find_entry(gint playlist_num, gint entry_num) void playlist_queue_delete(gint playlist_num, gint at, gint number) { DECLARE_PLAYLIST; - LOOKUP_PLAYLIST; + gint entries = index_count (playlist->entries); + gint first = entries, last = 0; + if (at == 0) { while (playlist->queued != NULL && number--) { - struct entry *entry = playlist->queued->data; + Entry * entry = playlist->queued->data; playlist->queued = g_list_delete_link(playlist->queued, playlist->queued); entry->queued = FALSE; + first = MIN (first, entry->number); + last = entry->number; } } else @@ -1706,16 +1817,19 @@ void playlist_queue_delete(gint playlist_num, gint at, gint number) while (anchor->next != NULL && number--) { - struct entry *entry = anchor->next->data; + Entry * entry = anchor->next->data; playlist->queued = g_list_delete_link(playlist->queued, anchor->next); entry->queued = FALSE; + first = MIN (first, entry->number); + last = entry->number; } } DONE: - SELECTION_HAS_CHANGED; + if (first < entries) + SELECTION_HAS_CHANGED (playlist, first, last + 1 - first); } void playlist_queue_delete_selected (gint playlist_num) @@ -1723,26 +1837,36 @@ void playlist_queue_delete_selected (gint playlist_num) DECLARE_PLAYLIST; LOOKUP_PLAYLIST; + gint entries = index_count (playlist->entries); + gint first = entries, last = 0; + for (GList * node = playlist->queued; node != NULL; ) { GList * next = node->next; - struct entry * entry = node->data; + Entry * entry = node->data; + if (entry->selected) + { playlist->queued = g_list_delete_link (playlist->queued, node); + first = MIN (first, entry->number); + last = entry->number; + } + node = next; } - SELECTION_HAS_CHANGED; + if (first < entries) + SELECTION_HAS_CHANGED (playlist, first, last + 1 - first); } -static gboolean shuffle_prev (struct playlist * playlist) +static gboolean shuffle_prev (Playlist * playlist) { gint entries = index_count (playlist->entries), count; - struct entry * found = NULL; + Entry * found = NULL; for (count = 0; count < entries; count ++) { - struct entry * entry = index_get (playlist->entries, count); + Entry * entry = index_get (playlist->entries, count); if (entry->shuffle_num && (playlist->position == NULL || entry->shuffle_num < playlist->position->shuffle_num) && (found == NULL @@ -1780,20 +1904,18 @@ gboolean playlist_prev_song(gint playlist_num) if (playlist == playing_playlist && playback_get_playing ()) playback_stop(); - SELECTION_HAS_CHANGED; - hook_call ("playlist position", GINT_TO_POINTER (playlist_num)); return TRUE; } -static gboolean shuffle_next (struct playlist * playlist) +static gboolean shuffle_next (Playlist * playlist) { gint entries = index_count (playlist->entries), choice = 0, count; - struct entry * found = NULL; + Entry * found = NULL; for (count = 0; count < entries; count ++) { - struct entry * entry = index_get (playlist->entries, count); + Entry * entry = index_get (playlist->entries, count); if (! entry->shuffle_num) choice ++; @@ -1812,11 +1934,11 @@ static gboolean shuffle_next (struct playlist * playlist) if (! choice) return FALSE; - choice = random () % choice; + choice = rand () % choice; for (count = 0; ; count ++) { - struct entry * entry = index_get (playlist->entries, count); + Entry * entry = index_get (playlist->entries, count); if (! entry->shuffle_num) { @@ -1831,7 +1953,7 @@ static gboolean shuffle_next (struct playlist * playlist) } } -static void shuffle_reset (struct playlist * playlist) +static void shuffle_reset (Playlist * playlist) { gint entries = index_count (playlist->entries), count; @@ -1839,8 +1961,7 @@ static void shuffle_reset (struct playlist * playlist) for (count = 0; count < entries; count ++) { - struct entry * entry = index_get (playlist->entries, count); - + Entry * entry = index_get (playlist->entries, count); entry->shuffle_num = 0; } } @@ -1896,17 +2017,10 @@ gboolean playlist_next_song(gint playlist_num, gboolean repeat) if (playlist == playing_playlist && playback_get_playing ()) playback_stop(); - SELECTION_HAS_CHANGED; - hook_call ("playlist position", GINT_TO_POINTER (playlist_num)); return TRUE; } -gboolean playlist_update_pending (void) -{ - return (update_source != 0); -} - void playlist_save_state (void) { gchar scratch[512]; @@ -1914,7 +2028,7 @@ void playlist_save_state (void) gint playlist_num; snprintf (scratch, sizeof scratch, "%s/" STATE_FILE, - aud_paths[BMP_PATH_USER_DIR]); + get_path (AUD_PATH_USER_DIR)); handle = fopen (scratch, "w"); if (handle == NULL) @@ -1926,7 +2040,7 @@ void playlist_save_state (void) for (playlist_num = 0; playlist_num < index_count (playlists); playlist_num ++) { - struct playlist * playlist = index_get (playlists, playlist_num); + Playlist * playlist = index_get (playlists, playlist_num); gint entries = index_count (playlist->entries), count; fprintf (handle, "playlist %d\n", playlist_num); @@ -1935,8 +2049,7 @@ void playlist_save_state (void) for (count = 0; count < entries; count ++) { - struct entry * entry = index_get (playlist->entries, count); - + Entry * entry = index_get (playlist->entries, count); fprintf (handle, "S %d\n", entry->shuffle_num); } } @@ -1980,10 +2093,10 @@ void playlist_load_state (void) { gchar scratch[512]; FILE * handle; - gint playlist_num, obsolete = 0; + gint playlist_num; snprintf (scratch, sizeof scratch, "%s/" STATE_FILE, - aud_paths[BMP_PATH_USER_DIR]); + get_path (AUD_PATH_USER_DIR)); handle = fopen (scratch, "r"); if (handle == NULL) @@ -2006,7 +2119,7 @@ void playlist_load_state (void) while (parse_integer ("playlist", & playlist_num) && playlist_num >= 0 && playlist_num < index_count (playlists)) { - struct playlist * playlist = index_get (playlists, playlist_num); + Playlist * playlist = index_get (playlists, playlist_num); gint entries = index_count (playlist->entries), position, count; parse_next (handle); @@ -2017,6 +2130,7 @@ void playlist_load_state (void) if (position >= 0 && position < entries) playlist->position = index_get (playlist->entries, position); + gint obsolete = 0; if (parse_integer ("shuffled", & obsolete)) /* compatibility with 2.3 */ parse_next (handle); @@ -2027,8 +2141,7 @@ void playlist_load_state (void) for (count = 0; count < entries; count ++) { - struct entry * entry = index_get (playlist->entries, count); - + Entry * entry = index_get (playlist->entries, count); if (parse_integer ("S", & entry->shuffle_num)) parse_next (handle); } diff --git a/src/audacious/playlist-utils.c b/src/audacious/playlist-utils.c index 0c795c7..cbb001f 100644 --- a/src/audacious/playlist-utils.c +++ b/src/audacious/playlist-utils.c @@ -26,10 +26,8 @@ #include <libaudcore/audstrings.h> #include "audconfig.h" -#include "main.h" #include "misc.h" #include "playlist.h" -#include "playlist_container.h" #include "playlist-utils.h" static const gchar * aud_titlestring_presets[] = @@ -109,14 +107,15 @@ static gint tuple_compare_track (const Tuple * a, const Tuple * b) return tuple_compare_int (a, b, FIELD_TRACK_NUMBER); } -static const PlaylistFilenameCompareFunc filename_comparisons[] = { +static const PlaylistStringCompareFunc filename_comparisons[] = { [PLAYLIST_SORT_PATH] = string_compare_encoded, [PLAYLIST_SORT_FILENAME] = filename_compare_basename, [PLAYLIST_SORT_TITLE] = NULL, [PLAYLIST_SORT_ALBUM] = NULL, [PLAYLIST_SORT_ARTIST] = NULL, [PLAYLIST_SORT_DATE] = NULL, - [PLAYLIST_SORT_TRACK] = NULL}; + [PLAYLIST_SORT_TRACK] = NULL, + [PLAYLIST_SORT_FORMATTED_TITLE] = NULL}; static const PlaylistTupleCompareFunc tuple_comparisons[] = { [PLAYLIST_SORT_PATH] = NULL, @@ -125,7 +124,18 @@ static const PlaylistTupleCompareFunc tuple_comparisons[] = { [PLAYLIST_SORT_ALBUM] = tuple_compare_album, [PLAYLIST_SORT_ARTIST] = tuple_compare_artist, [PLAYLIST_SORT_DATE] = tuple_compare_date, - [PLAYLIST_SORT_TRACK] = tuple_compare_track}; + [PLAYLIST_SORT_TRACK] = tuple_compare_track, + [PLAYLIST_SORT_FORMATTED_TITLE] = NULL}; + +static const PlaylistStringCompareFunc title_comparisons[] = { + [PLAYLIST_SORT_PATH] = NULL, + [PLAYLIST_SORT_FILENAME] = NULL, + [PLAYLIST_SORT_TITLE] = NULL, + [PLAYLIST_SORT_ALBUM] = NULL, + [PLAYLIST_SORT_ARTIST] = NULL, + [PLAYLIST_SORT_DATE] = NULL, + [PLAYLIST_SORT_TRACK] = NULL, + [PLAYLIST_SORT_FORMATTED_TITLE] = string_compare}; const gchar * get_gentitle_format (void) { @@ -142,6 +152,8 @@ void playlist_sort_by_scheme (gint playlist, gint scheme) playlist_sort_by_filename (playlist, filename_comparisons[scheme]); else if (tuple_comparisons[scheme] != NULL) playlist_sort_by_tuple (playlist, tuple_comparisons[scheme]); + else if (title_comparisons[scheme] != NULL) + playlist_sort_by_title (playlist, title_comparisons[scheme]); } void playlist_sort_selected_by_scheme (gint playlist, gint scheme) @@ -151,6 +163,8 @@ void playlist_sort_selected_by_scheme (gint playlist, gint scheme) filename_comparisons[scheme]); else if (tuple_comparisons[scheme] != NULL) playlist_sort_selected_by_tuple (playlist, tuple_comparisons[scheme]); + else if (title_comparisons[scheme] != NULL) + playlist_sort_selected_by_title (playlist, title_comparisons[scheme]); } /* Fix me: This considers empty fields as duplicates. */ @@ -216,8 +230,7 @@ void playlist_remove_failed (gint playlist) for (count = 0; count < entries; count ++) { - if (playlist_entry_get_decoder (playlist, count) == NULL || - playlist_entry_get_tuple (playlist, count, FALSE) == NULL) + if (! playlist_entry_get_decoder (playlist, count, FALSE)) playlist_entry_set_selected (playlist, count, TRUE); } @@ -275,65 +288,14 @@ void playlist_select_by_patterns (gint playlist, const Tuple * patterns) } } -gboolean filename_is_playlist (const gchar * filename) -{ - const gchar * period = strrchr (filename, '.'); - - return (period != NULL && playlist_container_find ((gchar *) period + 1) != - NULL); -} - -gboolean playlist_insert_playlist (gint playlist, gint at, const gchar * - filename) -{ - const gchar * period = strrchr (filename, '.'); - PlaylistContainer * container; - gint last; - - if (period == NULL) - return FALSE; - - container = playlist_container_find ((gchar *) period + 1); - - if (container == NULL || container->plc_read == NULL) - return FALSE; - - last = playlist_get_active (); - playlist_set_active (playlist); - container->plc_read (filename, at); - playlist_set_active (last); - return TRUE; -} - -gboolean playlist_save (gint playlist, const gchar * filename) -{ - const gchar * period = strrchr (filename, '.'); - PlaylistContainer * container; - gint last; - - if (period == NULL) - return FALSE; - - container = playlist_container_find ((gchar *) period + 1); - - if (container == NULL || container->plc_write == NULL) - return FALSE; - - last = playlist_get_active (); - playlist_set_active (playlist); - container->plc_write (filename, 0); - playlist_set_active (last); - return TRUE; -} - /* The algorithm is a bit quirky for historical reasons. -jlindgren */ static gchar * make_playlist_path (gint playlist) { if (! playlist) - return g_strdup (aud_paths[BMP_PATH_PLAYLIST_FILE]); + return g_strdup (get_path (AUD_PATH_PLAYLIST_FILE)); return g_strdup_printf ("%s/playlist_%02d.xspf", - aud_paths[BMP_PATH_PLAYLISTS_DIR], 1 + playlist); + get_path (AUD_PATH_PLAYLISTS_DIR), 1 + playlist); } void load_playlists (void) diff --git a/src/audacious/playlist.h b/src/audacious/playlist.h index 13f9d95..9b0b555 100644 --- a/src/audacious/playlist.h +++ b/src/audacious/playlist.h @@ -56,9 +56,11 @@ enum { PLAYLIST_SORT_ARTIST, PLAYLIST_SORT_DATE, PLAYLIST_SORT_TRACK, + PLAYLIST_SORT_FORMATTED_TITLE, PLAYLIST_SORT_SCHEMES}; -typedef gint (* PlaylistFilenameCompareFunc) (const gchar * a, const gchar * b); +#define PlaylistFilenameCompareFunc PlaylistStringCompareFunc /* deprecated */ +typedef gint (* PlaylistStringCompareFunc) (const gchar * a, const gchar * b); typedef gint (* PlaylistTupleCompareFunc) (const Tuple * a, const Tuple * b); #define AUD_API_NAME PlaylistAPI @@ -78,7 +80,8 @@ void playlist_save_state (void); void playlist_reformat_titles (void); -InputPlugin * playlist_entry_get_decoder (gint playlist, gint entry); +void playlist_entry_insert_batch_with_decoders (gint playlist, gint at, + struct index * filenames, struct index * decoders, struct index * tuples); void playlist_entry_set_tuple (gint playlist, gint entry, Tuple * tuple); gboolean playlist_entry_is_segmented (gint playlist, gint entry); diff --git a/src/audacious/playlist_container.c b/src/audacious/playlist_container.c deleted file mode 100644 index 0462347..0000000 --- a/src/audacious/playlist_container.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Audacious: A cross-platform multimedia player - * Copyright (c) 2006-2007 William Pitcock, Tony Vroon, George Averill, - * Giacomo Lozito, Derek Pomery and Yoshiki Yazawa. - * - * 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ - -#include <glib.h> -#include <string.h> - -#include "misc.h" -#include "playlist_container.h" - -/* - * PlaylistContainer objects handle the import and export of Playlist - * data. Basically, a PlaylistContainer acts as a filter for a PlaylistEntry. - */ - -static GList *registered_plcs = NULL; - -void playlist_container_register(PlaylistContainer *plc) -{ - registered_plcs = g_list_append(registered_plcs, plc); -} - -void playlist_container_unregister(PlaylistContainer *plc) -{ - registered_plcs = g_list_remove(registered_plcs, plc); -} - -PlaylistContainer *playlist_container_find(gchar *ext) -{ - GList *node; - PlaylistContainer *plc; - - /* check ext neither is NULL nor 1 (in a consequence of optimization). */ - g_return_val_if_fail(ext != NULL && ext != (void *)1, NULL); - - for (node = registered_plcs; node != NULL; node = g_list_next(node)) { - plc = node->data; - - if (!g_ascii_strncasecmp(plc->ext, ext, strlen(plc->ext))) - return plc; - } - - return NULL; -} - -void playlist_container_read(gchar *filename, gint pos) -{ - gchar *ext = strrchr(filename, '.') + 1; /* optimization: skip past the dot -nenolod */ - PlaylistContainer *plc = playlist_container_find(ext); - - if (plc->plc_read == NULL) - return; - - plc->plc_read(filename, pos); -} - -void playlist_container_write(gchar *filename, gint pos) -{ - gchar *ext = strrchr(filename, '.') + 1; /* optimization: skip past the dot -nenolod */ - PlaylistContainer *plc = playlist_container_find(ext); - - if (plc->plc_write == NULL) - return; - - plc->plc_write(filename, pos); -} diff --git a/src/audacious/playlist_container.h b/src/audacious/playlist_container.h deleted file mode 100644 index c86928d..0000000 --- a/src/audacious/playlist_container.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Audacious: A cross-platform multimedia player - * Copyright (c) 2006-2007 William Pitcock, Tony Vroon, George Averill, - * Giacomo Lozito, Derek Pomery and Yoshiki Yazawa. - * - * 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ - -#ifndef AUDACIOUS_PLAYLIST_CONTAINER_H -#define AUDACIOUS_PLAYLIST_CONTAINER_H - -#include <glib.h> - -#include "misc.h" - -void playlist_container_read (gchar * filename, gint pos); -void playlist_container_write (gchar * filename, gint pos); -PlaylistContainer * playlist_container_find (gchar * ext); - -#endif /* AUDACIOUS_PLAYLIST_CONTAINER_H */ diff --git a/src/audacious/plugin-init.c b/src/audacious/plugin-init.c new file mode 100644 index 0000000..a12ea11 --- /dev/null +++ b/src/audacious/plugin-init.c @@ -0,0 +1,298 @@ +/* + * plugin-init.c + * Copyright 2010 John Lindgren + * + * This file is part of Audacious. + * + * Audacious 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 or version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <glib.h> + +#include "debug.h" +#include "effect.h" +#include "general.h" +#include "interface.h" +#include "output.h" +#include "plugin.h" +#include "plugins.h" +#include "ui_preferences.h" +#include "visualization.h" + +static gboolean dummy_plugin_start (PluginHandle * p) +{ + return TRUE; +} + +static void dummy_plugin_stop (PluginHandle * p) +{ +} + +static const struct { + const gchar * name; + gboolean is_managed, is_single; + + union { + struct { + gboolean (* start) (PluginHandle * plugin); + void (* stop) (PluginHandle * plugin); + } m; + + struct { + PluginHandle * (* probe) (void); + PluginHandle * (* get_current) (void); + gboolean (* set_current) (PluginHandle * plugin); + } s; + } u; +} table[PLUGIN_TYPES] = { + [PLUGIN_TYPE_LOWLEVEL] = {"lowlevel", FALSE}, + [PLUGIN_TYPE_TRANSPORT] = {"transport", TRUE, FALSE, .u.m = + {dummy_plugin_start, dummy_plugin_stop}}, + [PLUGIN_TYPE_PLAYLIST] = {"playlist", TRUE, FALSE, .u.m = {dummy_plugin_start, + dummy_plugin_stop}}, + [PLUGIN_TYPE_INPUT] = {"input", TRUE, FALSE, .u.m = {dummy_plugin_start, + dummy_plugin_stop}}, + [PLUGIN_TYPE_EFFECT] = {"effect", TRUE, FALSE, .u.m = {effect_plugin_start, + effect_plugin_stop}}, + [PLUGIN_TYPE_OUTPUT] = {"output", TRUE, TRUE, .u.s = {output_plugin_probe, + output_plugin_get_current, output_plugin_set_current}}, + [PLUGIN_TYPE_VIS] = {"visualization", TRUE, FALSE, .u.m = {vis_plugin_start, + vis_plugin_stop}}, + [PLUGIN_TYPE_GENERAL] = {"general", TRUE, FALSE, .u.m = {general_plugin_start, + general_plugin_stop}}, + [PLUGIN_TYPE_IFACE] = {"interface", TRUE, TRUE, .u.s = {iface_plugin_probe, + iface_plugin_get_current, iface_plugin_set_current}}}; + +static gboolean find_enabled_cb (PluginHandle * p, PluginHandle * * pp) +{ + * pp = p; + return FALSE; +} + +static PluginHandle * find_enabled (gint type) +{ + PluginHandle * p = NULL; + plugin_for_enabled (type, (PluginForEachFunc) find_enabled_cb, & p); + return p; +} + +static void start_single (gint type) +{ + PluginHandle * p; + + if ((p = find_enabled (type)) != NULL) + { + AUDDBG ("Starting selected %s plugin %s.\n", table[type].name, + plugin_get_name (p)); + + if (table[type].u.s.set_current (p)) + return; + + AUDDBG ("%s failed to start.\n", plugin_get_name (p)); + plugin_set_enabled (p, FALSE); + } + + AUDDBG ("Probing for %s plugin.\n", table[type].name); + + if ((p = table[type].u.s.probe ()) == NULL) + { + fprintf (stderr, "FATAL: No %s plugin found.\n", table[type].name); + exit (EXIT_FAILURE); + } + + AUDDBG ("Starting %s.\n", plugin_get_name (p)); + plugin_set_enabled (p, TRUE); + + if (! table[type].u.s.set_current (p)) + { + fprintf (stderr, "FATAL: %s failed to start.\n", plugin_get_name (p)); + plugin_set_enabled (p, FALSE); + exit (EXIT_FAILURE); + } +} + +static gboolean start_multi_cb (PluginHandle * p, void * type) +{ + AUDDBG ("Starting %s.\n", plugin_get_name (p)); + + if (! table[GPOINTER_TO_INT (type)].u.m.start (p)) + { + AUDDBG ("%s failed to start; disabling.\n", plugin_get_name (p)); + plugin_set_enabled (p, FALSE); + } + + return TRUE; +} + +static void start_plugins (gint type) +{ + if (! table[type].is_managed) + return; + + if (table[type].is_single) + start_single (type); + else + plugin_for_enabled (type, (PluginForEachFunc) start_multi_cb, + GINT_TO_POINTER (type)); +} + +static VFSConstructor * lookup_transport (const gchar * scheme) +{ + PluginHandle * plugin = transport_plugin_for_scheme (scheme); + if (! plugin) + return NULL; + + TransportPlugin * tp = plugin_get_header (plugin); + return tp->vtable; +} + +void start_plugins_one (void) +{ + plugin_system_init (); + vfs_set_lookup_func (lookup_transport); + + for (gint i = 0; i < PLUGIN_TYPE_GENERAL; i ++) + start_plugins (i); +} + +void start_plugins_two (void) +{ + for (gint i = PLUGIN_TYPE_GENERAL; i < PLUGIN_TYPES; i ++) + start_plugins (i); +} + +static gboolean stop_multi_cb (PluginHandle * p, void * type) +{ + AUDDBG ("Shutting down %s.\n", plugin_get_name (p)); + table[GPOINTER_TO_INT (type)].u.m.stop (p); + return TRUE; +} + +static void stop_plugins (gint type) +{ + if (! table[type].is_managed) + return; + + if (table[type].is_single) + { + AUDDBG ("Shutting down %s.\n", plugin_get_name + (table[type].u.s.get_current ())); + table[type].u.s.set_current (NULL); + } + else + plugin_for_enabled (type, (PluginForEachFunc) stop_multi_cb, + GINT_TO_POINTER (type)); +} + +void stop_plugins_two (void) +{ + for (gint i = PLUGIN_TYPES - 1; i >= PLUGIN_TYPE_GENERAL; i --) + stop_plugins (i); +} + +void stop_plugins_one (void) +{ + for (gint i = PLUGIN_TYPE_GENERAL - 1; i >= 0; i --) + stop_plugins (i); + + vfs_set_lookup_func (NULL); + plugin_system_cleanup (); +} + +PluginHandle * plugin_get_current (gint type) +{ + g_return_val_if_fail (table[type].is_managed && table[type].is_single, NULL); + return table[type].u.s.get_current (); +} + +static gboolean enable_single (gint type, PluginHandle * p) +{ + PluginHandle * old = table[type].u.s.get_current (); + + AUDDBG ("Switching from %s to %s.\n", plugin_get_name (old), + plugin_get_name (p)); + plugin_set_enabled (old, FALSE); + plugin_set_enabled (p, TRUE); + + if (table[type].u.s.set_current (p)) + return TRUE; + + fprintf (stderr, "%s failed to start; falling back to %s.\n", + plugin_get_name (p), plugin_get_name (old)); + plugin_set_enabled (p, FALSE); + plugin_set_enabled (old, TRUE); + + if (table[type].u.s.set_current (old)) + return FALSE; + + fprintf (stderr, "FATAL: %s failed to start.\n", plugin_get_name (old)); + plugin_set_enabled (old, FALSE); + exit (EXIT_FAILURE); +} + +static gboolean enable_multi (gint type, PluginHandle * p, gboolean enable) +{ + AUDDBG ("%sabling %s.\n", enable ? "En" : "Dis", plugin_get_name (p)); + plugin_set_enabled (p, enable); + + if (enable) + { + if (! table[type].u.m.start (p)) + { + fprintf (stderr, "%s failed to start.\n", plugin_get_name (p)); + plugin_set_enabled (p, FALSE); + return FALSE; + } + } + else + table[type].u.m.stop (p); + + return TRUE; +} + +gboolean plugin_enable (PluginHandle * plugin, gboolean enable) +{ + if (! enable == ! plugin_get_enabled (plugin)) + { + AUDDBG ("%s is already %sabled.\n", plugin_get_name (plugin), enable ? + "en" : "dis"); + return TRUE; + } + + gint type = plugin_get_type (plugin); + g_return_val_if_fail (table[type].is_managed, FALSE); + + if (table[type].is_single) + { + g_return_val_if_fail (enable, FALSE); + return enable_single (type, plugin); + } + + return enable_multi (type, plugin, enable); +} + +/* This doesn't really belong here, but it's a bit of an oddball. */ +PluginHandle * plugin_by_widget (/* GtkWidget * */ void * widget) +{ + PluginHandle * p; + if ((p = vis_plugin_by_widget (widget))) + return p; + if ((p = general_plugin_by_widget (widget))) + return p; + return NULL; +} diff --git a/src/audacious/plugin-registry.c b/src/audacious/plugin-registry.c index 15ec913..59b5464 100644 --- a/src/audacious/plugin-registry.c +++ b/src/audacious/plugin-registry.c @@ -28,15 +28,13 @@ #include "debug.h" #include "interface.h" -#include "main.h" #include "misc.h" #include "plugin.h" -#include "pluginenum.h" #include "plugins.h" #include "util.h" #define FILENAME "plugin-registry" -#define FORMAT 2 +#define FORMAT 5 typedef struct { gchar * path; @@ -47,31 +45,50 @@ typedef struct { } ModuleData; typedef struct { + GList * schemes; +} TransportPluginData; + +typedef struct { + GList * exts; +} PlaylistPluginData; + +typedef struct { GList * keys[INPUT_KEYS]; + gboolean has_images, has_subtunes, can_write_tuple, has_infowin; } InputPluginData; struct PluginHandle { ModuleData * module; gint type, number; gboolean confirmed; - void * header; + const void * header; gchar * name; gint priority; gboolean has_about, has_configure, enabled; + GList * watches; union { + TransportPluginData t; + PlaylistPluginData p; InputPluginData i; } u; }; +typedef struct { + PluginForEachFunc func; + void * data; +} PluginWatch; + static const gchar * plugin_type_names[] = { - [PLUGIN_TYPE_BASIC] = NULL, + [PLUGIN_TYPE_LOWLEVEL] = NULL, + [PLUGIN_TYPE_TRANSPORT] = "transport", + [PLUGIN_TYPE_PLAYLIST] = "playlist", [PLUGIN_TYPE_INPUT] = "input", - [PLUGIN_TYPE_OUTPUT] = "output", [PLUGIN_TYPE_EFFECT] = "effect", + [PLUGIN_TYPE_OUTPUT] = "output", [PLUGIN_TYPE_VIS] = "vis", - [PLUGIN_TYPE_IFACE] = "iface", - [PLUGIN_TYPE_GENERAL] = "general"}; + [PLUGIN_TYPE_GENERAL] = "general", + [PLUGIN_TYPE_IFACE] = "iface"}; static const gchar * input_key_names[] = { [INPUT_KEY_SCHEME] = "scheme", [INPUT_KEY_EXTENSION] = "ext", @@ -97,7 +114,7 @@ static ModuleData * module_new (gchar * path, gboolean confirmed, gint } static PluginHandle * plugin_new (ModuleData * module, gint type, gint number, - gboolean confirmed, void * header) + gboolean confirmed, const void * header) { PluginHandle * plugin = g_malloc (sizeof (PluginHandle)); @@ -111,14 +128,27 @@ static PluginHandle * plugin_new (ModuleData * module, gint type, gint number, plugin->has_about = FALSE; plugin->has_configure = FALSE; plugin->enabled = FALSE; + plugin->watches = NULL; - if (type == PLUGIN_TYPE_INPUT) + if (type == PLUGIN_TYPE_TRANSPORT) { plugin->enabled = TRUE; - memset (plugin->u.i.keys, 0, sizeof plugin->u.i.keys); + plugin->u.t.schemes = NULL; } - else if (type == PLUGIN_TYPE_IFACE) + else if (type == PLUGIN_TYPE_PLAYLIST) + { plugin->enabled = TRUE; + plugin->u.p.exts = NULL; + } + else if (type == PLUGIN_TYPE_INPUT) + { + plugin->enabled = TRUE; + memset (plugin->u.i.keys, 0, sizeof plugin->u.i.keys); + plugin->u.i.has_images = FALSE; + plugin->u.i.has_subtunes = FALSE; + plugin->u.i.can_write_tuple = FALSE; + plugin->u.i.has_infowin = FALSE; + } plugin_list = g_list_prepend (plugin_list, plugin); module->plugin_list = g_list_prepend (module->plugin_list, plugin); @@ -131,7 +161,20 @@ static void plugin_free (PluginHandle * plugin, ModuleData * module) plugin_list = g_list_remove (plugin_list, plugin); module->plugin_list = g_list_remove (module->plugin_list, plugin); - if (plugin->type == PLUGIN_TYPE_INPUT) + g_list_foreach (plugin->watches, (GFunc) g_free, NULL); + g_list_free (plugin->watches); + + if (plugin->type == PLUGIN_TYPE_TRANSPORT) + { + g_list_foreach (plugin->u.t.schemes, (GFunc) g_free, NULL); + g_list_free (plugin->u.t.schemes); + } + else if (plugin->type == PLUGIN_TYPE_PLAYLIST) + { + g_list_foreach (plugin->u.p.exts, (GFunc) g_free, NULL); + g_list_free (plugin->u.p.exts); + } + else if (plugin->type == PLUGIN_TYPE_INPUT) { for (gint key = 0; key < INPUT_KEYS; key ++) { @@ -157,10 +200,22 @@ static void module_free (ModuleData * module) static FILE * open_registry_file (const gchar * mode) { gchar path[PATH_MAX]; - snprintf (path, sizeof path, "%s/" FILENAME, aud_paths[BMP_PATH_USER_DIR]); + snprintf (path, sizeof path, "%s/" FILENAME, get_path (AUD_PATH_USER_DIR)); return fopen (path, mode); } +static void transport_plugin_save (PluginHandle * plugin, FILE * handle) +{ + for (GList * node = plugin->u.t.schemes; node; node = node->next) + fprintf (handle, "scheme %s\n", (const gchar *) node->data); +} + +static void playlist_plugin_save (PluginHandle * plugin, FILE * handle) +{ + for (GList * node = plugin->u.p.exts; node; node = node->next) + fprintf (handle, "ext %s\n", (const gchar *) node->data); +} + static void input_plugin_save (PluginHandle * plugin, FILE * handle) { for (gint key = 0; key < INPUT_KEYS; key ++) @@ -170,6 +225,11 @@ static void input_plugin_save (PluginHandle * plugin, FILE * handle) fprintf (handle, "%s %s\n", input_key_names[key], (const gchar *) node->data); } + + fprintf (handle, "images %d\n", plugin->u.i.has_images); + fprintf (handle, "subtunes %d\n", plugin->u.i.has_subtunes); + fprintf (handle, "writes %d\n", plugin->u.i.can_write_tuple); + fprintf (handle, "infowin %d\n", plugin->u.i.has_infowin); } static void plugin_save (PluginHandle * plugin, FILE * handle) @@ -181,12 +241,14 @@ static void plugin_save (PluginHandle * plugin, FILE * handle) fprintf (handle, "config %d\n", plugin->has_configure); fprintf (handle, "enabled %d\n", plugin->enabled); - if (plugin->type == PLUGIN_TYPE_INPUT) + if (plugin->type == PLUGIN_TYPE_TRANSPORT) + transport_plugin_save (plugin, handle); + else if (plugin->type == PLUGIN_TYPE_PLAYLIST) + playlist_plugin_save (plugin, handle); + else if (plugin->type == PLUGIN_TYPE_INPUT) input_plugin_save (plugin, handle); } -/* If the module contains any plugins that we do not handle, we do not save it, - * thereby forcing it to be loaded on next startup. */ static gint plugin_not_handled_cb (PluginHandle * plugin) { return (plugin_type_names[plugin->type] == NULL) ? 0 : -1; @@ -194,8 +256,12 @@ static gint plugin_not_handled_cb (PluginHandle * plugin) static void module_save (ModuleData * module, FILE * handle) { - if (g_list_find_custom (module->plugin_list, NULL, (GCompareFunc) - plugin_not_handled_cb) != NULL) + /* If the module contains any plugins that we do not handle, we do not save + * it, thereby forcing it to be loaded on next startup. If the module + * appears to contain no plugins at all, we can assume that it failed to + * load, in which case we also want to try to load it again. */ + if (! module->plugin_list || g_list_find_custom (module->plugin_list, NULL, + (GCompareFunc) plugin_not_handled_cb) != NULL) return; fprintf (handle, "module %s\n", module->path); @@ -252,6 +318,26 @@ static gchar * parse_string (const gchar * key) (parse_value) : NULL; } +static void transport_plugin_parse (PluginHandle * plugin, FILE * handle) +{ + gchar * value; + while ((value = parse_string ("scheme"))) + { + plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, value); + parse_next (handle); + } +} + +static void playlist_plugin_parse (PluginHandle * plugin, FILE * handle) +{ + gchar * value; + while ((value = parse_string ("ext"))) + { + plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, value); + parse_next (handle); + } +} + static void input_plugin_parse (PluginHandle * plugin, FILE * handle) { for (gint key = 0; key < INPUT_KEYS; key ++) @@ -264,6 +350,15 @@ static void input_plugin_parse (PluginHandle * plugin, FILE * handle) parse_next (handle); } } + + if (parse_integer ("images", & plugin->u.i.has_images)) + parse_next (handle); + if (parse_integer ("subtunes", & plugin->u.i.has_subtunes)) + parse_next (handle); + if (parse_integer ("writes", & plugin->u.i.can_write_tuple)) + parse_next (handle); + if (parse_integer ("infowin", & plugin->u.i.has_infowin)) + parse_next (handle); } static gboolean plugin_parse (ModuleData * module, FILE * handle) @@ -293,7 +388,11 @@ FOUND:; if (parse_integer ("enabled", & plugin->enabled)) parse_next (handle); - if (type == PLUGIN_TYPE_INPUT) + if (type == PLUGIN_TYPE_TRANSPORT) + transport_plugin_parse (plugin, handle); + else if (type == PLUGIN_TYPE_PLAYLIST) + playlist_plugin_parse (plugin, handle); + else if (type == PLUGIN_TYPE_INPUT) input_plugin_parse (plugin, handle); return TRUE; @@ -333,14 +432,14 @@ void plugin_registry_load (void) gint format; if (! parse_integer ("format", & format) || format != FORMAT) - goto ERROR; + goto ERR; parse_next (handle); while (module_parse (handle)) ; -ERROR: +ERR: fclose (handle); UNLOCK: registry_locked = FALSE; @@ -450,7 +549,8 @@ static gint plugin_lookup_cb (PluginHandle * plugin, PluginLookupState * state) : -1; } -static PluginHandle * plugin_lookup (ModuleData * module, gint type, gint number) +static PluginHandle * plugin_lookup_real (ModuleData * module, gint type, gint + number) { PluginLookupState state = {type, number}; GList * node = g_list_find_custom (module->plugin_list, & state, @@ -458,12 +558,13 @@ static PluginHandle * plugin_lookup (ModuleData * module, gint type, gint number return (node != NULL) ? node->data : NULL; } -void plugin_register (const gchar * path, gint type, gint number, void * header) +void plugin_register (gint type, const gchar * path, gint number, const void * + header) { ModuleData * module = module_lookup (path); g_return_if_fail (module != NULL); - PluginHandle * plugin = plugin_lookup (module, type, number); + PluginHandle * plugin = plugin_lookup_real (module, type, number); if (plugin == NULL) { AUDDBG ("New plugin: %s %d:%d\n", path, type, number); @@ -475,14 +576,33 @@ void plugin_register (const gchar * path, gint type, gint number, void * header) plugin->confirmed = TRUE; plugin->header = header; - if (type == PLUGIN_TYPE_INPUT) + if (type != PLUGIN_TYPE_LOWLEVEL && type != PLUGIN_TYPE_IFACE) { - InputPlugin * ip = header; + Plugin * gp = header; g_free (plugin->name); - plugin->name = g_strdup (ip->description); + plugin->name = g_strdup (gp->description); + plugin->has_about = (gp->about != NULL); + plugin->has_configure = (gp->configure != NULL || gp->settings != NULL); + } + + if (type == PLUGIN_TYPE_TRANSPORT) + { + TransportPlugin * tp = header; + for (gint i = 0; tp->schemes[i]; i ++) + plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, g_strdup + (tp->schemes[i])); + } + else if (type == PLUGIN_TYPE_PLAYLIST) + { + PlaylistPlugin * pp = header; + for (gint i = 0; pp->extensions[i]; i ++) + plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, g_strdup + (pp->extensions[i])); + } + else if (type == PLUGIN_TYPE_INPUT) + { + InputPlugin * ip = header; plugin->priority = ip->priority; - plugin->has_about = (ip->about != NULL); - plugin->has_configure = (ip->configure != NULL); for (gint key = 0; key < INPUT_KEYS; key ++) { @@ -498,67 +618,55 @@ void plugin_register (const gchar * path, gint type, gint number, void * header) (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup (ip->vfs_extensions[i])); } + + plugin->u.i.has_images = (ip->get_song_image != NULL); + plugin->u.i.has_subtunes = ip->have_subtune; + plugin->u.i.can_write_tuple = (ip->update_song_tuple != NULL); + plugin->u.i.has_infowin = (ip->file_info_box != NULL); } else if (type == PLUGIN_TYPE_OUTPUT) { OutputPlugin * op = header; - g_free (plugin->name); - plugin->name = g_strdup (op->description); plugin->priority = 10 - op->probe_priority; - plugin->has_about = (op->about != NULL); - plugin->has_configure = (op->configure != NULL); } else if (type == PLUGIN_TYPE_EFFECT) { EffectPlugin * ep = header; - g_free (plugin->name); - plugin->name = g_strdup (ep->description); plugin->priority = ep->order; - plugin->has_about = (ep->about != NULL); - plugin->has_configure = (ep->configure != NULL); - } - else if (type == PLUGIN_TYPE_VIS) - { - VisPlugin * vp = header; - g_free (plugin->name); - plugin->name = g_strdup (vp->description); - plugin->has_about = (vp->about != NULL); - plugin->has_configure = (vp->configure != NULL); } else if (type == PLUGIN_TYPE_IFACE) { - Interface * i = header; + Iface * i = (void *) header; g_free (plugin->name); plugin->name = g_strdup (i->desc); } - else if (type == PLUGIN_TYPE_GENERAL) - { - GeneralPlugin * gp = header; - g_free (plugin->name); - plugin->name = g_strdup (gp->description); - plugin->has_about = (gp->about != NULL); - plugin->has_configure = (gp->configure != NULL); - } } -void plugin_get_path (PluginHandle * plugin, const gchar * * path, gint * type, - gint * number) +gint plugin_get_type (PluginHandle * plugin) +{ + return plugin->type; +} + +const gchar * plugin_get_filename (PluginHandle * plugin) +{ + return plugin->module->path; +} + +gint plugin_get_number (PluginHandle * plugin) { - * path = plugin->module->path; - * type = plugin->type; - * number = plugin->number; + return plugin->number; } -PluginHandle * plugin_by_path (const gchar * path, gint type, gint number) +PluginHandle * plugin_lookup (gint type, const gchar * path, gint number) { ModuleData * module = module_lookup (path); if (module == NULL) return NULL; - return plugin_lookup (module, type, number); + return plugin_lookup_real (module, type, number); } -void * plugin_get_header (PluginHandle * plugin) +const void * plugin_get_header (PluginHandle * plugin) { if (! plugin->module->loaded) { @@ -569,12 +677,12 @@ void * plugin_get_header (PluginHandle * plugin) return plugin->header; } -static gint plugin_by_header_cb (PluginHandle * plugin, void * header) +static gint plugin_by_header_cb (PluginHandle * plugin, const void * header) { return (plugin->header == header) ? 0 : -1; } -PluginHandle * plugin_by_header (void * header) +PluginHandle * plugin_by_header (const void * header) { GList * node = g_list_find_custom (plugin_list, header, (GCompareFunc) plugin_by_header_cb); @@ -612,9 +720,27 @@ gboolean plugin_get_enabled (PluginHandle * plugin) return plugin->enabled; } +static void plugin_call_watches (PluginHandle * plugin) +{ + for (GList * node = plugin->watches; node != NULL; ) + { + GList * next = node->next; + PluginWatch * watch = node->data; + + if (! watch->func (plugin, watch->data)) + { + g_free (watch); + plugin->watches = g_list_delete_link (plugin->watches, node); + } + + node = next; + } +} + void plugin_set_enabled (PluginHandle * plugin, gboolean enabled) { plugin->enabled = enabled; + plugin_call_watches (plugin); } typedef struct { @@ -636,6 +762,82 @@ void plugin_for_enabled (gint type, PluginForEachFunc func, void * data) plugin_for_each (type, (PluginForEachFunc) plugin_for_enabled_cb, & state); } +void plugin_add_watch (PluginHandle * plugin, PluginForEachFunc func, void * + data) +{ + PluginWatch * watch = g_malloc (sizeof (PluginWatch)); + watch->func = func; + watch->data = data; + plugin->watches = g_list_prepend (plugin->watches, watch); +} + +void plugin_remove_watch (PluginHandle * plugin, PluginForEachFunc func, void * + data) +{ + for (GList * node = plugin->watches; node != NULL; ) + { + GList * next = node->next; + PluginWatch * watch = node->data; + + if (watch->func == func && watch->data == data) + { + g_free (watch); + plugin->watches = g_list_delete_link (plugin->watches, node); + } + + node = next; + } +} + + +typedef struct { + const gchar * scheme; + PluginHandle * plugin; +} TransportPluginForSchemeState; + +static gboolean transport_plugin_for_scheme_cb (PluginHandle * plugin, + TransportPluginForSchemeState * state) +{ + if (! g_list_find_custom (plugin->u.t.schemes, state->scheme, (GCompareFunc) + strcasecmp)) + return TRUE; + + state->plugin = plugin; + return FALSE; +} + +PluginHandle * transport_plugin_for_scheme (const gchar * scheme) +{ + TransportPluginForSchemeState state = {scheme, NULL}; + plugin_for_enabled (PLUGIN_TYPE_TRANSPORT, (PluginForEachFunc) + transport_plugin_for_scheme_cb, & state); + return state.plugin; +} + +typedef struct { + const gchar * ext; + PluginHandle * plugin; +} PlaylistPluginForExtState; + +static gboolean playlist_plugin_for_ext_cb (PluginHandle * plugin, + PlaylistPluginForExtState * state) +{ + if (! g_list_find_custom (plugin->u.p.exts, state->ext, (GCompareFunc) + strcasecmp)) + return TRUE; + + state->plugin = plugin; + return FALSE; +} + +PluginHandle * playlist_plugin_for_extension (const gchar * extension) +{ + PlaylistPluginForExtState state = {extension, NULL}; + plugin_for_enabled (PLUGIN_TYPE_PLAYLIST, (PluginForEachFunc) + playlist_plugin_for_ext_cb, & state); + return state.plugin; +} + typedef struct { gint key; const gchar * value; @@ -679,3 +881,27 @@ void mime_set_plugin (const gchar * mime, InputPlugin * header) { input_plugin_add_key (header, INPUT_KEY_MIME, mime); } + +gboolean input_plugin_has_images (PluginHandle * plugin) +{ + g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); + return plugin->u.i.has_images; +} + +gboolean input_plugin_has_subtunes (PluginHandle * plugin) +{ + g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); + return plugin->u.i.has_subtunes; +} + +gboolean input_plugin_can_write_tuple (PluginHandle * plugin) +{ + g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); + return plugin->u.i.can_write_tuple; +} + +gboolean input_plugin_has_infowin (PluginHandle * plugin) +{ + g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); + return plugin->u.i.has_infowin; +} diff --git a/src/audacious/plugin-view.c b/src/audacious/plugin-view.c new file mode 100644 index 0000000..818adc1 --- /dev/null +++ b/src/audacious/plugin-view.c @@ -0,0 +1,269 @@ +/* + * plugin-view.c + * Copyright 2010 John Lindgren + * + * This file is part of Audacious. + * + * Audacious 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 or version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <gtk/gtk.h> + +#include "plugin.h" +#include "plugins.h" +#include "ui_preferences.h" + +enum { + PVIEW_COL_NODE, + PVIEW_COL_ENABLED, + PVIEW_COL_NAME, + PVIEW_COL_PATH, + PVIEW_COLS +}; + +typedef struct { + PluginHandle * p; + GtkTreeModel * model; + GtkTreePath * path; +} Node; + +static PluginHandle * get_selected_plugin (GtkTreeView * tree) +{ + Node * n = NULL; + + GtkTreeSelection * sel = gtk_tree_view_get_selection (tree); + GtkTreeModel * model; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (sel, & model, & iter)) + gtk_tree_model_get (model, & iter, PVIEW_COL_NODE, & n, -1); + + return n == NULL ? NULL : n->p; +} + +static Plugin * get_selected_header (GtkTreeView * tree) +{ + PluginHandle * p = get_selected_plugin (tree); + g_return_val_if_fail (p != NULL, NULL); + g_return_val_if_fail (plugin_get_enabled (p), NULL); + return plugin_get_header (p); +} + +static void do_enable (GtkCellRendererToggle * cell, const gchar * path_str, + GtkTreeModel * model) +{ + GtkTreePath * path = gtk_tree_path_new_from_string (path_str); + GtkTreeIter iter; + gtk_tree_model_get_iter (model, & iter, path); + gtk_tree_path_free (path); + + Node * n = NULL; + gboolean enabled; + gtk_tree_model_get (model, & iter, PVIEW_COL_NODE, & n, + PVIEW_COL_ENABLED, & enabled, -1); + g_return_if_fail (n != NULL); + + plugin_enable (n->p, ! enabled); +} + +static gboolean list_watcher (PluginHandle * p, Node * n) +{ + GtkTreeIter iter; + gtk_tree_model_get_iter (n->model, & iter, n->path); + gtk_list_store_set ((GtkListStore *) n->model, & iter, PVIEW_COL_ENABLED, + plugin_get_enabled (n->p), -1); + return TRUE; +} + +static gboolean fill_cb (PluginHandle * p, GtkTreeModel * model) +{ + Node * n = g_slice_new (Node); + + GtkTreeIter iter; + gtk_list_store_append ((GtkListStore *) model, & iter); + gtk_list_store_set ((GtkListStore *) model, & iter, PVIEW_COL_NODE, n, + PVIEW_COL_ENABLED, plugin_get_enabled (p), PVIEW_COL_NAME, plugin_get_name + (p), PVIEW_COL_PATH, plugin_get_filename (p), -1); + + n->p = p; + n->model = model; + n->path = gtk_tree_model_get_path (model, & iter); + + plugin_add_watch (p, (PluginForEachFunc) list_watcher, n); + + return TRUE; +} + +static void list_fill (GtkTreeView * tree, void * type) +{ + GtkTreeModel * model = (GtkTreeModel *) gtk_list_store_new (PVIEW_COLS, + G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING); + gtk_tree_view_set_model (tree, model); + + GtkTreeViewColumn * col = gtk_tree_view_column_new (); + gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable (col, FALSE); + gtk_tree_view_append_column (tree, col); + + GtkCellRenderer * rend = gtk_cell_renderer_toggle_new (); + g_signal_connect (rend, "toggled", (GCallback) do_enable, model); + gtk_tree_view_column_pack_start (col, rend, FALSE); + gtk_tree_view_column_set_attributes (col, rend, "active", PVIEW_COL_ENABLED, + NULL); + + for (gint i = PVIEW_COL_NAME; i <= PVIEW_COL_PATH; i ++) + { + col = gtk_tree_view_column_new (); + gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable (col, FALSE); + gtk_tree_view_append_column (tree, col); + + rend = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, rend, FALSE); + gtk_tree_view_column_set_attributes (col, rend, "text", i, NULL); + } + + plugin_for_each (GPOINTER_TO_INT (type), (PluginForEachFunc) fill_cb, model); +} + +static void list_destroy (GtkTreeView * tree) +{ + GtkTreeModel * model = gtk_tree_view_get_model (tree); + if (model == NULL) + return; + + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first (model, & iter)) + { + do + { + Node * n = NULL; + gtk_tree_model_get (model, & iter, PVIEW_COL_NODE, & n, -1); + g_return_if_fail (n != NULL); + + plugin_remove_watch (n->p, (PluginForEachFunc) list_watcher, n); + gtk_tree_path_free (n->path); + g_slice_free (Node, n); + } + while (gtk_tree_model_iter_next (model, & iter)); + } + + g_object_unref ((GObject *) model); +} + +static gboolean config_watcher (PluginHandle * p, GtkWidget * config) +{ + gtk_widget_set_sensitive (config, plugin_has_configure (p) && + plugin_get_enabled (p)); + return TRUE; +} + +static gboolean about_watcher (PluginHandle * p, GtkWidget * about) +{ + gtk_widget_set_sensitive (about, plugin_has_about (p) && plugin_get_enabled + (p)); + return TRUE; +} + +static void button_update (GtkTreeView * tree, GtkWidget * b) +{ + PluginForEachFunc watcher = g_object_get_data ((GObject *) b, "watcher"); + g_return_if_fail (watcher != NULL); + + PluginHandle * p = g_object_steal_data ((GObject *) b, "plugin"); + if (p != NULL) + plugin_remove_watch (p, watcher, b); + + p = get_selected_plugin (tree); + if (p != NULL) + { + g_object_set_data ((GObject *) b, "plugin", p); + watcher (p, b); + plugin_add_watch (p, watcher, b); + } + else + gtk_widget_set_sensitive (b, FALSE); +} + +static void do_config (GtkTreeView * tree) +{ + Plugin * header = get_selected_header (tree); + g_return_if_fail (header != NULL); + + if (header->configure != NULL) + header->configure (); + else if (header->settings != NULL) + plugin_preferences_show (header->settings); +} + +static void do_about (GtkTreeView * tree) +{ + Plugin * header = get_selected_header (tree); + g_return_if_fail (header != NULL); + + if (header->about != NULL) + header->about (); +} + +static void button_destroy (GtkWidget * b) +{ + PluginForEachFunc watcher = g_object_get_data ((GObject *) b, "watcher"); + g_return_if_fail (watcher != NULL); + + PluginHandle * p = g_object_steal_data ((GObject *) b, "plugin"); + if (p != NULL) + plugin_remove_watch (p, watcher, b); +} + +GtkWidget * plugin_view_new (gint type) +{ + GtkWidget * vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width ((GtkContainer *) vbox, 6); + + GtkWidget * scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_box_pack_start ((GtkBox *) vbox, scrolled, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy ((GtkScrolledWindow *) scrolled, + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) scrolled, + GTK_SHADOW_IN); + + GtkWidget * tree = gtk_tree_view_new (); + gtk_container_add ((GtkContainer *) scrolled, tree); + gtk_tree_view_set_headers_visible ((GtkTreeView *) tree, FALSE); + g_signal_connect (tree, "realize", (GCallback) list_fill, GINT_TO_POINTER + (type)); + g_signal_connect (tree, "destroy", (GCallback) list_destroy, NULL); + + GtkWidget * hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start ((GtkBox *) vbox, hbox, FALSE, FALSE, 0); + + GtkWidget * config = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES); + gtk_box_pack_start ((GtkBox *) hbox, config, FALSE, FALSE, 0); + gtk_widget_set_sensitive (config, FALSE); + g_object_set_data ((GObject *) config, "watcher", config_watcher); + g_signal_connect (tree, "cursor-changed", (GCallback) button_update, config); + g_signal_connect_swapped (config, "clicked", (GCallback) + do_config, tree); + g_signal_connect (config, "destroy", (GCallback) button_destroy, NULL); + + GtkWidget * about = gtk_button_new_from_stock (GTK_STOCK_ABOUT); + gtk_box_pack_start ((GtkBox *) hbox, about, FALSE, FALSE, 0); + gtk_widget_set_sensitive (about, FALSE); + g_object_set_data ((GObject *) about, "watcher", about_watcher); + g_signal_connect (tree, "cursor-changed", (GCallback) button_update, about); + g_signal_connect_swapped (about, "clicked", (GCallback) do_about, tree); + g_signal_connect (about, "destroy", (GCallback) button_destroy, NULL); + + return vbox; +} diff --git a/src/audacious/plugin.h b/src/audacious/plugin.h index c5321f5..d8958c5 100644 --- a/src/audacious/plugin.h +++ b/src/audacious/plugin.h @@ -1,41 +1,24 @@ -/* Audacious - * Copyright (C) 2005-2010 Audacious team. +/* + * plugin.h + * Copyright 2005-2010 Audacious Development Team * - * BMP - Cross-platform multimedia player - * Copyright (C) 2003-2004 BMP development team. + * This file is part of Audacious. * - * Based on XMMS: - * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * Audacious 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 or version 3 of the License. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Audacious 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. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/** - * @file plugin.h - * @brief Main Audacious plugin API header file. + * You should have received a copy of the GNU General Public License along with + * Audacious. If not, see <http://www.gnu.org/licenses/>. * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. */ + #ifndef AUDACIOUS_PLUGIN_H #define AUDACIOUS_PLUGIN_H @@ -48,132 +31,149 @@ #include <libaudcore/tuple.h> #include <libaudcore/vfs.h> -//@{ -/** Preprocessor defines for different API features */ -#define __AUDACIOUS_PLUGIN_API__ 17 /**< Current generic plugin API/ABI version, exact match is required for plugin to be loaded. */ -//@} - -typedef enum { - PLUGIN_MESSAGE_ERROR = 0, - PLUGIN_MESSAGE_OK = 1, - PLUGIN_MESSAGE_DEFERRED = 2 -} PluginMessageResponse; +/* "Magic" bytes identifying an Audacious plugin header. */ +#define _AUD_PLUGIN_MAGIC 0x8EAC8DE2 -typedef struct _InputPlayback InputPlayback; +/* API version. Plugins are marked with this number at compile time. + * + * _AUD_PLUGIN_VERSION is the current version; _AUD_PLUGIN_VERSION_MIN is + * the oldest one we are backward compatible with. Plugins marked older than + * _AUD_PLUGIN_VERSION_MIN or newer than _AUD_PLUGIN_VERSION are not loaded. + * + * Before releases that add new pointers to the end of the API tables, increment + * _AUD_PLUGIN_VERSION but leave _AUD_PLUGIN_VERSION_MIN the same. + * + * Before releases that break backward compatibility (e.g. remove pointers from + * the API tables), increment _AUD_PLUGIN_VERSION *and* set + * _AUD_PLUGIN_VERSION_MIN to the same value. */ -/** ReplayGain information structure */ -typedef struct { - gfloat track_gain; /**< Track gain in decibels (dB) */ - gfloat track_peak; - gfloat album_gain; - gfloat album_peak; -} ReplayGainInfo; +#define _AUD_PLUGIN_VERSION_MIN 18 /* 2.5-alpha1 */ +#define _AUD_PLUGIN_VERSION 20 /* 2.5-beta2 */ /** * The plugin module header. Each module can contain several plugins, * of any supported type. */ -typedef struct { - gint magic; /**< Audacious plugin module magic ID */ - gint api_version; /**< API version plugin has been compiled for, - this is checked against __AUDACIOUS_PLUGIN_API__ */ - gchar *name; /**< Module name */ +typedef const struct { + gint magic; /* checked against _AUD_PLUGIN_MAGIC */ + gint version; /* checked against _AUD_PLUGIN_VERSION */ + const gchar * name; void (* init) (void); void (* fini) (void); - Plugin *priv_assoc; - InputPlugin **ip_list; /**< List of InputPlugin(s) in this module */ - OutputPlugin **op_list; - EffectPlugin **ep_list; - GeneralPlugin **gp_list; - VisPlugin **vp_list; - Interface *interface; -} PluginHeader; - -#define PLUGIN_MAGIC 0x8EAC8DE2 - -#define DECLARE_PLUGIN(name, init, fini, ...) \ - G_BEGIN_DECLS \ - static PluginHeader _pluginInfo = { PLUGIN_MAGIC, __AUDACIOUS_PLUGIN_API__, \ - (gchar *)#name, init, fini, NULL, __VA_ARGS__ }; \ - AudAPITable * _aud_api_table = NULL; \ - G_MODULE_EXPORT PluginHeader * get_plugin_info (AudAPITable * table) { \ - _aud_api_table = table; \ - return &_pluginInfo; \ - } \ - G_END_DECLS - -#define SIMPLE_INPUT_PLUGIN(name, ip_list) \ - DECLARE_PLUGIN(name, NULL, NULL, ip_list) -#define SIMPLE_OUTPUT_PLUGIN(name, op_list) \ - DECLARE_PLUGIN(name, NULL, NULL, NULL, op_list) + /* These are arrays of pointers, ending with NULL: */ + InputPlugin * const * ip_list; + OutputPlugin * const * op_list; + EffectPlugin * const * ep_list; + GeneralPlugin * const * gp_list; + VisPlugin * const * vp_list; + TransportPlugin * const * tp_list; + PlaylistPlugin * const * pp_list; -#define SIMPLE_EFFECT_PLUGIN(name, ep_list) \ - DECLARE_PLUGIN(name, NULL, NULL, NULL, NULL, ep_list) - -#define SIMPLE_GENERAL_PLUGIN(name, gp_list) \ - DECLARE_PLUGIN(name, NULL, NULL, NULL, NULL, NULL, gp_list) - -#define SIMPLE_VISUAL_PLUGIN(name, vp_list) \ - DECLARE_PLUGIN(name, NULL, NULL, NULL, NULL, NULL, NULL, vp_list) - -#define SIMPLE_INTERFACE_PLUGIN(name, interface) \ - DECLARE_PLUGIN(name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, interface) + Iface * iface; +} PluginHeader; +#define DECLARE_PLUGIN(name, ...) \ + AudAPITable * _aud_api_table = NULL; \ + G_MODULE_EXPORT PluginHeader * get_plugin_info (AudAPITable * table) { \ + static PluginHeader h = {_AUD_PLUGIN_MAGIC, _AUD_PLUGIN_VERSION, #name, \ + __VA_ARGS__}; \ + _aud_api_table = table; \ + return & h; \ + } + +#define SIMPLE_TRANSPORT_PLUGIN(name, t) DECLARE_PLUGIN(name, .tp_list = t) +#define SIMPLE_PLAYLIST_PLUGIN(name, p) DECLARE_PLUGIN(name, .pp_list = p) +#define SIMPLE_INPUT_PLUGIN(name, i) DECLARE_PLUGIN (name, .ip_list = i) +#define SIMPLE_EFFECT_PLUGIN(name, e) DECLARE_PLUGIN (name, .ep_list = e) +#define SIMPLE_OUTPUT_PLUGIN(name, o) DECLARE_PLUGIN (name, .op_list = o) +#define SIMPLE_VIS_PLUGIN(name, v) DECLARE_PLUGIN(name, .vp_list = v) +#define SIMPLE_GENERAL_PLUGIN(name, g) DECLARE_PLUGIN (name, .gp_list = g) +#define SIMPLE_IFACE_PLUGIN(name, i) DECLARE_PLUGIN(name, .iface = i) + +#define SIMPLE_VISUAL_PLUGIN SIMPLE_VIS_PLUGIN /* deprecated */ #define PLUGIN_COMMON_FIELDS \ - gpointer handle; \ - gchar *filename; \ gchar *description; \ - void (*init) (void); \ + gboolean (* init) (void); \ void (*cleanup) (void); \ void (*about) (void); \ void (*configure) (void); \ - PluginPreferences *settings; \ - PluginMessageResponse (*sendmsg)(gint msgtype, gpointer msgdata); + PluginPreferences *settings; -/* Sadly, this is the most we can generalize out of the disparate - plugin structs usable with typecasts - descender */ struct _Plugin { PLUGIN_COMMON_FIELDS }; -typedef enum { - OUTPUT_PLUGIN_INIT_FAIL, - OUTPUT_PLUGIN_INIT_NO_DEVICES, - OUTPUT_PLUGIN_INIT_FOUND_DEVICES, -} OutputPluginInitStatus; +struct _TransportPlugin { + PLUGIN_COMMON_FIELDS + const gchar * const * schemes; /* array ending with NULL */ + const VFSConstructor * vtable; +}; -struct _OutputPlugin { - gpointer handle; - gchar *filename; - gchar *description; +struct _PlaylistPlugin { + PLUGIN_COMMON_FIELDS + const gchar * const * extensions; /* array ending with NULL */ + gboolean (* load) (const gchar * filename, gint list, gint at); + gboolean (* save) (const gchar * filename, gint list); +}; - OutputPluginInitStatus (*init) (void); - void (*cleanup) (void); - void (*about) (void); - void (*configure) (void); +struct _OutputPlugin { + PLUGIN_COMMON_FIELDS + /* During probing, plugins with higher priority (10 to 0) are tried first. */ gint probe_priority; - void (*get_volume) (gint * l, gint * r); - void (*set_volume) (gint l, gint r); + /* Returns current volume for left and right channels (0 to 100). */ + void (* get_volume) (gint * l, gint * r); - gint (*open_audio) (gint fmt, gint rate, gint nch); - void (*write_audio) (gpointer ptr, gint length); - void (*close_audio) (void); + /* Changes volume for left and right channels (0 to 100). */ + void (* set_volume) (gint l, gint r); - void (* set_written_time) (gint time); - void (*flush) (gint time); - void (*pause) (gshort paused); + /* Begins playback of a PCM stream. <format> is one of the FMT_* + * enumeration values defined in libaudcore/audio.h. Returns nonzero on + * success. */ + gboolean (* open_audio) (gint format, gint rate, gint chans); + + /* Ends playback. Any buffered audio data is discarded. */ + void (* close_audio) (void); + + /* Returns how many bytes of data may be passed to a following write_audio() + * call. NULL if the plugin supports only blocking writes (not recommended). */ gint (* buffer_free) (void); - gint (*buffer_playing) (void); /* obsolete */ - gint (*output_time) (void); - gint (*written_time) (void); - void (*tell_audio) (gint * fmt, gint * rate, gint * nch); /* obsolete */ - void (* drain) (void); + /* Waits until buffer_free() will return a size greater than zero. + * output_time(), pause(), and flush() may be called meanwhile; if flush() + * is called, period_wait() should return immediately. NULL if the plugin + * supports only blocking writes (not recommended). */ void (* period_wait) (void); + + /* Buffers <size> bytes of data, in the format given to open_audio(). */ + void (* write_audio) (void * data, gint size); + + /* Waits until all buffered data has been heard by the user. */ + void (* drain) (void); + + /* Returns time count (in milliseconds) of how much data has been written. */ + gint (* written_time) (void); + + /* Returns time count (in milliseconds) of how much data has been heard by + * the user. */ + gint (* output_time) (void); + + /* Pauses the stream if <p> is nonzero; otherwise unpauses it. + * write_audio() will not be called while the stream is paused. */ + void (* pause) (gboolean p); + + /* Discards any buffered audio data and sets the time counter (in + * milliseconds) of data written. */ + void (* flush) (gint time); + + /* Sets the time counter (in milliseconds) of data written without + * discarding any buffered audio data. If <time> is less than the amount of + * buffered data, following calls to output_time() will return negative + * values. */ + void (* set_written_time) (gint time); }; struct _EffectPlugin { @@ -279,36 +279,26 @@ struct OutputAPI void (* abort_write) (void); }; +typedef const struct _InputPlayback InputPlayback; + struct _InputPlayback { - gchar *filename; /**< Filename URI */ - void *data; + /* Pointer to the output API functions. */ + const struct OutputAPI * output; - gint playing; /**< 1 = Playing, 0 = Stopped. */ - gboolean error; /**< TRUE if there has been an error. */ - gboolean eof; /**< TRUE if end of file has been reached- */ + /* Allows the plugin to associate data with a playback instance. */ + void (* set_data) (InputPlayback * p, void * data); - InputPlugin *plugin; - struct OutputAPI * output; - GThread *thread; + /* Returns the pointer passed to set_data. */ + void * (* get_data) (InputPlayback * p); - GMutex *pb_ready_mutex; - GCond *pb_ready_cond; - gint pb_ready_val; - gint (*set_pb_ready) (InputPlayback*); + /* Signifies that the plugin has started playback is ready to accept mseek, + * pause, and stop calls. */ + void (* set_pb_ready) (InputPlayback * p); - gint nch; /**< */ - gint rate; /**< */ - gint freq; /**< */ - gint length; - gchar *title; - - /** - * Set playback parameters. Title should be NULL and length should be 0. - * @deprecated Use of this function to set title or length is deprecated, - * please use #set_tuple() for information other than bitrate/samplerate/channels. - */ - void (*set_params) (InputPlayback * playback, const gchar * title, gint - length, gint bitrate, gint samplerate, gint channels); + /* Updates attributes of the stream. "bitrate" is in bits per second. + * "samplerate" is in hertz. */ + void (* set_params) (InputPlayback * p, gint bitrate, gint samplerate, + gint channels); /** * Sets / updates playback entry #Tuple. @@ -322,15 +312,8 @@ struct _InputPlayback { * those settings. If the settings are changed in a call to set_tuple, this * function must be called again to apply the updated settings. */ void (* set_gain_from_playlist) (InputPlayback * playback); - - /* deprecated */ - void (*pass_audio) (InputPlayback *, gint, gint, gint, gpointer, gint *); - void (*set_replaygain_info) (InputPlayback *, ReplayGainInfo *); }; -/** - * Input plugin structure. - */ struct _InputPlugin { PLUGIN_COMMON_FIELDS @@ -365,11 +348,7 @@ struct _InputPlugin { /* Must return nonzero if the plugin can handle this file. If the file * could not be opened, "file" will be NULL. (This is normal in the case of * special URI schemes like cdda:// that do not represent actual files.) */ - /* Bug: The return value should be a gboolean, not a gint. */ - gint (* is_our_file_from_vfs) (const gchar * filename, VFSFile * file); - - /* Deprecated. */ - Tuple * (* get_song_tuple) (const gchar * filename); /* Use probe_for_tuple. */ + gboolean (* is_our_file_from_vfs) (const gchar * filename, VFSFile * file); /* Must return a tuple containing metadata for this file, or NULL if no * metadata could be read. If the file could not be opened, "file" will be @@ -414,17 +393,13 @@ struct _InputPlugin { /* Must pause or unpause a file currently being played. This function will * be called from a different thread than play, but it will not be called * before the plugin calls set_pb_ready or after stop is called. */ - /* Bug: paused should be a gboolean, not a gshort. */ - /* Bug: There is no way to indicate success or failure. */ - void (* pause) (InputPlayback * playback, gshort paused); + void (* pause) (InputPlayback * playback, gboolean paused); /* Optional. Must seek to the given position in milliseconds within a file * currently being played. This function will be called from a different * thread than play, but it will not be called before the plugin calls * set_pb_ready or after stop is called. */ - /* Bug: time should be a gint, not a gulong. */ - /* Bug: There is no way to indicate success or failure. */ - void (* mseek) (InputPlayback * playback, gulong time); + void (* mseek) (InputPlayback * playback, gint time); /* Must signal a currently playing song to stop and cause play to return. * This function will be called from a different thread than play. It will @@ -437,15 +412,13 @@ struct _InputPlugin { gint (* get_time) (InputPlayback * playback); gint (* get_volume) (gint * l, gint * r); gint (* set_volume) (gint l, gint r); - - /* Deprecated. */ - gint (* is_our_file) (const gchar * filename); /* Use is_our_file_from_vfs. */ - void (* play_file) (InputPlayback * playback); /* Use play. */ - void (* seek) (InputPlayback * playback, gint time); /* Use mseek. */ }; struct _GeneralPlugin { PLUGIN_COMMON_FIELDS + + /* GtkWidget * (* get_widget) (void); */ + void * (* get_widget) (void); }; struct _VisPlugin { @@ -454,7 +427,6 @@ struct _VisPlugin { gint num_pcm_chs_wanted; gint num_freq_chs_wanted; - void (*disable_plugin) (struct _VisPlugin *); void (*playback_start) (void); void (*playback_stop) (void); void (*render_pcm) (gint16 pcm_data[2][512]); @@ -471,7 +443,6 @@ struct _VisPlugin { void * (* get_widget) (void); }; -/* undefine the macro -- struct Plugin should be used instead. */ #undef PLUGIN_COMMON_FIELDS #endif /* AUDACIOUS_PLUGIN_H */ diff --git a/src/audacious/pluginenum.c b/src/audacious/pluginenum.c index efec570..e4b8641 100644 --- a/src/audacious/pluginenum.c +++ b/src/audacious/pluginenum.c @@ -23,36 +23,25 @@ * Audacious or using our public API to be a derived work. */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifndef SHARED_SUFFIX -# define SHARED_SUFFIX G_MODULE_SUFFIX -#endif +#include <assert.h> #include <glib.h> #include <gmodule.h> -#include <gtk/gtk.h> -#include <string.h> #include <libaudcore/audstrings.h> #include <libaudgui/init.h> -#include "pluginenum.h" -#include "plugins.h" +#include "config.h" + +#ifndef SHARED_SUFFIX +# define SHARED_SUFFIX G_MODULE_SUFFIX +#endif #include "audconfig.h" #include "debug.h" -#include "effect.h" -#include "general.h" -#include "i18n.h" -#include "interface.h" -#include "main.h" -#include "output.h" -#include "playback.h" +#include "plugin.h" +#include "ui_preferences.h" #include "util.h" -#include "visualization.h" #define AUD_API_DECLARE #include "configdb.h" @@ -62,10 +51,7 @@ #include "plugins.h" #undef AUD_API_DECLARE -const gchar *plugin_dir_list[] = { - PLUGINSUBS, - NULL -}; +static const gchar * plugin_dir_list[] = {PLUGINSUBS, NULL}; static AudAPITable api_table = { .configdb_api = & configdb_api, @@ -75,200 +61,166 @@ static AudAPITable api_table = { .plugins_api = & plugins_api, .cfg = & cfg}; -extern GList *vfs_transports; -static mowgli_list_t *headers_list = NULL; - -static void input_plugin_init(Plugin * plugin) -{ - InputPlugin *p = INPUT_PLUGIN(plugin); - - if (p->init != NULL) - p->init (); -} - -static void effect_plugin_init(Plugin * plugin) -{ - EffectPlugin *p = EFFECT_PLUGIN(plugin); - - if (p->init != NULL) - p->init (); -} - -static void vis_plugin_disable_by_header (VisPlugin * header) -{ - vis_plugin_enable (plugin_by_header (header), FALSE); -} - -static void vis_plugin_init(Plugin * plugin) -{ - ((VisPlugin *) plugin)->disable_plugin = vis_plugin_disable_by_header; -} - -/*******************************************************************/ - -static void plugin2_dispose(GModule * module, const gchar * str, ...) -{ - gchar *buf; - va_list va; - - va_start(va, str); - buf = g_strdup_vprintf(str, va); - va_end(va); +typedef struct { + PluginHeader * header; + GModule * module; +} LoadedModule; - AUDDBG ("*** %s\n", buf); - g_free(buf); +static GList * loaded_modules = NULL; - g_module_close(module); -} - -void plugin2_process(PluginHeader * header, GModule * module, const gchar * filename) +static void plugin2_process (PluginHeader * header, GModule * module, const gchar * filename) { - gint i, n; - mowgli_node_t *hlist_node; - - if (header->magic != PLUGIN_MAGIC) + if (header->magic != _AUD_PLUGIN_MAGIC) { - plugin2_dispose (module, "plugin <%s> discarded, invalid module magic", - filename); + fprintf (stderr, " *** ERROR: %s is not a valid Audacious plugin.\n", filename); + g_module_close (module); return; } - if (header->api_version != __AUDACIOUS_PLUGIN_API__) + if (header->version < _AUD_PLUGIN_VERSION_MIN || header->version > _AUD_PLUGIN_VERSION) { - plugin2_dispose (module, "plugin <%s> discarded, wanting API version " - "%d, we implement API version %d", filename, header->api_version, - __AUDACIOUS_PLUGIN_API__); + fprintf (stderr, " *** ERROR: %s is not compatible with this version of Audacious.\n", filename); + g_module_close (module); return; } - hlist_node = mowgli_node_create(); - mowgli_node_add(header, hlist_node, headers_list); + LoadedModule * loaded = g_slice_new (LoadedModule); + loaded->header = header; + loaded->module = module; + loaded_modules = g_list_prepend (loaded_modules, loaded); - if (header->init) + if (header->init != NULL) { - plugin_register (filename, PLUGIN_TYPE_BASIC, 0, NULL); - header->init(); + plugin_register (PLUGIN_TYPE_LOWLEVEL, filename, 0, NULL); + header->init (); } - header->priv_assoc = g_new0(Plugin, 1); - header->priv_assoc->handle = module; - header->priv_assoc->filename = g_strdup(filename); - - n = 0; - - if (header->ip_list) + if (header->tp_list) { - for (i = 0; (header->ip_list)[i] != NULL; i++, n++) + TransportPlugin * tp; + for (gint i = 0; (tp = header->tp_list[i]); i ++) { - plugin_register (filename, PLUGIN_TYPE_INPUT, i, header->ip_list[i]); - PLUGIN((header->ip_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n); - input_plugin_init(PLUGIN((header->ip_list)[i])); + plugin_register (PLUGIN_TYPE_TRANSPORT, filename, i, tp); + if (tp->init != NULL) + tp->init (); /* FIXME: Pay attention to the return value. */ } } - if (header->op_list) + if (header->pp_list) { - for (i = 0; (header->op_list)[i] != NULL; i++, n++) + PlaylistPlugin * pp; + for (gint i = 0; (pp = header->pp_list[i]); i ++) { - OutputPlugin * plugin = header->op_list[i]; - - plugin->filename = g_strdup_printf ("%s (#%d)", filename, n); - plugin_register (filename, PLUGIN_TYPE_OUTPUT, i, plugin); + plugin_register (PLUGIN_TYPE_PLAYLIST, filename, i, pp); + if (pp->init != NULL) + pp->init (); /* FIXME: Pay attention to the return value. */ } } - if (header->ep_list) + if (header->ip_list != NULL) { - for (i = 0; (header->ep_list)[i] != NULL; i++, n++) + InputPlugin * ip; + for (gint i = 0; (ip = header->ip_list[i]) != NULL; i ++) { - plugin_register (filename, PLUGIN_TYPE_EFFECT, i, header->ep_list[i]); - PLUGIN((header->ep_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n); - effect_plugin_init(PLUGIN((header->ep_list)[i])); + plugin_register (PLUGIN_TYPE_INPUT, filename, i, ip); + if (ip->init != NULL) + ip->init (); /* FIXME: Pay attention to the return value. */ } } - - if (header->gp_list) + if (header->ep_list != NULL) { - for (i = 0; (header->gp_list)[i] != NULL; i++, n++) + EffectPlugin * ep; + for (gint i = 0; (ep = header->ep_list[i]) != NULL; i ++) { - plugin_register (filename, PLUGIN_TYPE_GENERAL, i, header->gp_list[i]); - PLUGIN((header->gp_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n); + plugin_register (PLUGIN_TYPE_EFFECT, filename, i, ep); + if (ep->init != NULL) + ep->init (); /* FIXME: Pay attention to the return value. */ } } - if (header->vp_list) + if (header->op_list != NULL) + { + OutputPlugin * op; + for (gint i = 0; (op = header->op_list[i]) != NULL; i ++) + plugin_register (PLUGIN_TYPE_OUTPUT, filename, i, op); + } + + if (header->vp_list != NULL) { - for (i = 0; (header->vp_list)[i] != NULL; i++, n++) - { - plugin_register (filename, PLUGIN_TYPE_VIS, i, header->vp_list[i]); - PLUGIN((header->vp_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n); - vis_plugin_init(PLUGIN((header->vp_list)[i])); - } + VisPlugin * vp; + for (gint i = 0; (vp = header->vp_list[i]) != NULL; i ++) + plugin_register (PLUGIN_TYPE_VIS, filename, i, vp); } - if (header->interface) - plugin_register (filename, PLUGIN_TYPE_IFACE, 0, header->interface); + if (header->gp_list != NULL) + { + GeneralPlugin * gp; + for (gint i = 0; (gp = header->gp_list[i]) != NULL; i ++) + plugin_register (PLUGIN_TYPE_GENERAL, filename, i, gp); + } + + if (header->iface != NULL) + plugin_register (PLUGIN_TYPE_IFACE, filename, 0, header->iface); } -void plugin2_unload(PluginHeader * header, mowgli_node_t * hlist_node) +static void plugin2_unload (LoadedModule * loaded) { - GModule *module; - - g_return_if_fail(header->priv_assoc != NULL); + PluginHeader * header = loaded->header; if (header->ip_list != NULL) { - for (gint i = 0; header->ip_list[i] != NULL; i ++) + InputPlugin * ip; + for (gint i = 0; (ip = header->ip_list[i]) != NULL; i ++) { - if (header->ip_list[i]->cleanup != NULL) - header->ip_list[i]->cleanup (); - - g_free (header->ip_list[i]->filename); + if (ip->settings != NULL) + plugin_preferences_cleanup (ip->settings); + if (ip->cleanup != NULL) + ip->cleanup (); } } - if (header->op_list != NULL) - { - for (gint i = 0; header->op_list[i] != NULL; i ++) - g_free (header->op_list[i]->filename); - } - if (header->ep_list != NULL) { - for (gint i = 0; header->ep_list[i] != NULL; i ++) + EffectPlugin * ep; + for (gint i = 0; (ep = header->ep_list[i]) != NULL; i ++) { - if (header->ep_list[i]->cleanup != NULL) - header->ep_list[i]->cleanup (); - - g_free (header->ep_list[i]->filename); + if (ep->settings != NULL) + plugin_preferences_cleanup (ep->settings); + if (ep->cleanup != NULL) + ep->cleanup (); } } - if (header->vp_list != NULL) + if (header->pp_list != NULL) { - for (gint i = 0; header->vp_list[i] != NULL; i ++) - g_free (header->vp_list[i]->filename); + PlaylistPlugin * pp; + for (gint i = 0; (pp = header->pp_list[i]) != NULL; i ++) + { + if (pp->settings != NULL) + plugin_preferences_cleanup (pp->settings); + if (pp->cleanup != NULL) + pp->cleanup (); + } } - if (header->gp_list != NULL) + if (header->tp_list != NULL) { - for (gint i = 0; header->gp_list[i] != NULL; i ++) - g_free (header->gp_list[i]->filename); + TransportPlugin * tp; + for (gint i = 0; (tp = header->tp_list[i]) != NULL; i ++) + { + if (tp->settings != NULL) + plugin_preferences_cleanup (tp->settings); + if (tp->cleanup != NULL) + tp->cleanup (); + } } - module = header->priv_assoc->handle; - - g_free(header->priv_assoc->filename); - g_free(header->priv_assoc); - - if (header->fini) - header->fini(); + if (header->fini != NULL) + header->fini (); - mowgli_node_delete(hlist_node, headers_list); - mowgli_node_free(hlist_node); - - g_module_close(module); + g_module_close (loaded->module); + g_slice_free (LoadedModule, loaded); } /******************************************************************/ @@ -317,70 +269,19 @@ static void scan_plugins(const gchar * path) dir_foreach(path, scan_plugin_func, NULL, NULL); } -static OutputPlugin * output_load_selected (void) -{ - if (cfg.output_path == NULL) - return NULL; - - PluginHandle * handle = plugin_by_path (cfg.output_path, PLUGIN_TYPE_OUTPUT, - cfg.output_number); - if (handle == NULL) - return NULL; - - OutputPlugin * plugin = plugin_get_header (handle); - if (plugin == NULL || plugin->init () != OUTPUT_PLUGIN_INIT_FOUND_DEVICES) - return NULL; - - return plugin; -} - -static gboolean output_probe_func (PluginHandle * handle, OutputPlugin * * result) -{ - AUDDBG ("Probing output plugin %s.\n", plugin_get_name (handle)); - OutputPlugin * plugin = plugin_get_header (handle); - - if (plugin == NULL || plugin->init == NULL || plugin->init () != - OUTPUT_PLUGIN_INIT_FOUND_DEVICES) - return TRUE; - - * result = plugin; - return FALSE; -} - -static OutputPlugin * output_probe (void) -{ - OutputPlugin * plugin = NULL; - plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) output_probe_func, - & plugin); - - if (plugin == NULL) - fprintf (stderr, "ALL OUTPUT PLUGINS FAILED TO INITIALIZE.\n"); - - return plugin; -} - void plugin_system_init(void) { + assert (g_module_supported ()); + gchar *dir; - GtkWidget *dialog; gint dirsel = 0; audgui_init (& api_table); - if (!g_module_supported()) - { - dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Module loading not supported! Plugins will not be loaded.\n")); - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); - return; - } - plugin_registry_load (); - headers_list = mowgli_list_create(); - #ifndef DISABLE_USER_PLUGIN_DIR - scan_plugins(aud_paths[BMP_PATH_USER_PLUGIN_DIR]); + scan_plugins (get_path (AUD_PATH_USER_PLUGIN_DIR)); /* * This is in a separate loop so if the user puts them in the * wrong dir we'll still get them in the right order (home dir @@ -388,7 +289,8 @@ void plugin_system_init(void) */ while (plugin_dir_list[dirsel]) { - dir = g_build_filename(aud_paths[BMP_PATH_USER_PLUGIN_DIR], plugin_dir_list[dirsel++], NULL); + dir = g_build_filename (get_path (AUD_PATH_USER_PLUGIN_DIR), + plugin_dir_list[dirsel ++], NULL); scan_plugins(dir); g_free(dir); } @@ -397,47 +299,22 @@ void plugin_system_init(void) while (plugin_dir_list[dirsel]) { - dir = g_build_filename(PLUGIN_DIR, plugin_dir_list[dirsel++], NULL); + dir = g_build_filename (get_path (AUD_PATH_PLUGIN_DIR), + plugin_dir_list[dirsel ++], NULL); scan_plugins(dir); g_free(dir); } plugin_registry_prune (); - - current_output_plugin = output_load_selected (); - - if (current_output_plugin == NULL) - current_output_plugin = output_probe (); - - general_init (); } void plugin_system_cleanup(void) { - mowgli_node_t *hlist_node; - - AUDDBG ("Shutting down plugin system.\n"); - - if (current_output_plugin != NULL) - { - if (current_output_plugin->cleanup != NULL) - current_output_plugin->cleanup (); - - current_output_plugin = NULL; - } - - general_cleanup (); - plugin_registry_save (); - /* XXX: vfs will crash otherwise. -nenolod */ - if (vfs_transports != NULL) - { - g_list_free(vfs_transports); - vfs_transports = NULL; - } - - MOWGLI_LIST_FOREACH(hlist_node, headers_list->head) plugin2_unload(hlist_node->data, hlist_node); + for (GList * node = loaded_modules; node != NULL; node = node->next) + plugin2_unload (node->data); - mowgli_list_free(headers_list); + g_list_free (loaded_modules); + loaded_modules = NULL; } diff --git a/src/audacious/pluginenum.h b/src/audacious/pluginenum.h deleted file mode 100644 index 3883ca0..0000000 --- a/src/audacious/pluginenum.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2007 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ -#ifndef AUDACIOUS_PLUGINENUM_H -#define AUDACIOUS_PLUGINENUM_H - -#include "config.h" - -#include <glib.h> - -#define PLUGIN_FILENAME(name) (name SHARED_SUFFIX) - -void plugin_system_init(void); -void plugin_system_cleanup(void); - -void module_load (const gchar * path); - -extern const gchar *plugin_dir_list[]; -extern GHashTable *plugin_matrix; - -#endif /* AUDACIOUS_PLUGINENUM_H */ diff --git a/src/audacious/plugins-api.h b/src/audacious/plugins-api.h index 79bb5a8..6d0d875 100644 --- a/src/audacious/plugins-api.h +++ b/src/audacious/plugins-api.h @@ -23,33 +23,36 @@ /* CAUTION: These functions are not thread safe. */ -/* effect.c */ -AUD_FUNC2 (void, effect_plugin_enable, PluginHandle *, plugin, gboolean, enable) - -/* general.c */ -AUD_FUNC2 (void, general_plugin_enable, PluginHandle *, plugin, gboolean, enable) - -/* main.c */ -AUD_FUNC0 (PluginHandle *, iface_plugin_get_active) -AUD_FUNC1 (void, iface_plugin_set_active, PluginHandle *, plugin) +/* plugin-init.c */ +AUD_FUNC1 (PluginHandle *, plugin_get_current, gint, type) +AUD_FUNC2 (gboolean, plugin_enable, PluginHandle *, plugin, gboolean, enable) /* plugin-registry.c */ -AUD_FUNC4 (void, plugin_get_path, PluginHandle *, plugin, const gchar * *, path, - gint *, type, gint *, number) -AUD_FUNC3 (PluginHandle *, plugin_by_path, const gchar *, path, gint, type, +AUD_FUNC1 (gint, plugin_get_type, PluginHandle *, plugin) +AUD_FUNC1 (const gchar *, plugin_get_filename, PluginHandle *, plugin) +AUD_FUNC1 (gint, plugin_get_number, PluginHandle *, plugin) +AUD_FUNC3 (PluginHandle *, plugin_lookup, gint, type, const gchar *, filename, gint, number) -AUD_FUNC1 (void *, plugin_get_header, PluginHandle *, plugin) -AUD_FUNC1 (PluginHandle *, plugin_by_header, void *, header) + +AUD_FUNC1 (const void *, plugin_get_header, PluginHandle *, plugin) +AUD_FUNC1 (PluginHandle *, plugin_by_header, const void *, header) + AUD_FUNC2 (gint, plugin_compare, PluginHandle *, a, PluginHandle *, b) AUD_FUNC3 (void, plugin_for_each, gint, type, PluginForEachFunc, func, void *, data) -AUD_FUNC1 (const gchar *, plugin_get_name, PluginHandle *, plugin) -AUD_FUNC1 (gboolean, plugin_has_about, PluginHandle *, plugin) -AUD_FUNC1 (gboolean, plugin_has_configure, PluginHandle *, plugin) + AUD_FUNC1 (gboolean, plugin_get_enabled, PluginHandle *, plugin) -AUD_FUNC2 (void, plugin_set_enabled, PluginHandle *, plugin, gboolean, enabled) AUD_FUNC3 (void, plugin_for_enabled, gint, type, PluginForEachFunc, func, void *, data) -/* visualization.c */ -AUD_FUNC2 (void, vis_plugin_enable, PluginHandle *, plugin, gboolean, enable) +AUD_FUNC1 (const gchar *, plugin_get_name, PluginHandle *, plugin) +AUD_FUNC1 (gboolean, plugin_has_about, PluginHandle *, plugin) +AUD_FUNC1 (gboolean, plugin_has_configure, PluginHandle *, plugin) + +AUD_FUNC3 (void, plugin_add_watch, PluginHandle *, plugin, PluginForEachFunc, + func, void *, data) +AUD_FUNC3 (void, plugin_remove_watch, PluginHandle *, plugin, PluginForEachFunc, + func, void *, data) + +/* New in 2.5-alpha2 */ +AUD_FUNC1 (PluginHandle *, plugin_by_widget, /* GtkWidget * */ void *, widget) diff --git a/src/audacious/plugins.h b/src/audacious/plugins.h index 6612d11..01dc6b5 100644 --- a/src/audacious/plugins.h +++ b/src/audacious/plugins.h @@ -23,19 +23,22 @@ #define AUDACIOUS_PLUGINS_H #include <glib.h> + #include <audacious/api.h> +#include <audacious/types.h> enum { - PLUGIN_TYPE_BASIC, + PLUGIN_TYPE_LOWLEVEL, + PLUGIN_TYPE_TRANSPORT, + PLUGIN_TYPE_PLAYLIST, PLUGIN_TYPE_INPUT, - PLUGIN_TYPE_OUTPUT, PLUGIN_TYPE_EFFECT, + PLUGIN_TYPE_OUTPUT, PLUGIN_TYPE_VIS, - PLUGIN_TYPE_IFACE, PLUGIN_TYPE_GENERAL, + PLUGIN_TYPE_IFACE, PLUGIN_TYPES}; -typedef struct PluginHandle PluginHandle; typedef gboolean (* PluginForEachFunc) (PluginHandle * plugin, void * data); #define AUD_API_NAME PluginsAPI @@ -53,15 +56,36 @@ enum { INPUT_KEY_MIME, INPUT_KEYS}; +/* plugin-init.c */ +void start_plugins_one (void); +void start_plugins_two (void); +void stop_plugins_two (void); +void stop_plugins_one (void); + +/* plugin-registry.c */ void plugin_registry_load (void); void plugin_registry_prune (void); void plugin_registry_save (void); void module_register (const gchar * path); -void plugin_register (const gchar * path, gint type, gint number, void * header); +void plugin_register (gint type, const gchar * path, gint number, const void * + header); + +void plugin_set_enabled (PluginHandle * plugin, gboolean enabled); +PluginHandle * transport_plugin_for_scheme (const gchar * scheme); +PluginHandle * playlist_plugin_for_extension (const gchar * extension); void input_plugin_for_key (gint key, const gchar * value, PluginForEachFunc func, void * data); +gboolean input_plugin_has_images (PluginHandle * plugin); +gboolean input_plugin_has_subtunes (PluginHandle * plugin); +gboolean input_plugin_can_write_tuple (PluginHandle * plugin); +gboolean input_plugin_has_infowin (PluginHandle * plugin); + +/* pluginenum.c */ +void plugin_system_init (void); +void plugin_system_cleanup (void); +void module_load (const gchar * path); #else diff --git a/src/audacious/preferences.h b/src/audacious/preferences.h index 0df7b0a..f82bbf0 100644 --- a/src/audacious/preferences.h +++ b/src/audacious/preferences.h @@ -125,7 +125,6 @@ typedef struct _NotebookTab { typedef enum { PREFERENCES_WINDOW, /* displayed in seperate window */ - PREFERENCES_PAGE, /* added as new page in main preferences window */ } PreferencesType; struct _PluginPreferences { diff --git a/src/audacious/probe-buffer.c b/src/audacious/probe-buffer.c index ee70daf..e9621e1 100644 --- a/src/audacious/probe-buffer.c +++ b/src/audacious/probe-buffer.c @@ -59,7 +59,8 @@ static void increase_buffer (ProbeBuffer * p, gint64 size) } if (p->filled < size) - p->filled += vfs_fread (p->buffer + p->at, 1, size - p->filled, p->file); + p->filled += vfs_fread (p->buffer + p->filled, 1, size - p->filled, + p->file); } static gint64 probe_buffer_fread (void * buffer, gint64 size, gint64 count, @@ -156,7 +157,6 @@ static gchar * probe_buffer_get_metadata (VFSFile * file, const gchar * field) static VFSConstructor probe_buffer_table = { - .uri_id = NULL, .vfs_fopen_impl = NULL, .vfs_fclose_impl = probe_buffer_fclose, .vfs_fread_impl = probe_buffer_fread, @@ -193,6 +193,7 @@ VFSFile * probe_buffer_new (const gchar * filename) file2->handle = p; file2->uri = g_strdup (filename); file2->ref = 1; + file2->sig = VFS_SIG; return file2; } diff --git a/src/audacious/probe.c b/src/audacious/probe.c index 703a10c..9c15789 100644 --- a/src/audacious/probe.c +++ b/src/audacious/probe.c @@ -82,14 +82,6 @@ static gboolean probe_func (PluginHandle * plugin, ProbeState * state) if (vfs_fseek (state->handle, 0, SEEK_SET) < 0) return FALSE; } - else if (decoder->is_our_file != NULL) - { - if (decoder->is_our_file (state->filename)) - { - state->plugin = plugin; - return FALSE; - } - } return TRUE; } @@ -176,10 +168,10 @@ static void probe_by_mime (ProbeState * state) static void probe_by_content (ProbeState * state) { AUDDBG ("Probing by content.\n"); - plugin_for_each (PLUGIN_TYPE_INPUT, (PluginForEachFunc) probe_func, state); + plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc) probe_func, state); } -InputPlugin * file_find_decoder (const gchar * filename, gboolean fast) +PluginHandle * file_find_decoder (const gchar * filename, gboolean fast) { ProbeState state; @@ -211,69 +203,72 @@ DONE: if (state.handle != NULL) vfs_fclose (state.handle); - return (state.plugin != NULL) ? plugin_get_header (state.plugin) : NULL; + return state.plugin; } -Tuple * file_read_tuple (const gchar * filename, InputPlugin * decoder) +Tuple * file_read_tuple (const gchar * filename, PluginHandle * decoder) { - if (decoder->get_song_tuple != NULL) - return decoder->get_song_tuple (filename); + InputPlugin * ip = plugin_get_header (decoder); + g_return_val_if_fail (ip, NULL); + g_return_val_if_fail (ip->probe_for_tuple, NULL); - if (decoder->probe_for_tuple != NULL) - { - VFSFile * handle = vfs_fopen (filename, "r"); - Tuple * tuple; + gchar * real = filename_split_subtune (filename, NULL); + VFSFile * handle = vfs_fopen (real, "r"); + g_free (real); - if (handle == NULL) - return NULL; + Tuple * tuple = ip->probe_for_tuple (filename, handle); - tuple = decoder->probe_for_tuple (filename, handle); + if (handle) vfs_fclose (handle); - return tuple; - } - return NULL; + return tuple; } -gboolean file_read_image (const gchar * filename, InputPlugin * decoder, +gboolean file_read_image (const gchar * filename, PluginHandle * decoder, void * * data, gint * size) { - VFSFile * handle; - gboolean success; - - if (decoder->get_song_image == NULL) + if (! input_plugin_has_images (decoder)) return FALSE; - handle = vfs_fopen (filename, "r"); - success = decoder->get_song_image (filename, handle, data, size); + InputPlugin * ip = plugin_get_header (decoder); + g_return_val_if_fail (ip, FALSE); + g_return_val_if_fail (ip->get_song_image, FALSE); + + gchar * real = filename_split_subtune (filename, NULL); + VFSFile * handle = vfs_fopen (real, "r"); + g_free (real); + + gboolean success = ip->get_song_image (filename, handle, data, size); - if (handle != NULL) + if (handle) vfs_fclose (handle); return success; } -gboolean file_can_write_tuple (const gchar * filename, InputPlugin * decoder) +gboolean file_can_write_tuple (const gchar * filename, PluginHandle * decoder) { - return (decoder->update_song_tuple != NULL); + return input_plugin_can_write_tuple (decoder); } -gboolean file_write_tuple (const gchar * filename, InputPlugin * decoder, +gboolean file_write_tuple (const gchar * filename, PluginHandle * decoder, const Tuple * tuple) { - VFSFile * handle; - gboolean success; + InputPlugin * ip = plugin_get_header (decoder); + g_return_val_if_fail (ip, FALSE); + g_return_val_if_fail (ip->update_song_tuple, FALSE); - if (decoder->update_song_tuple == NULL) - return FALSE; + gchar * real = filename_split_subtune (filename, NULL); + VFSFile * handle = vfs_fopen (real, "r+"); + g_free (real); - handle = vfs_fopen (filename, "r+"); - - if (handle == NULL) + if (! handle) return FALSE; - success = decoder->update_song_tuple (tuple, handle); - vfs_fclose (handle); + gboolean success = ip->update_song_tuple (tuple, handle); + + if (handle) + vfs_fclose (handle); if (success) playlist_rescan_file (filename); @@ -281,11 +276,15 @@ gboolean file_write_tuple (const gchar * filename, InputPlugin * decoder, return success; } -gboolean custom_infowin (const gchar * filename, InputPlugin * decoder) +gboolean custom_infowin (const gchar * filename, PluginHandle * decoder) { - if (decoder->file_info_box == NULL) + if (! input_plugin_has_infowin (decoder)) return FALSE; - decoder->file_info_box (filename); + InputPlugin * ip = plugin_get_header (decoder); + g_return_val_if_fail (ip, FALSE); + g_return_val_if_fail (ip->file_info_box, FALSE); + + ip->file_info_box (filename); return TRUE; } diff --git a/src/audacious/signals.c b/src/audacious/signals.c index 6b8a30d..99fbbc5 100644 --- a/src/audacious/signals.c +++ b/src/audacious/signals.c @@ -1,100 +1,48 @@ /* - * Audacious - * Copyright (c) 2005-2007 Yoshiki Yazawa - * Copyright 2009 John Lindgren (POSIX threaded signal handling) + * signals.c + * Copyright 2009 John Lindgren * - * 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 file is part of Audacious. * - * 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. + * Audacious 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 or version 3 of the License. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses>. + * Audacious 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. * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. + * You should have received a copy of the GNU General Public License along with + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. */ #include <signal.h> - #include <glib.h> -#include <gtk/gtk.h> - #include <libaudcore/eventqueue.h> -#include "audconfig.h" #include "config.h" -#include "main.h" -#include "signals.h" - -#ifdef USE_EGGSM -#include "eggsmclient.h" -#endif +#ifdef HAVE_SIGWAIT static sigset_t signal_set; -#ifdef USE_EGGSM -static void -signal_session_quit_cb(EggSMClient *client, gpointer user_data) -{ - const gchar * argv[2]; - - g_print("Session quit requested. Saving state and shutting down.\n"); - - argv[0] = "audacious"; - argv[1] = g_strdup_printf ("--display=%s", gdk_display_get_name (gdk_display_get_default())); - egg_sm_client_set_restart_command (client, 2, argv); - - aud_quit(); -} - -static void -signal_session_save_cb(EggSMClient *client, GKeyFile *state_file, gpointer user_data) -{ - const gchar * argv[2]; - - g_print("Session save requested. Saving state.\n"); - - argv[0] = "audacious"; - argv[1] = g_strdup_printf ("--display=%s", gdk_display_get_name (gdk_display_get_default())); - egg_sm_client_set_restart_command (client, 2, argv); - - aud_config_save(); -} -#endif - static void * signal_thread (void * data) { gint signal; while (! sigwait (& signal_set, & signal)) - event_queue ("quit", 0); + event_queue ("quit", NULL); return NULL; } +#endif /* Must be called before any threads are created. */ -void signal_handlers_init (void) +void signals_init (void) { -#ifdef USE_EGGSM - EggSMClient *client; - - client = egg_sm_client_get (); - if (client != NULL) - { - egg_sm_client_set_mode (EGG_SM_CLIENT_MODE_NORMAL); - g_signal_connect (client, "quit", - G_CALLBACK (signal_session_quit_cb), NULL); - g_signal_connect (client, "save-state", - G_CALLBACK (signal_session_save_cb), NULL); - - } -#endif - +#ifdef HAVE_SIGWAIT sigemptyset (& signal_set); sigaddset (& signal_set, SIGHUP); sigaddset (& signal_set, SIGINT); @@ -103,4 +51,5 @@ void signal_handlers_init (void) sigprocmask (SIG_BLOCK, & signal_set, NULL); g_thread_create (signal_thread, NULL, FALSE, NULL); +#endif } diff --git a/src/audacious/signals.h b/src/audacious/signals.h deleted file mode 100644 index c9fcbe6..0000000 --- a/src/audacious/signals.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Audacious - * Copyright (c) 2005-2007 Audacious development team - * - * BMP - Cross-platform multimedia player - * Copyright (C) 2003-2005 BMP 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ - -#ifndef AUDACIOUS_SIGNALS_H -#define AUDACIOUS_SIGNALS_H - -void signal_handlers_init(void); - -#endif /* AUDACIOUS_SIGNALS_H */ diff --git a/src/audacious/smclient.c b/src/audacious/smclient.c new file mode 100644 index 0000000..bb5e2ff --- /dev/null +++ b/src/audacious/smclient.c @@ -0,0 +1,75 @@ +/* + * Audacious + * Copyright (c) 2005-2007 Yoshiki Yazawa + * + * 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 <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#include <gdk/gdk.h> +#include <libaudcore/hook.h> + +#include "audconfig.h" +#include "config.h" + +#ifdef USE_EGGSM + +#include "eggsmclient.h" + +static void +signal_session_quit_cb(EggSMClient *client, gpointer user_data) +{ + const gchar * argv[2]; + + g_print("Session quit requested. Saving state and shutting down.\n"); + + argv[0] = "audacious"; + argv[1] = g_strdup_printf ("--display=%s", gdk_display_get_name (gdk_display_get_default())); + egg_sm_client_set_restart_command (client, 2, argv); + + hook_call ("quit", NULL); +} + +static void +signal_session_save_cb(EggSMClient *client, GKeyFile *state_file, gpointer user_data) +{ + const gchar * argv[2]; + + g_print("Session save requested. Saving state.\n"); + + argv[0] = "audacious"; + argv[1] = g_strdup_printf ("--display=%s", gdk_display_get_name (gdk_display_get_default())); + egg_sm_client_set_restart_command (client, 2, argv); + + aud_config_save(); +} +#endif + +void smclient_init (void) +{ +#ifdef USE_EGGSM + EggSMClient *client; + + client = egg_sm_client_get (); + if (client != NULL) + { + g_signal_connect (client, "quit", + G_CALLBACK (signal_session_quit_cb), NULL); + g_signal_connect (client, "save-state", + G_CALLBACK (signal_session_save_cb), NULL); + + } +#endif +} diff --git a/src/audacious/types.h b/src/audacious/types.h index 7766828..50faaba 100644 --- a/src/audacious/types.h +++ b/src/audacious/types.h @@ -27,22 +27,29 @@ #define AUD_EQUALIZER_NBANDS 10 #define EQUALIZER_MAX_GAIN 12 -typedef struct _Plugin Plugin; -typedef struct _InputPlugin InputPlugin; -typedef struct _OutputPlugin OutputPlugin; -typedef struct _EffectPlugin EffectPlugin; -typedef struct _GeneralPlugin GeneralPlugin; -typedef struct _VisPlugin VisPlugin; - -#define PLUGIN(x) ((Plugin *) (x)) -#define INPUT_PLUGIN(x) ((InputPlugin *) (x)) -#define OUTPUT_PLUGIN(x) ((OutputPlugin *) (x)) -#define EFFECT_PLUGIN(x) ((EffectPlugin *) (x)) -#define GENERAL_PLUGIN(x) ((GeneralPlugin *) (x)) -#define VIS_PLUGIN(x) ((VisPlugin *) (x)) - -typedef struct _Interface Interface; +typedef struct PluginHandle PluginHandle; + +#ifdef _AUDACIOUS_CORE +typedef const struct _Plugin Plugin; +#endif + +typedef const struct _InputPlugin InputPlugin; +typedef const struct _OutputPlugin OutputPlugin; +typedef const struct _EffectPlugin EffectPlugin; +typedef const struct _GeneralPlugin GeneralPlugin; +typedef const struct _VisPlugin VisPlugin; +typedef const struct _TransportPlugin TransportPlugin; +typedef const struct _PlaylistPlugin PlaylistPlugin; + +typedef struct _Iface Iface; typedef struct _PluginPreferences PluginPreferences; typedef struct _PreferencesWidget PreferencesWidget; +typedef struct { + gfloat track_gain; /* dB */ + gfloat track_peak; /* 0-1 */ + gfloat album_gain; /* dB */ + gfloat album_peak; /* 0-1 */ +} ReplayGainInfo; + #endif diff --git a/src/audacious/ui_preferences.c b/src/audacious/ui_preferences.c index 4d69b0b..7dbc2f9 100644 --- a/src/audacious/ui_preferences.c +++ b/src/audacious/ui_preferences.c @@ -1,5 +1,5 @@ /* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2007 Audacious development team. + * Copyright (C) 2005-2010 Audacious 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 @@ -17,44 +17,29 @@ * Audacious or using our public API to be a derived work. */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <glib.h> -#include <gtk/gtk.h> #include <string.h> -#include <stddef.h> #include <stdio.h> -#include <sys/types.h> -#include <dirent.h> -#include <unistd.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> + #include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> #include <libaudcore/hook.h> #include "audconfig.h" -#include "compatibility.h" +#include "config.h" +#include "configdb.h" #include "debug.h" +#include "glib-compat.h" +#include "gtk-compat.h" #include "i18n.h" #include "misc.h" -#include "playback.h" -#include "plugin.h" -#include "pluginenum.h" -#include "plugins.h" -#include "effect.h" -#include "general.h" #include "output.h" +#include "playback.h" #include "playlist.h" #include "playlist-utils.h" -#include "visualization.h" -#include "util.h" -#include "configdb.h" +#include "plugin.h" +#include "plugins.h" #include "preferences.h" - #include "ui_preferences.h" #define TITLESTRING_UPDATE_TIMEOUT 3 @@ -68,22 +53,6 @@ enum CategoryViewCols { CATEGORY_VIEW_N_COLS }; -enum PluginViewCols { - PLUGIN_VIEW_COL_ACTIVE, - PLUGIN_VIEW_COL_DESC, - PLUGIN_VIEW_COL_FILENAME, - PLUGIN_VIEW_COL_ID, - PLUGIN_VIEW_COL_PLUGIN_PTR, - PLUGIN_VIEW_N_COLS -}; - -enum PluginViewType { - PLUGIN_VIEW_TYPE_INPUT, - PLUGIN_VIEW_TYPE_GENERAL, - PLUGIN_VIEW_TYPE_VIS, - PLUGIN_VIEW_TYPE_EFFECT -}; - typedef struct { const gchar *icon_path; const gchar *name; @@ -94,11 +63,6 @@ typedef struct { const gchar *tag; } TitleFieldTag; -typedef struct { - gint x; - gint y; -} MenuPos; - static /* GtkWidget * */ void * prefswin = NULL; static GtkWidget *filepopup_settings = NULL; static GtkWidget *category_treeview = NULL; @@ -121,12 +85,11 @@ GtkWidget *filepopup_for_tuple_settings_button; static gint titlestring_timeout_counter = 0; static Category categories[] = { - {DATA_DIR "/images/audio.png", N_("Audio")}, - {DATA_DIR "/images/replay_gain.png", N_("Replay Gain")}, - {DATA_DIR "/images/connectivity.png", N_("Network")}, - {DATA_DIR "/images/playback.png", N_("Playback")}, - {DATA_DIR "/images/playlist.png", N_("Playlist")}, - {DATA_DIR "/images/plugins.png", N_("Plugins")}, + {"audio.png", N_("Audio")}, + {"replay_gain.png", N_("Replay Gain")}, + {"connectivity.png", N_("Network")}, + {"playlist.png", N_("Playlist")}, + {"plugins.png", N_("Plugins")}, }; static gint n_categories = G_N_ELEMENTS(categories); @@ -191,23 +154,27 @@ static PreferencesWidget audio_page_widgets[] = { N_("Use software volume control. This may be useful for situations where your audio system does not support controlling the playback volume."), FALSE}, }; -static PreferencesWidget rg_params_elements[] = { - {WIDGET_SPIN_BTN, N_("Preamp:"), &cfg.replay_gain_preamp, NULL, NULL, FALSE, {.spin_btn = {-15, 15, 0.01, N_("dB")}}, VALUE_FLOAT}, - {WIDGET_SPIN_BTN, N_("Default gain:"), &cfg.default_gain, NULL, N_("This gain will be used if file doesn't contain Replay Gain metadata."), FALSE, {.spin_btn = {-15, 15, 0.01, N_("dB")}}, VALUE_FLOAT}, - {WIDGET_LABEL, N_("<span size=\"small\">Please remember that the most efficient way to prevent signal clipping is not to use positive values above.</span>"), NULL, NULL, NULL, FALSE, {.label = {"gtk-info"}}}, -}; - -static PreferencesWidget replay_gain_page_widgets[] = { - {WIDGET_LABEL, N_("<b>Replay Gain configuration</b>"), NULL, NULL, NULL, FALSE}, - {WIDGET_CHK_BTN, N_("Enable Replay Gain"), &cfg.enable_replay_gain, NULL, NULL, FALSE}, - {WIDGET_LABEL, N_("<b>Replay Gain mode</b>"), NULL, NULL, NULL, TRUE}, - {WIDGET_RADIO_BTN, N_("Track gain/peak"), &cfg.replay_gain_track, NULL, NULL, TRUE}, - {WIDGET_RADIO_BTN, N_("Album gain/peak"), &cfg.replay_gain_album, NULL, NULL, TRUE}, - {WIDGET_LABEL, N_("<b>Miscellaneous</b>"), NULL, NULL, NULL, TRUE}, - {WIDGET_CHK_BTN, N_("Enable peak info clipping prevention"), &cfg.enable_clipping_prevention, NULL, - N_("Use peak value from Replay Gain info for clipping prevention"), TRUE}, - {WIDGET_TABLE, NULL, NULL, NULL, NULL, TRUE, {.table = {rg_params_elements, G_N_ELEMENTS(rg_params_elements)}}}, -}; +static PreferencesWidget rg_params_elements[] = +{{WIDGET_SPIN_BTN, N_("Amplify all files:"), & cfg.replay_gain_preamp, NULL, + NULL, FALSE, {.spin_btn = {-15, 15, 0.01, N_("dB")}}, VALUE_FLOAT}, +{WIDGET_SPIN_BTN, N_("Amplify untagged files:"), & cfg.default_gain, NULL, + NULL, FALSE, {.spin_btn = {-15, 15, 0.01, N_("dB")}}, VALUE_FLOAT}}; + +static PreferencesWidget replay_gain_page_widgets[] = + {{WIDGET_LABEL, N_("<b>Replay Gain</b>"), NULL, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Enable Replay Gain"), &cfg.enable_replay_gain, NULL, + NULL, FALSE}, + {WIDGET_LABEL, N_("<b>Mode</b>"), NULL, NULL, NULL, TRUE}, + {WIDGET_RADIO_BTN, N_("Single track mode"), &cfg.replay_gain_track, NULL, + NULL, TRUE}, + {WIDGET_RADIO_BTN, N_("Album mode"), &cfg.replay_gain_album, NULL, NULL, + TRUE}, + {WIDGET_LABEL, N_("<b>Adjust Levels</b>"), NULL, NULL, NULL, TRUE}, + {WIDGET_TABLE, NULL, NULL, NULL, NULL, TRUE, {.table = {rg_params_elements, + G_N_ELEMENTS (rg_params_elements)}}}, + {WIDGET_LABEL, N_("<b>Clipping Prevention</b>"), NULL, NULL, NULL, TRUE}, + {WIDGET_CHK_BTN, N_("Enable clipping prevention"), + & cfg.enable_clipping_prevention, NULL, NULL, TRUE}}; static PreferencesWidget proxy_host_port_elements[] = { {WIDGET_ENTRY, N_("Proxy hostname:"), "proxy_host", NULL, NULL, FALSE, {.entry = {FALSE}}, VALUE_CFG_STRING}, @@ -230,16 +197,6 @@ static PreferencesWidget connectivity_page_widgets[] = { {WIDGET_LABEL, N_("<span size=\"small\">Changing these settings will require a restart of Audacious.</span>"), NULL, NULL, NULL, FALSE, {.label = {"gtk-dialog-warning"}}}, }; -static PreferencesWidget playback_page_widgets[] = { - {WIDGET_LABEL, N_("<b>Playback</b>"), NULL, NULL, NULL, FALSE}, - {WIDGET_CHK_BTN, N_("Continue playback on startup"), &cfg.resume_playback_on_startup, NULL, - N_("When Audacious starts, automatically begin playing from the point where we stopped before."), FALSE}, - {WIDGET_CHK_BTN, N_("Don't advance in the playlist"), &cfg.no_playlist_advance, NULL, - N_("When finished playing a song, don't automatically advance to the next."), FALSE}, - {WIDGET_CHK_BTN, N_("Clear current playlist when opening new files"), - & cfg.clear_playlist, NULL, NULL, FALSE}, -}; - static PreferencesWidget chardet_elements[] = { {WIDGET_COMBO_BOX, N_("Auto character encoding detector for:"), &cfg.chardet_detector, NULL, NULL, TRUE, {.combo = {chardet_detector_presets, G_N_ELEMENTS(chardet_detector_presets), @@ -253,25 +210,20 @@ static PreferencesWidget chardet_elements[] = { }; static PreferencesWidget playlist_page_widgets[] = { + {WIDGET_LABEL, N_("<b>Behavior</b>"), NULL, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Continue playback on startup"), + & cfg.resume_playback_on_startup, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"), + & cfg.advance_on_delete, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"), + & cfg.clear_playlist, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"), + & cfg.open_to_temporary, NULL, NULL, FALSE}, {WIDGET_LABEL, N_("<b>Metadata</b>"), NULL, NULL, NULL, FALSE}, {WIDGET_TABLE, NULL, NULL, NULL, NULL, TRUE, {.table = {chardet_elements, G_N_ELEMENTS(chardet_elements)}}}, }; static void prefswin_page_queue_destroy(CategoryQueueEntry *ent); -void create_plugin_preferences_page(PluginPreferences *settings); -void destroy_plugin_preferences_page(PluginPreferences *settings); - -static void output_about (OutputPlugin * plugin) -{ - if (plugin->about != NULL) - plugin->about (); -} - -static void output_configure (OutputPlugin * plugin) -{ - if (plugin->configure != NULL) - plugin->configure (); -} static void change_category(GtkNotebook * notebook, @@ -288,218 +240,6 @@ change_category(GtkNotebook * notebook, gtk_notebook_set_current_page(notebook, index); } -static void output_plugin_open_prefs (GtkComboBox * combo, void * unused) -{ - output_configure (g_list_nth_data (get_output_list (), - gtk_combo_box_get_active (combo))); -} - -static void output_plugin_open_info (GtkComboBox * combo, void * unused) -{ - output_about (g_list_nth_data (get_output_list (), gtk_combo_box_get_active - (combo))); -} - -static void -plugin_toggle(GtkCellRendererToggle * cell, - const gchar * path_str, - gpointer data) -{ - GtkTreeModel *model = GTK_TREE_MODEL(data); - GtkTreeIter iter; - GtkTreePath *path = gtk_tree_path_new_from_string(path_str); - Plugin *plugin = NULL; - gint plugin_type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(data), "plugin_type")); - gboolean enabled; - - /* get toggled iter */ - gtk_tree_model_get_iter(model, &iter, path); - - gtk_tree_model_get (model, & iter, PLUGIN_VIEW_COL_ACTIVE, & enabled, - PLUGIN_VIEW_COL_PLUGIN_PTR, & plugin, -1); - enabled = ! enabled; - - switch (plugin_type) - { - case PLUGIN_VIEW_TYPE_INPUT: - plugin_set_enabled (plugin_by_header (plugin), enabled); - break; - case PLUGIN_VIEW_TYPE_GENERAL: - general_plugin_enable (plugin_by_header (plugin), enabled); - break; - case PLUGIN_VIEW_TYPE_VIS: - vis_plugin_enable (plugin_by_header (plugin), enabled); - break; - case PLUGIN_VIEW_TYPE_EFFECT: - effect_plugin_enable (plugin_by_header (plugin), enabled); - break; - } - - gtk_list_store_set ((GtkListStore *) model, & iter, PLUGIN_VIEW_COL_ACTIVE, - enabled, -1); - - if (plugin && plugin->settings && plugin->settings->type == PREFERENCES_PAGE) { - if (enabled) - create_plugin_preferences_page(plugin->settings); - else - destroy_plugin_preferences_page(plugin->settings); - } - /* clean up */ - gtk_tree_path_free(path); -} - -static void on_output_plugin_cbox_changed (GtkComboBox * combo, void * unused) -{ - set_current_output_plugin (g_list_nth_data (get_output_list (), - gtk_combo_box_get_active (combo))); -} - -static void -on_output_plugin_cbox_realize(GtkComboBox * cbox, - gpointer data) -{ - GList *olist = get_output_list(); - OutputPlugin * op; - gint i = 0, selected = 0; - - if (olist == NULL) { - gtk_widget_set_sensitive(GTK_WIDGET(cbox), FALSE); - return; - } - - for (i = 0; olist != NULL; i++, olist = g_list_next(olist)) { - op = OUTPUT_PLUGIN(olist->data); - - if (olist->data == current_output_plugin) - selected = i; - - gtk_combo_box_append_text(cbox, op->description); - } - - gtk_combo_box_set_active(cbox, selected); - g_signal_connect(cbox, "changed", - G_CALLBACK(on_output_plugin_cbox_changed), NULL); -} - -static void -on_plugin_view_realize(GtkTreeView * treeview, - GCallback callback, - gpointer data, - gint plugin_type) -{ - GtkListStore *store; - GtkTreeIter iter; - - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - GList *ilist; - gchar *description[2]; - gint id = 0; - - GList *list = (GList *) data; - - store = gtk_list_store_new(PLUGIN_VIEW_N_COLS, - G_TYPE_BOOLEAN, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER); - g_object_set_data(G_OBJECT(store), "plugin_type" , GINT_TO_POINTER(plugin_type)); - - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, _("Enabled")); - gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); - gtk_tree_view_column_set_spacing(column, 4); - gtk_tree_view_column_set_resizable(column, FALSE); - gtk_tree_view_column_set_fixed_width(column, 50); - - renderer = gtk_cell_renderer_toggle_new(); - g_signal_connect(renderer, "toggled", - G_CALLBACK(callback), store); - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_set_attributes(column, renderer, "active", - PLUGIN_VIEW_COL_ACTIVE, NULL); - - gtk_tree_view_append_column(treeview, column); - - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, _("Description")); - gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_column_set_spacing(column, 4); - gtk_tree_view_column_set_resizable(column, TRUE); - - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, renderer, FALSE); - gtk_tree_view_column_set_attributes(column, renderer, - "text", PLUGIN_VIEW_COL_DESC, NULL); - gtk_tree_view_append_column(treeview, column); - - column = gtk_tree_view_column_new(); - - gtk_tree_view_column_set_title(column, _("Filename")); - gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_column_set_spacing(column, 4); - gtk_tree_view_column_set_resizable(column, TRUE); - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_set_attributes(column, renderer, "text", - PLUGIN_VIEW_COL_FILENAME, NULL); - - gtk_tree_view_append_column(treeview, column); - - MOWGLI_ITER_FOREACH(ilist, list) - { - Plugin *plugin = PLUGIN(ilist->data); - gboolean enabled = plugin_get_enabled (plugin_by_header (plugin)); - - description[0] = g_strdup(plugin->description); - description[1] = g_strdup(plugin->filename); - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - PLUGIN_VIEW_COL_ACTIVE, enabled, - PLUGIN_VIEW_COL_DESC, description[0], - PLUGIN_VIEW_COL_FILENAME, description[1], - PLUGIN_VIEW_COL_ID, id++, - PLUGIN_VIEW_COL_PLUGIN_PTR, plugin, -1); - - g_free(description[1]); - g_free(description[0]); - } - - gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store)); -} - -static void -on_input_plugin_view_realize(GtkTreeView * treeview, - gpointer data) -{ - on_plugin_view_realize (treeview, (GCallback) plugin_toggle, plugin_get_list(PLUGIN_TYPE_INPUT), PLUGIN_VIEW_TYPE_INPUT); -} - -static void -on_effect_plugin_view_realize(GtkTreeView * treeview, - gpointer data) -{ - on_plugin_view_realize (treeview, (GCallback) plugin_toggle, plugin_get_list - (PLUGIN_TYPE_EFFECT), PLUGIN_VIEW_TYPE_EFFECT); -} - -static void -on_general_plugin_view_realize(GtkTreeView * treeview, - gpointer data) -{ - on_plugin_view_realize (treeview, (GCallback) plugin_toggle, plugin_get_list - (PLUGIN_TYPE_GENERAL), PLUGIN_VIEW_TYPE_GENERAL); -} - -static void -on_vis_plugin_view_realize(GtkTreeView * treeview, - gpointer data) -{ - on_plugin_view_realize(treeview, G_CALLBACK(plugin_toggle), plugin_get_list(PLUGIN_TYPE_VIS), PLUGIN_VIEW_TYPE_VIS); -} - static void editable_insert_text(GtkEditable * editable, const gchar * text, @@ -529,51 +269,11 @@ titlestring_tag_menu_callback(GtkMenuItem * menuitem, } static void -util_menu_position(GtkMenu * menu, gint * x, gint * y, - gboolean * push_in, gpointer data) -{ - GtkRequisition requisition; - gint screen_width; - gint screen_height; - MenuPos *pos = data; - - gtk_widget_size_request(GTK_WIDGET(menu), &requisition); - - screen_width = gdk_screen_width(); - screen_height = gdk_screen_height(); - - *x = CLAMP(pos->x - 2, 0, MAX(0, screen_width - requisition.width)); - *y = CLAMP(pos->y - 2, 0, MAX(0, screen_height - requisition.height)); -} - -static void on_titlestring_help_button_clicked(GtkButton * button, gpointer data) { - GtkMenu *menu; - MenuPos *pos = g_newa(MenuPos, 1); - GdkWindow *parent, *window; - - gint x_ro, y_ro; - gint x_widget, y_widget; - gint x_size, y_size; - - g_return_if_fail (button != NULL); - g_return_if_fail (GTK_IS_MENU (data)); - - parent = gtk_widget_get_parent_window(GTK_WIDGET(button)); - window = gtk_widget_get_window(GTK_WIDGET(button)); - - gdk_drawable_get_size(parent, &x_size, &y_size); - gdk_window_get_root_origin(window, &x_ro, &y_ro); - gdk_window_get_position(window, &x_widget, &y_widget); - - pos->x = x_size + x_ro; - pos->y = y_size + y_ro - 100; - - menu = GTK_MENU(data); - gtk_menu_popup (menu, NULL, NULL, util_menu_position, pos, - 0, GDK_CURRENT_TIME); + GtkMenu * menu = data; + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME); } @@ -674,8 +374,7 @@ plugin_preferences_cancel(GtkWidget *widget, PluginPreferences *settings) gtk_widget_destroy(GTK_WIDGET(settings->data)); } -static void -plugin_preferences_destroy(GtkWidget *widget, PluginPreferences *settings) +static void plugin_preferences_destroy(GtkWidget *widget, PluginPreferences *settings) { gtk_widget_destroy(widget); @@ -685,8 +384,7 @@ plugin_preferences_destroy(GtkWidget *widget, PluginPreferences *settings) settings->data = NULL; } -static void -create_plugin_preferences(PluginPreferences *settings) +void plugin_preferences_show (PluginPreferences * settings) { GtkWidget *window; GtkWidget *vbox, *bbox, *ok, *apply, *cancel; @@ -738,112 +436,13 @@ create_plugin_preferences(PluginPreferences *settings) settings->data = (gpointer)window; } -static void -plugin_treeview_open_prefs(GtkTreeView *treeview) -{ - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - Plugin *plugin = NULL; - - selection = gtk_tree_view_get_selection(treeview); - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) - return; - gtk_tree_model_get(model, &iter, PLUGIN_VIEW_COL_PLUGIN_PTR, &plugin, -1); - - g_return_if_fail(plugin != NULL); - g_return_if_fail((plugin->configure != NULL) || - ((plugin->settings != NULL) && (plugin->settings->type == PREFERENCES_WINDOW))); - - if (plugin->configure != NULL) - plugin->configure(); - else - create_plugin_preferences(plugin->settings); -} - -static void -plugin_treeview_open_info(GtkTreeView *treeview) -{ - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - Plugin *plugin = NULL; - - selection = gtk_tree_view_get_selection(treeview); - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) - return; - gtk_tree_model_get(model, &iter, PLUGIN_VIEW_COL_PLUGIN_PTR, &plugin, -1); - - g_return_if_fail(plugin != NULL); - plugin->about(); -} - -static void -plugin_treeview_enable_prefs(GtkTreeView * treeview, GtkButton * button) -{ - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - Plugin *plugin = NULL; - - selection = gtk_tree_view_get_selection(treeview); - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) - return; - - gtk_tree_model_get(model, &iter, PLUGIN_VIEW_COL_PLUGIN_PTR, &plugin, -1); - - g_return_if_fail(plugin != NULL); - - gtk_widget_set_sensitive(GTK_WIDGET(button), - ((plugin->configure != NULL) || - (plugin->settings ? (plugin->settings->type == PREFERENCES_WINDOW) : FALSE))); -} - -static void -plugin_treeview_enable_info(GtkTreeView * treeview, GtkButton * button) +void plugin_preferences_cleanup (PluginPreferences * p) { - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - Plugin *plugin = NULL; - - selection = gtk_tree_view_get_selection(treeview); - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) - return; - - gtk_tree_model_get(model, &iter, PLUGIN_VIEW_COL_PLUGIN_PTR, &plugin, -1); - - g_return_if_fail(plugin != NULL); - - gtk_widget_set_sensitive(GTK_WIDGET(button), plugin->about != NULL); -} - - -static void -output_plugin_enable_info(GtkComboBox * cbox, GtkButton * button) -{ - GList *plist; - - gint id = gtk_combo_box_get_active(cbox); - - plist = get_output_list(); - plist = g_list_nth(plist, id); - - gtk_widget_set_sensitive(GTK_WIDGET(button), - OUTPUT_PLUGIN(plist->data)->about != NULL); -} - -static void -output_plugin_enable_prefs(GtkComboBox * cbox, GtkButton * button) -{ - GList *plist; - gint id = gtk_combo_box_get_active(cbox); - - plist = get_output_list(); - plist = g_list_nth(plist, id); - - gtk_widget_set_sensitive(GTK_WIDGET(button), - OUTPUT_PLUGIN(plist->data)->configure != NULL); + if (p->data != NULL) + { + gtk_widget_destroy (p->data); + p->data = NULL; + } } static void @@ -920,8 +519,13 @@ on_category_treeview_realize(GtkTreeView * treeview, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store)); - for (i = 0; i < n_categories; i++) { - img = gdk_pixbuf_new_from_file(categories[i].icon_path, NULL); + for (i = 0; i < n_categories; i ++) + { + gchar * path = g_strdup_printf ("%s/images/%s", + get_path (AUD_PATH_DATA_DIR), categories[i].icon_path); + img = gdk_pixbuf_new_from_file (path, NULL); + g_free (path); + gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, CATEGORY_VIEW_COL_ICON, img, @@ -1134,20 +738,19 @@ on_cbox_changed_string(GtkComboBox * combobox, PreferencesWidget *widget) gint position = 0; position = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox)); - + g_free(*((gchar **)widget->cfg)); - + *((gchar **)widget->cfg) = g_strdup(widget->data.combo.elements[position].value); } -static void -on_cbox_realize(GtkComboBox *combobox, PreferencesWidget * widget) +static void on_cbox_realize (GtkWidget * combobox, PreferencesWidget * widget) { guint i=0,index=0; - for(i=0; i<widget->data.combo.n_elements; i++) { - gtk_combo_box_append_text(combobox, _(widget->data.combo.elements[i].label)); - } + for (i = 0; i < widget->data.combo.n_elements; i ++) + gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox, + _(widget->data.combo.elements[i].label)); if (widget->data.combo.enabled) { switch (widget->cfg_type) { @@ -1198,8 +801,8 @@ create_filepopup_settings(void) GtkWidget *label_misc; GtkWidget *label_delay; - GtkObject *recurse_for_cover_depth_adj; - GtkObject *delay_adj; + GtkAdjustment *recurse_for_cover_depth_adj; + GtkAdjustment *delay_adj; GtkWidget *alignment; GtkWidget *hbox; @@ -1278,7 +881,8 @@ create_filepopup_settings(void) gtk_box_pack_start(GTK_BOX(filepopup_settings_recurse_for_cover_depth_box), label_search_depth, TRUE, TRUE, 0); gtk_misc_set_padding(GTK_MISC(label_search_depth), 4, 0); - recurse_for_cover_depth_adj = gtk_adjustment_new(0, 0, 100, 1, 10, 0); + recurse_for_cover_depth_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, + 100, 1, 10, 0); filepopup_settings_recurse_for_cover_depth = gtk_spin_button_new(GTK_ADJUSTMENT(recurse_for_cover_depth_adj), 1, 0); gtk_box_pack_start(GTK_BOX(filepopup_settings_recurse_for_cover_depth_box), filepopup_settings_recurse_for_cover_depth, TRUE, TRUE, 0); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_settings_recurse_for_cover_depth), TRUE); @@ -1314,7 +918,7 @@ create_filepopup_settings(void) gtk_misc_set_alignment(GTK_MISC(label_delay), 0, 0.5); gtk_misc_set_padding(GTK_MISC(label_delay), 12, 0); - delay_adj = gtk_adjustment_new(0, 0, 100, 1, 10, 0); + delay_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 100, 1, 10, 0); filepopup_settings_delay = gtk_spin_button_new(GTK_ADJUSTMENT(delay_adj), 1, 0); gtk_box_pack_start(GTK_BOX(hbox), filepopup_settings_delay, TRUE, TRUE, 0); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_settings_delay), TRUE); @@ -1482,7 +1086,7 @@ static void create_label (PreferencesWidget * widget, GtkWidget * * label, static void create_cbox (PreferencesWidget * widget, GtkWidget * * label, GtkWidget * * combobox, const gchar * domain) { - *combobox = gtk_combo_box_new_text(); + * combobox = gtk_combo_box_text_new (); if (widget->label) { * label = gtk_label_new (dgettext (domain, widget->label)); @@ -1783,20 +1387,6 @@ create_titlestring_tag_menu(void) } static void -create_playback_category(void) -{ - GtkWidget *playback_page_vbox; - GtkWidget *widgets_vbox; - - playback_page_vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (category_notebook), playback_page_vbox); - - widgets_vbox = gtk_vbox_new (FALSE, 0); - create_widgets(GTK_BOX(widgets_vbox), playback_page_widgets, G_N_ELEMENTS(playback_page_widgets)); - gtk_box_pack_start (GTK_BOX (playback_page_vbox), widgets_vbox, TRUE, TRUE, 0); -} - -static void create_replay_gain_category(void) { GtkWidget *rg_page_vbox; @@ -1814,14 +1404,29 @@ static void show_numbers_cb (GtkToggleButton * numbers, void * unused) { cfg.show_numbers_in_pl = gtk_toggle_button_get_active (numbers); - hook_call ("playlist update", NULL); hook_call ("title change", NULL); + + /* trigger playlist update */ + gchar * t = g_strdup (playlist_get_title (playlist_get_active ())); + playlist_set_title (playlist_get_active (), t); + g_free (t); +} + +static void leading_zero_cb (GtkToggleButton * leading) +{ + cfg.leading_zero = gtk_toggle_button_get_active (leading); + + hook_call ("title change", NULL); + + /* trigger playlist update */ + gchar * t = g_strdup (playlist_get_title (playlist_get_active ())); + playlist_set_title (playlist_get_active (), t); + g_free (t); } static void create_playlist_category(void) { - GtkWidget *playlist_page_vbox; GtkWidget *vbox5; GtkWidget *alignment55; GtkWidget *label60; @@ -1842,11 +1447,8 @@ create_playlist_category(void) GtkWidget *titlestring_tag_menu = create_titlestring_tag_menu(); GtkWidget * numbers_alignment, * numbers; - playlist_page_vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (category_notebook), playlist_page_vbox); - vbox5 = gtk_vbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (playlist_page_vbox), vbox5, TRUE, TRUE, 0); + gtk_container_add ((GtkContainer *) category_notebook, vbox5); create_widgets(GTK_BOX(vbox5), playlist_page_widgets, G_N_ELEMENTS(playlist_page_widgets)); @@ -1870,6 +1472,17 @@ create_playlist_category(void) show_numbers_cb, 0); gtk_container_add ((GtkContainer *) numbers_alignment, numbers); + numbers_alignment = gtk_alignment_new (0, 0, 0, 0); + gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0); + gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3); + + numbers = gtk_check_button_new_with_label (_("Show leading zeroes (02:00 " + "instead of 2:00)")); + gtk_toggle_button_set_active ((GtkToggleButton *) numbers, cfg.leading_zero); + g_signal_connect ((GObject *) numbers, "toggled", (GCallback) + leading_zero_cb, 0); + gtk_container_add ((GtkContainer *) numbers_alignment, numbers); + alignment56 = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_box_pack_start (GTK_BOX (vbox5), alignment56, FALSE, FALSE, 0); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment56), 0, 0, 12, 0); @@ -1892,17 +1505,19 @@ create_playlist_category(void) image1 = gtk_image_new_from_stock ("gtk-index", GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (titlestring_help_button), image1); - titlestring_cbox = gtk_combo_box_new_text (); + titlestring_cbox = gtk_combo_box_text_new (); + gtk_table_attach (GTK_TABLE (table6), titlestring_cbox, 1, 3, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); - gtk_combo_box_append_text (GTK_COMBO_BOX (titlestring_cbox), _("TITLE")); - gtk_combo_box_append_text (GTK_COMBO_BOX (titlestring_cbox), _("ARTIST - TITLE")); - gtk_combo_box_append_text (GTK_COMBO_BOX (titlestring_cbox), _("ARTIST - ALBUM - TITLE")); - gtk_combo_box_append_text (GTK_COMBO_BOX (titlestring_cbox), _("ARTIST - ALBUM - TRACK. TITLE")); - gtk_combo_box_append_text (GTK_COMBO_BOX (titlestring_cbox), _("ARTIST [ ALBUM ] - TRACK. TITLE")); - gtk_combo_box_append_text (GTK_COMBO_BOX (titlestring_cbox), _("ALBUM - TITLE")); - gtk_combo_box_append_text (GTK_COMBO_BOX (titlestring_cbox), _("Custom")); + + gtk_combo_box_text_append_text ((GtkComboBoxText *) titlestring_cbox, _("TITLE")); + gtk_combo_box_text_append_text ((GtkComboBoxText *) titlestring_cbox, _("ARTIST - TITLE")); + gtk_combo_box_text_append_text ((GtkComboBoxText *) titlestring_cbox, _("ARTIST - ALBUM - TITLE")); + gtk_combo_box_text_append_text ((GtkComboBoxText *) titlestring_cbox, _("ARTIST - ALBUM - TRACK. TITLE")); + gtk_combo_box_text_append_text ((GtkComboBoxText *) titlestring_cbox, _("ARTIST [ ALBUM ] - TRACK. TITLE")); + gtk_combo_box_text_append_text ((GtkComboBoxText *) titlestring_cbox, _("ALBUM - TITLE")); + gtk_combo_box_text_append_text ((GtkComboBoxText *) titlestring_cbox, _("Custom")); titlestring_entry = gtk_entry_new (); gtk_table_attach (GTK_TABLE (table6), titlestring_entry, 1, 2, 1, 2, @@ -1995,6 +1610,68 @@ create_playlist_category(void) create_filepopup_settings(); } +static GtkWidget * output_config_button, * output_about_button; + +static gboolean output_enum_cb (PluginHandle * plugin, GList * * list) +{ + * list = g_list_prepend (* list, plugin); + return TRUE; +} + +static GList * output_get_list (void) +{ + static GList * list = NULL; + + if (list == NULL) + { + plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) output_enum_cb, + & list); + list = g_list_reverse (list); + } + + return list; +} + +static void output_combo_update (GtkComboBox * combo) +{ + PluginHandle * plugin = plugin_get_current (PLUGIN_TYPE_OUTPUT); + gtk_combo_box_set_active (combo, g_list_index (output_get_list (), plugin)); + gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin)); + gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin)); +} + +static void output_combo_changed (GtkComboBox * combo) +{ + PluginHandle * plugin = g_list_nth_data (output_get_list (), + gtk_combo_box_get_active (combo)); + g_return_if_fail (plugin != NULL); + + plugin_enable (plugin, TRUE); + output_combo_update (combo); +} + +static void output_combo_fill (GtkComboBox * combo) +{ + for (GList * node = output_get_list (); node != NULL; node = node->next) + gtk_combo_box_text_append_text ((GtkComboBoxText *) combo, + plugin_get_name (node->data)); +} + +static void output_do_config (void) +{ + OutputPlugin * op = plugin_get_header (output_plugin_get_current ()); + g_return_if_fail (op != NULL); + if (op->configure != NULL) + op->configure (); +} + +static void output_do_about (void) +{ + OutputPlugin * op = plugin_get_header (output_plugin_get_current ()); + g_return_if_fail (op != NULL); + if (op->about != NULL) + op->about (); +} static void create_audio_category(void) @@ -2006,22 +1683,10 @@ create_audio_category(void) GtkWidget *vbox33; GtkWidget *table11; GtkWidget *label79; - GtkObject *output_plugin_bufsize_adj; + GtkAdjustment * output_plugin_bufsize_adj; GtkWidget *output_plugin_bufsize; GtkWidget *output_plugin_cbox; GtkWidget *label78; - GtkWidget *alignment82; - GtkWidget *output_plugin_button_box; - GtkWidget *output_plugin_prefs; - GtkWidget *alignment76; - GtkWidget *hbox7; - GtkWidget *image5; - GtkWidget *label80; - GtkWidget *output_plugin_info; - GtkWidget *alignment77; - GtkWidget *hbox8; - GtkWidget *image6; - GtkWidget *label81; audio_page_vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (category_notebook), audio_page_vbox); @@ -2053,14 +1718,15 @@ create_audio_category(void) (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label79), 1, 0.5); - output_plugin_bufsize_adj = - gtk_adjustment_new (0, 100, 10000, 100, 1000, 0); + output_plugin_bufsize_adj = (GtkAdjustment *) gtk_adjustment_new (0, 100, + 10000, 100, 1000, 0); output_plugin_bufsize = gtk_spin_button_new (GTK_ADJUSTMENT (output_plugin_bufsize_adj), 100, 0); gtk_table_attach (GTK_TABLE (table11), output_plugin_bufsize, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); - output_plugin_cbox = gtk_combo_box_new_text (); + output_plugin_cbox = gtk_combo_box_text_new (); + gtk_table_attach (GTK_TABLE (table11), output_plugin_cbox, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); @@ -2071,77 +1737,32 @@ create_audio_category(void) (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label78), 0, 0.5); - alignment82 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start (GTK_BOX (audio_page_vbox), alignment82, FALSE, FALSE, 0); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment82), 0, 0, 12, 0); - - output_plugin_button_box = gtk_hbutton_box_new (); - gtk_container_add (GTK_CONTAINER (alignment82), output_plugin_button_box); - gtk_button_box_set_layout (GTK_BUTTON_BOX (output_plugin_button_box), GTK_BUTTONBOX_START); - gtk_box_set_spacing (GTK_BOX (output_plugin_button_box), 8); - - output_plugin_prefs = gtk_button_new (); - gtk_container_add (GTK_CONTAINER (output_plugin_button_box), output_plugin_prefs); - gtk_widget_set_sensitive (output_plugin_prefs, FALSE); - gtk_widget_set_can_default(output_plugin_prefs, TRUE); - - alignment76 = gtk_alignment_new (0.5, 0.5, 0, 0); - gtk_container_add (GTK_CONTAINER (output_plugin_prefs), alignment76); + GtkWidget * hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start ((GtkBox *) audio_page_vbox, hbox, FALSE, FALSE, 0); - hbox7 = gtk_hbox_new (FALSE, 2); - gtk_container_add (GTK_CONTAINER (alignment76), hbox7); + output_config_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES); + output_about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT); - image5 = gtk_image_new_from_stock ("gtk-preferences", GTK_ICON_SIZE_BUTTON); - gtk_box_pack_start (GTK_BOX (hbox7), image5, FALSE, FALSE, 0); - - label80 = gtk_label_new_with_mnemonic (_("Output Plugin Preferences")); - gtk_box_pack_start (GTK_BOX (hbox7), label80, FALSE, FALSE, 0); - - output_plugin_info = gtk_button_new (); - gtk_container_add (GTK_CONTAINER (output_plugin_button_box), output_plugin_info); - gtk_widget_set_sensitive (output_plugin_info, FALSE); - gtk_widget_set_can_default(output_plugin_info, TRUE); - - alignment77 = gtk_alignment_new (0.5, 0.5, 0, 0); - gtk_container_add (GTK_CONTAINER (output_plugin_info), alignment77); - - hbox8 = gtk_hbox_new (FALSE, 2); - gtk_container_add (GTK_CONTAINER (alignment77), hbox8); - - image6 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_BUTTON); - gtk_box_pack_start (GTK_BOX (hbox8), image6, FALSE, FALSE, 0); - - label81 = gtk_label_new_with_mnemonic (_("Output Plugin Information")); - gtk_box_pack_start (GTK_BOX (hbox8), label81, FALSE, FALSE, 0); + gtk_box_pack_end ((GtkBox *) hbox, output_about_button, FALSE, FALSE, 0); + gtk_box_pack_end ((GtkBox *) hbox, output_config_button, FALSE, FALSE, 0); create_widgets(GTK_BOX(audio_page_vbox), audio_page_widgets, G_N_ELEMENTS(audio_page_widgets)); + output_combo_fill ((GtkComboBox *) output_plugin_cbox); + output_combo_update ((GtkComboBox *) output_plugin_cbox); + g_signal_connect (output_plugin_cbox, "changed", (GCallback) + output_combo_changed, NULL); + g_signal_connect (output_config_button, "clicked", (GCallback) + output_do_config, NULL); + g_signal_connect (output_about_button, "clicked", (GCallback) + output_do_about, NULL); + g_signal_connect(G_OBJECT(output_plugin_bufsize), "value_changed", G_CALLBACK(on_output_plugin_bufsize_value_changed), NULL); g_signal_connect_after(G_OBJECT(output_plugin_bufsize), "realize", G_CALLBACK(on_output_plugin_bufsize_realize), NULL); - g_signal_connect_after(G_OBJECT(output_plugin_cbox), "realize", - G_CALLBACK(on_output_plugin_cbox_realize), - NULL); - - /* plugin->output page */ - - g_signal_connect(G_OBJECT(output_plugin_cbox), "changed", - G_CALLBACK(output_plugin_enable_prefs), - output_plugin_prefs); - g_signal_connect_swapped(G_OBJECT(output_plugin_prefs), "clicked", - G_CALLBACK(output_plugin_open_prefs), - output_plugin_cbox); - - g_signal_connect(G_OBJECT(output_plugin_cbox), "changed", - G_CALLBACK(output_plugin_enable_info), - output_plugin_info); - g_signal_connect_swapped(G_OBJECT(output_plugin_info), "clicked", - G_CALLBACK(output_plugin_open_info), - output_plugin_cbox); - } static void @@ -2159,361 +1780,24 @@ create_connectivity_category(void) create_widgets(GTK_BOX(vbox29), connectivity_page_widgets, G_N_ELEMENTS(connectivity_page_widgets)); } -static void -create_plugin_category(void) -{ - GtkWidget *plugin_page_vbox; - GtkWidget *plugin_notebook; - GtkWidget *plugin_input_vbox; - GtkWidget *alignment43; - GtkWidget *input_plugin_list_label; - GtkWidget *scrolledwindow3; - GtkWidget *input_plugin_view; - GtkWidget *input_plugin_button_box; - GtkWidget *input_plugin_prefs; - GtkWidget *input_plugin_info; - GtkWidget *plugin_input_label; - GtkWidget *plugin_general_vbox; - GtkWidget *alignment45; - GtkWidget *label11; - GtkWidget *scrolledwindow5; - GtkWidget *general_plugin_view; - GtkWidget *general_plugin_button_box; - GtkWidget *general_plugin_prefs; - GtkWidget *general_plugin_info; - GtkWidget *plugin_general_label; - GtkWidget *vbox21; - GtkWidget *alignment46; - GtkWidget *label53; - GtkWidget *scrolledwindow7; - GtkWidget *vis_plugin_view; - GtkWidget *hbuttonbox6; - GtkWidget *vis_plugin_prefs; - GtkWidget *vis_plugin_info; - GtkWidget *vis_label; - GtkWidget *vbox25; - GtkWidget *alignment58; - GtkWidget *label64; - GtkWidget *scrolledwindow9; - GtkWidget *effect_plugin_view; - GtkWidget *hbuttonbox9; - GtkWidget *effect_plugin_prefs; - GtkWidget *effect_plugin_info; - GtkWidget *effects_label; - - plugin_page_vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (category_notebook), plugin_page_vbox); - - plugin_notebook = gtk_notebook_new (); - gtk_box_pack_start (GTK_BOX (plugin_page_vbox), plugin_notebook, TRUE, TRUE, 0); - gtk_notebook_set_show_border (GTK_NOTEBOOK (plugin_notebook), FALSE); - - plugin_input_vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (plugin_notebook), plugin_input_vbox); - gtk_container_set_border_width (GTK_CONTAINER (plugin_input_vbox), 12); - - alignment43 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start (GTK_BOX (plugin_input_vbox), alignment43, FALSE, FALSE, 4); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment43), 0, 6, 0, 0); - - input_plugin_list_label = gtk_label_new_with_mnemonic (_("_Decoder list:")); - gtk_container_add (GTK_CONTAINER (alignment43), input_plugin_list_label); - gtk_label_set_use_markup (GTK_LABEL (input_plugin_list_label), TRUE); - gtk_misc_set_alignment (GTK_MISC (input_plugin_list_label), 0, 0.5); - - scrolledwindow3 = gtk_scrolled_window_new (NULL, NULL); - gtk_box_pack_start (GTK_BOX (plugin_input_vbox), scrolledwindow3, TRUE, TRUE, 0); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow3), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow3), GTK_SHADOW_IN); - - input_plugin_view = gtk_tree_view_new (); - gtk_container_add (GTK_CONTAINER (scrolledwindow3), input_plugin_view); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (input_plugin_view), TRUE); - gtk_tree_view_set_reorderable (GTK_TREE_VIEW (input_plugin_view), TRUE); - - input_plugin_button_box = gtk_hbutton_box_new (); - gtk_box_pack_start (GTK_BOX (plugin_input_vbox), input_plugin_button_box, FALSE, FALSE, 8); - gtk_button_box_set_layout (GTK_BUTTON_BOX (input_plugin_button_box), GTK_BUTTONBOX_START); - gtk_box_set_spacing (GTK_BOX (input_plugin_button_box), 8); - - input_plugin_prefs = gtk_button_new_from_stock ("gtk-preferences"); - gtk_container_add (GTK_CONTAINER (input_plugin_button_box), input_plugin_prefs); - gtk_widget_set_sensitive (input_plugin_prefs, FALSE); - gtk_widget_set_can_default(input_plugin_prefs, TRUE); - - input_plugin_info = gtk_button_new_from_stock ("gtk-dialog-info"); - gtk_container_add (GTK_CONTAINER (input_plugin_button_box), input_plugin_info); - gtk_widget_set_sensitive (input_plugin_info, FALSE); - gtk_widget_set_can_default(input_plugin_info, TRUE); - - plugin_input_label = gtk_label_new (_("<span size=\"medium\"><b>Decoders</b></span>")); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (plugin_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (plugin_notebook), 0), plugin_input_label); - gtk_label_set_use_markup (GTK_LABEL (plugin_input_label), TRUE); - gtk_misc_set_alignment (GTK_MISC (plugin_input_label), 0, 0); - - plugin_general_vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (plugin_notebook), plugin_general_vbox); - gtk_container_set_border_width (GTK_CONTAINER (plugin_general_vbox), 12); - - alignment45 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start (GTK_BOX (plugin_general_vbox), alignment45, FALSE, FALSE, 4); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment45), 0, 6, 0, 0); - - label11 = gtk_label_new_with_mnemonic (_("_General plugin list:")); - gtk_container_add (GTK_CONTAINER (alignment45), label11); - gtk_label_set_use_markup (GTK_LABEL (label11), TRUE); - gtk_misc_set_alignment (GTK_MISC (label11), 0, 0.5); - - scrolledwindow5 = gtk_scrolled_window_new (NULL, NULL); - gtk_box_pack_start (GTK_BOX (plugin_general_vbox), scrolledwindow5, TRUE, TRUE, 0); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow5), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow5), GTK_SHADOW_IN); - - general_plugin_view = gtk_tree_view_new (); - gtk_container_add (GTK_CONTAINER (scrolledwindow5), general_plugin_view); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (general_plugin_view), TRUE); - gtk_tree_view_set_reorderable (GTK_TREE_VIEW (general_plugin_view), TRUE); - - general_plugin_button_box = gtk_hbutton_box_new (); - gtk_box_pack_start (GTK_BOX (plugin_general_vbox), general_plugin_button_box, FALSE, FALSE, 8); - gtk_button_box_set_layout (GTK_BUTTON_BOX (general_plugin_button_box), GTK_BUTTONBOX_START); - gtk_box_set_spacing (GTK_BOX (general_plugin_button_box), 8); - - general_plugin_prefs = gtk_button_new_from_stock ("gtk-preferences"); - gtk_container_add (GTK_CONTAINER (general_plugin_button_box), general_plugin_prefs); - gtk_widget_set_sensitive (general_plugin_prefs, FALSE); - gtk_widget_set_can_default(general_plugin_prefs, TRUE); - - general_plugin_info = gtk_button_new_from_stock ("gtk-dialog-info"); - gtk_container_add (GTK_CONTAINER (general_plugin_button_box), general_plugin_info); - gtk_widget_set_sensitive (general_plugin_info, FALSE); - gtk_widget_set_can_default(general_plugin_info, TRUE); - - plugin_general_label = gtk_label_new (_("<span size=\"medium\"><b>General</b></span>")); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (plugin_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (plugin_notebook), 1), plugin_general_label); - gtk_label_set_use_markup (GTK_LABEL (plugin_general_label), TRUE); - - vbox21 = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (plugin_notebook), vbox21); - gtk_container_set_border_width (GTK_CONTAINER (vbox21), 12); - - alignment46 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start (GTK_BOX (vbox21), alignment46, FALSE, FALSE, 4); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment46), 0, 6, 0, 0); - - label53 = gtk_label_new_with_mnemonic (_("_Visualization plugin list:")); - gtk_container_add (GTK_CONTAINER (alignment46), label53); - gtk_label_set_use_markup (GTK_LABEL (label53), TRUE); - gtk_misc_set_alignment (GTK_MISC (label53), 0, 0.5); - - scrolledwindow7 = gtk_scrolled_window_new (NULL, NULL); - gtk_box_pack_start (GTK_BOX (vbox21), scrolledwindow7, TRUE, TRUE, 0); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow7), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow7), GTK_SHADOW_IN); - - vis_plugin_view = gtk_tree_view_new (); - gtk_container_add (GTK_CONTAINER (scrolledwindow7), vis_plugin_view); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (vis_plugin_view), TRUE); - gtk_tree_view_set_reorderable (GTK_TREE_VIEW (vis_plugin_view), TRUE); - - hbuttonbox6 = gtk_hbutton_box_new (); - gtk_box_pack_start (GTK_BOX (vbox21), hbuttonbox6, FALSE, FALSE, 8); - gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox6), GTK_BUTTONBOX_START); - gtk_box_set_spacing (GTK_BOX (hbuttonbox6), 8); - - vis_plugin_prefs = gtk_button_new_from_stock ("gtk-preferences"); - gtk_container_add (GTK_CONTAINER (hbuttonbox6), vis_plugin_prefs); - gtk_widget_set_sensitive (vis_plugin_prefs, FALSE); - gtk_widget_set_can_default(vis_plugin_prefs, TRUE); - - vis_plugin_info = gtk_button_new_from_stock ("gtk-dialog-info"); - gtk_container_add (GTK_CONTAINER (hbuttonbox6), vis_plugin_info); - gtk_widget_set_sensitive (vis_plugin_info, FALSE); - gtk_widget_set_can_default(vis_plugin_info, TRUE); - - vis_label = gtk_label_new (_("<b>Visualization</b>")); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (plugin_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (plugin_notebook), 2), vis_label); - gtk_label_set_use_markup (GTK_LABEL (vis_label), TRUE); - - vbox25 = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (plugin_notebook), vbox25); - gtk_container_set_border_width (GTK_CONTAINER (vbox25), 12); - - alignment58 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start (GTK_BOX (vbox25), alignment58, FALSE, FALSE, 4); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment58), 0, 6, 0, 0); - - label64 = gtk_label_new (_("Effect plugins:")); - gtk_container_add (GTK_CONTAINER (alignment58), label64); - gtk_misc_set_alignment (GTK_MISC (label64), 0, 0.5); - - scrolledwindow9 = gtk_scrolled_window_new (NULL, NULL); - gtk_box_pack_start (GTK_BOX (vbox25), scrolledwindow9, TRUE, TRUE, 0); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow9), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow9), GTK_SHADOW_IN); - - effect_plugin_view = gtk_tree_view_new (); - gtk_container_add (GTK_CONTAINER (scrolledwindow9), effect_plugin_view); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (effect_plugin_view), TRUE); - gtk_tree_view_set_reorderable (GTK_TREE_VIEW (effect_plugin_view), TRUE); - - hbuttonbox9 = gtk_hbutton_box_new (); - gtk_box_pack_start (GTK_BOX (vbox25), hbuttonbox9, FALSE, FALSE, 8); - gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox9), GTK_BUTTONBOX_START); - gtk_box_set_spacing (GTK_BOX (hbuttonbox9), 8); - - effect_plugin_prefs = gtk_button_new_from_stock ("gtk-preferences"); - gtk_container_add (GTK_CONTAINER (hbuttonbox9), effect_plugin_prefs); - gtk_widget_set_sensitive (effect_plugin_prefs, FALSE); - gtk_widget_set_can_default(effect_plugin_prefs, TRUE); - - effect_plugin_info = gtk_button_new_from_stock ("gtk-dialog-info"); - gtk_container_add (GTK_CONTAINER (hbuttonbox9), effect_plugin_info); - gtk_widget_set_sensitive (effect_plugin_info, FALSE); - gtk_widget_set_can_default(effect_plugin_info, TRUE); - - effects_label = gtk_label_new (_("<b>Effects</b>")); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (plugin_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (plugin_notebook), 3), effects_label); - gtk_label_set_use_markup (GTK_LABEL (effects_label), TRUE); - - - - gtk_label_set_mnemonic_widget (GTK_LABEL (input_plugin_list_label), category_notebook); - gtk_label_set_mnemonic_widget (GTK_LABEL (label11), category_notebook); - gtk_label_set_mnemonic_widget (GTK_LABEL (label53), category_notebook); - gtk_label_set_mnemonic_widget (GTK_LABEL (label64), category_notebook); - - - - g_signal_connect_after(G_OBJECT(input_plugin_view), "realize", - G_CALLBACK(on_input_plugin_view_realize), - NULL); - g_signal_connect_after(G_OBJECT(general_plugin_view), "realize", - G_CALLBACK(on_general_plugin_view_realize), - NULL); - g_signal_connect_after(G_OBJECT(vis_plugin_view), "realize", - G_CALLBACK(on_vis_plugin_view_realize), - NULL); - g_signal_connect_after(G_OBJECT(effect_plugin_view), "realize", - G_CALLBACK(on_effect_plugin_view_realize), - NULL); - - - - /* plugin->input page */ - g_object_set_data(G_OBJECT(input_plugin_view), "plugin_type" , GINT_TO_POINTER(PLUGIN_VIEW_TYPE_INPUT)); - g_signal_connect(G_OBJECT(input_plugin_view), "row-activated", - G_CALLBACK(plugin_treeview_open_prefs), - NULL); - g_signal_connect(G_OBJECT(input_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_prefs), - input_plugin_prefs); - - g_signal_connect_swapped(G_OBJECT(input_plugin_prefs), "clicked", - G_CALLBACK(plugin_treeview_open_prefs), - input_plugin_view); - - g_signal_connect(G_OBJECT(input_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_info), - input_plugin_info); - g_signal_connect_swapped(G_OBJECT(input_plugin_info), "clicked", - G_CALLBACK(plugin_treeview_open_info), - input_plugin_view); - - - /* plugin->general page */ - - g_object_set_data(G_OBJECT(general_plugin_view), "plugin_type" , GINT_TO_POINTER(PLUGIN_VIEW_TYPE_GENERAL)); - g_signal_connect(G_OBJECT(general_plugin_view), "row-activated", - G_CALLBACK(plugin_treeview_open_prefs), - NULL); - - g_signal_connect(G_OBJECT(general_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_prefs), - general_plugin_prefs); - - g_signal_connect_swapped(G_OBJECT(general_plugin_prefs), "clicked", - G_CALLBACK(plugin_treeview_open_prefs), - general_plugin_view); - - g_signal_connect(G_OBJECT(general_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_info), - general_plugin_info); - g_signal_connect_swapped(G_OBJECT(general_plugin_info), "clicked", - G_CALLBACK(plugin_treeview_open_info), - general_plugin_view); - - - /* plugin->vis page */ - - g_object_set_data(G_OBJECT(vis_plugin_view), "plugin_type" , GINT_TO_POINTER(PLUGIN_VIEW_TYPE_VIS)); - g_signal_connect(G_OBJECT(vis_plugin_view), "row-activated", - G_CALLBACK(plugin_treeview_open_prefs), - NULL); - g_signal_connect_swapped(G_OBJECT(vis_plugin_prefs), "clicked", - G_CALLBACK(plugin_treeview_open_prefs), - vis_plugin_view); - g_signal_connect(G_OBJECT(vis_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_prefs), vis_plugin_prefs); - - g_signal_connect(G_OBJECT(vis_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_info), vis_plugin_info); - g_signal_connect_swapped(G_OBJECT(vis_plugin_info), "clicked", - G_CALLBACK(plugin_treeview_open_info), - vis_plugin_view); - - - /* plugin->effects page */ - - g_object_set_data(G_OBJECT(effect_plugin_view), "plugin_type" , GINT_TO_POINTER(PLUGIN_VIEW_TYPE_EFFECT)); - g_signal_connect(G_OBJECT(effect_plugin_view), "row-activated", - G_CALLBACK(plugin_treeview_open_prefs), - NULL); - g_signal_connect_swapped(G_OBJECT(effect_plugin_prefs), "clicked", - G_CALLBACK(plugin_treeview_open_prefs), - effect_plugin_view); - g_signal_connect(G_OBJECT(effect_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_prefs), effect_plugin_prefs); - - g_signal_connect(G_OBJECT(effect_plugin_view), "cursor-changed", - G_CALLBACK(plugin_treeview_enable_info), effect_plugin_info); - g_signal_connect_swapped(G_OBJECT(effect_plugin_info), "clicked", - G_CALLBACK(plugin_treeview_open_info), - effect_plugin_view); - -} - -static void -destroy_plugin_page(GList *list) +static void create_plugin_category (void) { - GList *iter; + GtkWidget * notebook = gtk_notebook_new (); + gtk_container_add ((GtkContainer *) category_notebook, notebook); - MOWGLI_ITER_FOREACH(iter, list) - { - Plugin *plugin = PLUGIN(iter->data); - if (plugin->settings && plugin->settings->data) { - plugin->settings->data = NULL; - if (plugin->settings->apply) - plugin->settings->apply(); - if (plugin->settings->cleanup) - plugin->settings->cleanup(); - } - } -} + gint types[] = {PLUGIN_TYPE_TRANSPORT, PLUGIN_TYPE_PLAYLIST, + PLUGIN_TYPE_INPUT, PLUGIN_TYPE_EFFECT, PLUGIN_TYPE_VIS, PLUGIN_TYPE_GENERAL}; + const gchar * names[] = {N_("Transport"), N_("Playlist"), N_("Input"), + N_("Effect"), N_("Visualization"), N_("General")}; -static void -destroy_plugin_pages(void) -{ - destroy_plugin_page(plugin_get_list(PLUGIN_TYPE_INPUT)); - destroy_plugin_page(plugin_get_list(PLUGIN_TYPE_GENERAL)); - destroy_plugin_page(plugin_get_list(PLUGIN_TYPE_VIS)); - destroy_plugin_page(plugin_get_list(PLUGIN_TYPE_EFFECT)); + for (gint i = 0; i < G_N_ELEMENTS (types); i ++) + gtk_notebook_append_page ((GtkNotebook *) notebook, plugin_view_new + (types[i]), gtk_label_new (_(names[i]))); } static gboolean prefswin_destroy(GtkWidget *window, GdkEvent *event, gpointer data) { - destroy_plugin_pages(); prefswin = NULL; category_notebook = NULL; gtk_widget_destroy(filepopup_settings); @@ -2522,29 +1806,6 @@ prefswin_destroy(GtkWidget *window, GdkEvent *event, gpointer data) return TRUE; } -static void -create_plugin_page(GList *list) -{ - GList *iter; - - MOWGLI_ITER_FOREACH(iter, list) - { - Plugin *plugin = PLUGIN(iter->data); - if (plugin->settings && plugin->settings->type == PREFERENCES_PAGE) { - create_plugin_preferences_page(plugin->settings); - } - } -} - -static void -create_plugin_pages(void) -{ - create_plugin_page(plugin_get_list(PLUGIN_TYPE_INPUT)); - create_plugin_page(plugin_get_list(PLUGIN_TYPE_GENERAL)); - create_plugin_page(plugin_get_list(PLUGIN_TYPE_VIS)); - create_plugin_page(plugin_get_list(PLUGIN_TYPE_EFFECT)); -} - /* GtkWidget * * create_prefs_window (void) */ void * * create_prefs_window (void) { @@ -2600,10 +1861,8 @@ void * * create_prefs_window (void) create_audio_category(); create_replay_gain_category(); create_connectivity_category(); - create_playback_category(); create_playlist_category(); create_plugin_category(); - create_plugin_pages(); hseparator1 = gtk_hseparator_new (); gtk_box_pack_start (GTK_BOX (vbox), hseparator1, FALSE, FALSE, 6); @@ -2641,7 +1900,7 @@ void * * create_prefs_window (void) NULL); g_signal_connect_swapped(G_OBJECT(close), "clicked", G_CALLBACK(prefswin_destroy), - GTK_OBJECT (prefswin)); + prefswin); /* create category view */ on_category_treeview_realize ((GtkTreeView *) category_treeview, @@ -2667,42 +1926,6 @@ destroy_prefs_window(void) } void -create_plugin_preferences_page(PluginPreferences *settings) -{ - g_return_if_fail(settings->type == PREFERENCES_PAGE); - - if (settings->data != NULL) - return; - - if (settings->init) - settings->init(); - - GtkWidget *vbox; - vbox = gtk_vbox_new(FALSE, 5); - - create_widgets(GTK_BOX(vbox), settings->prefs, settings->n_prefs); - gtk_widget_show_all(vbox); - prefswin_page_new(vbox, settings->title, settings->imgurl); - - settings->data = (gpointer) vbox; -} - -void -destroy_plugin_preferences_page(PluginPreferences *settings) -{ - if (settings->data) { - if (settings->apply) - settings->apply(); - - prefswin_page_destroy(GTK_WIDGET(settings->data)); - settings->data = NULL; - - if (settings->cleanup) - settings->cleanup(); - } -} - -void show_prefs_window(void) { gtk_window_present(GTK_WINDOW(prefswin)); /* show or raise prefs window */ @@ -2853,7 +2076,7 @@ static void sw_volume_toggled (void) vol[1] = cfg.sw_volume_right; } else - input_get_volume (& vol[0], & vol[1]); + playback_get_volume (& vol[0], & vol[1]); hook_call ("volume set", vol); } diff --git a/src/audacious/ui_preferences.h b/src/audacious/ui_preferences.h index 38fd677..c41a62f 100644 --- a/src/audacious/ui_preferences.h +++ b/src/audacious/ui_preferences.h @@ -21,6 +21,9 @@ #define AUDACIOUS_UI_PREFERENCES_H #include <glib.h> +#include <gtk/gtk.h> + +#include "preferences.h" /* GtkWidget * * create_prefs_window (void); */ void * * create_prefs_window (void); @@ -34,4 +37,10 @@ gint prefswin_page_new (void * container, const gchar * name, const gchar * imgurl); void prefswin_page_destroy(GtkWidget *container); +void plugin_preferences_show (PluginPreferences * p); +void plugin_preferences_cleanup (PluginPreferences * p); + +/* plugin-view.c */ +GtkWidget * plugin_view_new (gint type); + #endif /* AUDACIOUS_UI_PREFERENCES_H */ diff --git a/src/audacious/util.c b/src/audacious/util.c index 750dfd1..2c2445b 100644 --- a/src/audacious/util.c +++ b/src/audacious/util.c @@ -1,5 +1,5 @@ /* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2008 Audacious development team + * Copyright (C) 2005-2011 Audacious development team * * Based on BMP: * Copyright (C) 2003-2004 BMP development team. @@ -23,11 +23,17 @@ * Audacious or using our public API to be a derived work. */ +#include <limits.h> +#include <unistd.h> + +#ifdef _WIN32 +#include <windows.h> +#endif + #ifdef HAVE_CONFIG_H # include "config.h" #endif - #include <glib.h> #include <stdlib.h> #include <string.h> @@ -42,6 +48,7 @@ #endif #include <libaudcore/audstrings.h> +#include <libaudcore/stringpool.h> #include "audconfig.h" #include "debug.h" @@ -106,7 +113,6 @@ util_get_localdir(void) gchar * construct_uri (const gchar * string, const gchar * playlist_name) { gchar *filename = g_strdup(string); - gchar *tmp, *path; gchar *uri = NULL; /* try to translate dos path */ @@ -118,28 +124,20 @@ gchar * construct_uri (const gchar * string, const gchar * playlist_name) 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); - uri = NULL; + // case 2: filename is not raw full path nor uri + // make full path by replacing last part of playlist path with filename. + else + { + const gchar * fslash = strrchr (filename, '/'); + const gchar * pslash = strrchr (playlist_name, '/'); + + if (pslash) + uri = g_strdup_printf ("%.*s/%s", (gint) (pslash - playlist_name), + playlist_name, fslash ? fslash + 1 : filename); } - AUDDBG("uri=%s\n", uri); + g_free (filename); return uri; } @@ -164,6 +162,28 @@ make_directory(const gchar * path, mode_t mode) g_strerror(errno)); } +gchar * get_path_to_self (void) +{ + gchar buf[PATH_MAX]; + gint len; + +#ifdef _WIN32 + if (! (len = GetModuleFileName (NULL, buf, sizeof buf)) || len == sizeof buf) + { + fprintf (stderr, "GetModuleFileName failed.\n"); + return NULL; + } +#else + if ((len = readlink ("/proc/self/exe", buf, sizeof buf)) < 0) + { + fprintf (stderr, "Cannot access /proc/self/exe: %s.\n", strerror (errno)); + return NULL; + } +#endif + + return g_strndup (buf, len); +} + #define URL_HISTORY_MAX_SIZE 30 void @@ -181,24 +201,186 @@ util_add_url_history_entry(const gchar * url) } } -static gboolean plugin_list_func (PluginHandle * plugin, GList * * list) +/* Strips various common top-level folders from a file name (not URI). The + * string passed will not be modified, but the string returned will share the + * same memory. Examples: + * "/home/john/folder/file.mp3" -> "folder/file.mp3" + * "/folder/file.mp3" -> "folder/file.mp3" + * "C:\Users\John\folder\file.mp3" -> "folder\file.mp3" + * "E:\folder\file.mp3" -> "folder\file.mp3" */ + +static gchar * skip_top_folders (gchar * name) { - gpointer p_hdr = plugin_get_header(plugin); - g_return_val_if_fail(p_hdr != NULL, TRUE); - *list = g_list_prepend (*list, p_hdr); - return TRUE; + const gchar * home = getenv ("HOME"); + if (! home) + goto NO_HOME; + + gint len = strlen (home); + if (len > 0 && home[len - 1] == G_DIR_SEPARATOR) + len --; + +#ifdef _WIN32 + if (! strncasecmp (name, home, len) && name[len] == '\\') +#else + if (! strncmp (name, home, len) && name[len] == '/') +#endif + return name + len + 1; + +NO_HOME: +#ifdef _WIN32 + return (name[0] && name[1] == ':' && name[2] == '\\') ? name + 3 : name; +#else + return (name[0] == '/') ? name + 1 : name; +#endif } -/* Deprecated: This loads all the plugins at once, causing a major slowdown. */ -GList * plugin_get_list (gint type) +/* Divides a file name (not URI) into the base name, the lowest folder, and the + * second lowest folder. The string passed will be modified, and the strings + * returned will use the same memory. May return NULL for <first> and <second>. + * Examples: + * "a/b/c/d/e.mp3" -> "e", "d", "c" + * "d/e.mp3" -> "e", "d", NULL + * "e.mp3" -> "e", NULL, NULL */ + +static void split_filename (gchar * name, gchar * * base, gchar * * first, + gchar * * second) { - static GList *list[PLUGIN_TYPES] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + * first = * second = NULL; - if (list[type] == NULL) + gchar * c; + + if ((c = strrchr (name, G_DIR_SEPARATOR))) { - plugin_for_each (type, (PluginForEachFunc) plugin_list_func, & list[type]); - list[type] = g_list_reverse (list[type]); + * base = c + 1; + * c = 0; } + else + { + * base = name; + goto DONE; + } + + if ((c = strrchr (name, G_DIR_SEPARATOR))) + { + * first = c + 1; + * c = 0; + } + else + { + * first = name; + goto DONE; + } + + if ((c = strrchr (name, G_DIR_SEPARATOR))) + * second = c + 1; + else + * second = name; + +DONE: + if ((c = strrchr (* base, '.'))) + * c = 0; +} + +/* Separates the domain name from an internet URI. The string passed will be + * modified, and the string returned will share the same memory. May return + * NULL. Examples: + * "http://some.domain.org/folder/file.mp3" -> "some.domain.org" + * "http://some.stream.fm:8000" -> "some.stream.fm" */ + +static gchar * stream_name (gchar * name) +{ + if (! strncmp (name, "http://", 7)) + name += 7; + else if (! strncmp (name, "https://", 8)) + name += 8; + else if (! strncmp (name, "mms://", 6)) + name += 6; + else + return NULL; + + gchar * c; + + if ((c = strchr (name, '/'))) + * c = 0; + if ((c = strchr (name, ':'))) + * c = 0; + if ((c = strchr (name, '?'))) + * c = 0; + + return name; +} + +/* Derives best guesses of title, artist, and album from a file name (URI) and + * tuple. The returned strings are stringpooled or NULL. */ + +void describe_song (const gchar * name, const Tuple * tuple, gchar * * _title, + gchar * * _artist, gchar * * _album) +{ + /* Common folder names to skip */ + static const gchar * const skip[] = {"music"}; + + const gchar * title = tuple_get_string (tuple, FIELD_TITLE, NULL); + const gchar * artist = tuple_get_string (tuple, FIELD_ARTIST, NULL); + const gchar * album = tuple_get_string (tuple, FIELD_ALBUM, NULL); + + if (title && ! title[0]) + title = NULL; + if (artist && ! artist[0]) + artist = NULL; + if (album && ! album[0]) + album = NULL; + + gchar * copy = NULL; + + if (title && artist && album) + goto DONE; + + copy = uri_to_display (name); + + if (! strncmp (name, "file://", 7)) + { + gchar * base, * first, * second; + split_filename (skip_top_folders (copy), & base, & first, + & second); + + if (! title) + title = base; + + for (gint i = 0; i < G_N_ELEMENTS (skip); i ++) + { + if (first && ! strcasecmp (first, skip[i])) + first = NULL; + if (second && ! strcasecmp (second, skip[i])) + second = NULL; + } + + if (first) + { + if (second && ! artist && ! album) + { + artist = second; + album = first; + } + else if (! artist) + artist = first; + else if (! album) + album = first; + } + } + else + { + if (! title) + title = stream_name (copy); + else if (! artist) + artist = stream_name (copy); + else if (! album) + album = stream_name (copy); + } + +DONE: + * _title = title ? stringpool_get ((gchar *) title, FALSE) : NULL; + * _artist = artist ? stringpool_get ((gchar *) artist, FALSE) : NULL; + * _album = album ? stringpool_get ((gchar *) album, FALSE) : NULL; - return list[type]; + g_free (copy); } diff --git a/src/audacious/util.h b/src/audacious/util.h index 89a54f2..f5edf24 100644 --- a/src/audacious/util.h +++ b/src/audacious/util.h @@ -1,5 +1,5 @@ /* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2008 Audacious development team + * Copyright (C) 2005-2011 Audacious development team * * Based on BMP: * Copyright (C) 2003-2004 BMP development team @@ -39,6 +39,9 @@ gboolean dir_foreach(const gchar * path, DirForeachFunc function, gint file_get_mtime (const gchar * filename); void make_directory(const gchar * path, mode_t mode); -GList * plugin_get_list (gint type); +gchar * get_path_to_self (void); + +void describe_song (const gchar * filename, const Tuple * tuple, + gchar * * title, gchar * * artist, gchar * * album); #endif /* AUDACIOUS_UTIL_H */ diff --git a/src/audacious/vis_runner.c b/src/audacious/vis_runner.c index 8e3505a..a82e5f7 100644 --- a/src/audacious/vis_runner.c +++ b/src/audacious/vis_runner.c @@ -22,7 +22,7 @@ #include <glib.h> #include <libaudcore/hook.h> -#include "compatibility.h" +#include "glib-compat.h" #include "misc.h" #include "output.h" #include "vis_runner.h" @@ -51,8 +51,7 @@ static gboolean send_audio (void * unused) return FALSE; } - /* We need raw time, not changed for effects and gapless playback. */ - gint outputted = current_output_plugin->output_time (); + gint outputted = get_raw_output_time (); VisNode * vis_node = NULL; VisNode * next; @@ -95,7 +94,26 @@ static gboolean send_clear (void * unused) return FALSE; } -static void flush_locked (void) +static gboolean locked = FALSE; + +void vis_runner_lock (void) +{ + G_LOCK (mutex); + locked = TRUE; +} + +void vis_runner_unlock (void) +{ + locked = FALSE; + G_UNLOCK (mutex); +} + +gboolean vis_runner_locked (void) +{ + return locked; +} + +void vis_runner_flush (void) { g_free (current_node); current_node = NULL; @@ -107,8 +125,6 @@ static void flush_locked (void) void vis_runner_start_stop (gboolean new_playing, gboolean new_paused) { - G_LOCK (mutex); - playing = new_playing; paused = new_paused; active = playing && hooks; @@ -126,20 +142,16 @@ void vis_runner_start_stop (gboolean new_playing, gboolean new_paused) } if (! active) - flush_locked (); + vis_runner_flush (); else if (! paused) send_source = g_timeout_add (INTERVAL, send_audio, NULL); - - G_UNLOCK (mutex); } void vis_runner_pass_audio (gint time, gfloat * data, gint samples, gint channels, gint rate) { - G_LOCK (mutex); - if (! active) - goto UNLOCK; + return; if (current_node && current_node->nch != MIN (channels, 2)) { @@ -196,9 +208,6 @@ void vis_runner_pass_audio (gint time, gfloat * data, gint samples, gint g_queue_push_tail (& vis_list, current_node); current_node = NULL; } - -UNLOCK: - G_UNLOCK (mutex); } static void time_offset_cb (VisNode * vis_node, void * offset) @@ -208,21 +217,10 @@ static void time_offset_cb (VisNode * vis_node, void * offset) void vis_runner_time_offset (gint offset) { - G_LOCK (mutex); - if (current_node) current_node->time += offset; g_queue_foreach (& vis_list, (GFunc) time_offset_cb, GINT_TO_POINTER (offset)); - - G_UNLOCK (mutex); -} - -void vis_runner_flush (void) -{ - G_LOCK (mutex); - flush_locked (); - G_UNLOCK (mutex); } void vis_runner_add_hook (VisHookFunc func, void * user) @@ -234,8 +232,8 @@ void vis_runner_add_hook (VisHookFunc func, void * user) item->user = user; hooks = g_list_prepend (hooks, item); - G_UNLOCK (mutex); vis_runner_start_stop (playing, paused); + G_UNLOCK (mutex); } void vis_runner_remove_hook (VisHookFunc func) @@ -252,6 +250,6 @@ void vis_runner_remove_hook (VisHookFunc func) } } - G_UNLOCK (mutex); vis_runner_start_stop (playing, paused); + G_UNLOCK (mutex); } diff --git a/src/audacious/vis_runner.h b/src/audacious/vis_runner.h index 7599f25..30d4e58 100644 --- a/src/audacious/vis_runner.h +++ b/src/audacious/vis_runner.h @@ -22,6 +22,14 @@ #ifndef AUD_VIS_RUNNER_H #define AUD_VIS_RUNNER_H +/* When the decoder thread wants to send data to the vis runner, it must block + * the vis timeout before blocking output functions; otherwise, the vis timeout + * will hang up waiting for those output functions to be unblocked while the + * decoder thread hangs up waiting for the vis timeout to finish. */ +void vis_runner_lock (void); +void vis_runner_unlock (void); +gboolean vis_runner_locked (void); + void vis_runner_start_stop (gboolean playing, gboolean paused); void vis_runner_pass_audio (gint time, gfloat * data, gint samples, gint channels, gint rate); diff --git a/src/audacious/visualization.c b/src/audacious/visualization.c index 30a821b..9310e8b 100644 --- a/src/audacious/visualization.c +++ b/src/audacious/visualization.c @@ -37,6 +37,7 @@ #include "playback.h" #include "plugin.h" #include "plugins.h" +#include "ui_preferences.h" #include "visualization.h" typedef struct { @@ -46,6 +47,7 @@ typedef struct { gboolean started; } LoadedVis; +static gint running = FALSE; static GList * loaded_vis_plugins = NULL; void calc_stereo_pcm (VisPCMData dest, const VisPCMData src, gint nch) @@ -211,9 +213,6 @@ static void vis_load (PluginHandle * plugin) VisPlugin * header = plugin_get_header (plugin); g_return_if_fail (header != NULL); - if (header->init != NULL) - header->init (); - LoadedVis * vis = g_slice_new (LoadedVis); vis->plugin = plugin; vis->header = header; @@ -261,9 +260,6 @@ static void vis_unload (PluginHandle * plugin) g_return_if_fail (vis->widget == NULL); /* not destroyed? */ } - if (vis->header->cleanup != NULL) - vis->header->cleanup (); - g_slice_free (LoadedVis, vis); } @@ -275,6 +271,9 @@ static gboolean vis_init_cb (PluginHandle * plugin) void vis_init (void) { + g_return_if_fail (! running); + running = TRUE; + plugin_for_enabled (PLUGIN_TYPE_VIS, (PluginForEachFunc) vis_init_cb, NULL); hook_associate ("playback begin", (HookFunction) vis_start_all, NULL); @@ -288,18 +287,53 @@ static void vis_cleanup_cb (LoadedVis * vis) void vis_cleanup (void) { + g_return_if_fail (running); + running = FALSE; + hook_dissociate ("playback begin", (HookFunction) vis_start_all); hook_dissociate ("playback stop", (HookFunction) vis_stop_all); g_list_foreach (loaded_vis_plugins, (GFunc) vis_cleanup_cb, NULL); } -void vis_plugin_enable (PluginHandle * plugin, gboolean enable) +gboolean vis_plugin_start (PluginHandle * plugin) { - plugin_set_enabled (plugin, enable); + VisPlugin * vp = plugin_get_header (plugin); + g_return_val_if_fail (vp != NULL, FALSE); + + if (vp->init != NULL && ! vp->init ()) + return FALSE; - if (enable) + if (running) vis_load (plugin); - else + + return TRUE; +} + +void vis_plugin_stop (PluginHandle * plugin) +{ + VisPlugin * vp = plugin_get_header (plugin); + g_return_if_fail (vp != NULL); + + if (running) vis_unload (plugin); + + if (vp->settings != NULL) + plugin_preferences_cleanup (vp->settings); + if (vp->cleanup != NULL) + vp->cleanup (); +} + +PluginHandle * vis_plugin_by_widget (/* GtkWidget * */ void * widget) +{ + g_return_val_if_fail (widget, NULL); + + for (GList * node = loaded_vis_plugins; node; node = node->next) + { + LoadedVis * vis = node->data; + if (vis->widget == widget) + return vis->plugin; + } + + return NULL; } diff --git a/src/audacious/visualization.h b/src/audacious/visualization.h index c4ff618..f84cb10 100644 --- a/src/audacious/visualization.h +++ b/src/audacious/visualization.h @@ -22,7 +22,16 @@ #ifndef AUDACIOUS_VISUALIZATION_H #define AUDACIOUS_VISUALIZATION_H +#include <glib.h> + +#include "plugins.h" + void vis_init (void); void vis_cleanup (void); +gboolean vis_plugin_start (PluginHandle * plugin); +void vis_plugin_stop (PluginHandle * plugin); + +PluginHandle * vis_plugin_by_widget (/* GtkWidget * */ void * widget); + #endif diff --git a/src/audtool/Makefile b/src/audtool/Makefile index df743a1..dbc3c94 100644 --- a/src/audtool/Makefile +++ b/src/audtool/Makefile @@ -1,4 +1,4 @@ -PROG = audtool2 +PROG = audtool SRCS = main.c \ handlers_general.c \ handlers_playback.c \ @@ -25,11 +25,3 @@ LIBS += ${DBUS_LIBS} \ ${GTK_LIBS} \ ${MOWGLI_LIBS} \ ${LIBMCS_LIBS} - -install-extra: - if test -h "${DESTDIR}${bindir}/audtool" ; then ${RM} "${DESTDIR}${bindir}/audtool" ; fi - mkdir -p "${DESTDIR}${bindir}" - ${LN_S} audtool2 "${DESTDIR}${bindir}/audtool" - -uninstall-extra: - if test -h "${DESTDIR}${bindir}/audtool" ; then ${RM} "${DESTDIR}${bindir}/audtool" ; fi diff --git a/src/audtool/main.c b/src/audtool/main.c index 1666d41..18612fe 100644 --- a/src/audtool/main.c +++ b/src/audtool/main.c @@ -1,5 +1,5 @@ /* - * Audtool2 + * Audtool * Copyright (c) 2007 Audacious development team * * Redistribution and use in source and binary forms, with or without @@ -84,7 +84,7 @@ struct commandhandler handlers[] = { {"playqueue-add", playqueue_add, "adds a song to the playqueue", 1}, {"playqueue-remove", playqueue_remove, "removes a song from the playqueue", 1}, {"playqueue-is-queued", playqueue_is_queued, "returns OK if a song is queued", 1}, - {"playqueue-get-queue-position", playqueue_get_queue_position, "returns the playqueue position of a song in the given poition in the playlist", 1}, + {"playqueue-get-queue-position", playqueue_get_queue_position, "returns the playqueue position of a song in the given position in the playlist", 1}, {"playqueue-get-list-position", playqueue_get_list_position, "returns the playlist position of a song in the given position in the playqueue", 1}, {"playqueue-length", playqueue_length, "returns the length of the playqueue", 0}, {"playqueue-display", playqueue_display, "returns a list of currently-queued songs", 0}, @@ -190,7 +190,7 @@ main(gint argc, gchar **argv) if (argc < 2) mowgli_error_context_display_with_error (e, ":\n * ", "not enough " - "parameters, use \'audtool2 help\' for more information."); + "parameters, use \'audtool help\' for more information."); for (j = 1; j < argc; j++) { @@ -212,7 +212,7 @@ main(gint argc, gchar **argv) if (k == 0) mowgli_error_context_display_with_error (e, ":\n * ", g_strdup_printf - ("Unknown command '%s' encountered, use \'audtool2 help\' for a " + ("Unknown command '%s' encountered, use \'audtool help\' for a " "command list.", argv[1])); audtool_disconnect(); diff --git a/src/libaudclient/Makefile b/src/libaudclient/Makefile index 0ce223b..e5c74da 100644 --- a/src/libaudclient/Makefile +++ b/src/libaudclient/Makefile @@ -12,7 +12,7 @@ include ../../extra.mk pre-depend: cd ../audacious; ${MAKE} ${MFLAGS} dbus-client-bindings.h -CPPFLAGS = ${LIB_CPPFLAGS} ${CFLAGS} -D_AUDACIOUS_CORE -I.. -I../.. +CPPFLAGS = ${CFLAGS} -D_AUDACIOUS_CORE -I.. -I../.. CFLAGS += ${LIB_CFLAGS} \ ${GLIB_CFLAGS} \ ${MOWGLI_CFLAGS} \ diff --git a/src/libaudcore/Makefile b/src/libaudcore/Makefile index 2ddd5fd..39815f9 100644 --- a/src/libaudcore/Makefile +++ b/src/libaudcore/Makefile @@ -16,8 +16,7 @@ SRCS = audio.c \ vfs_buffer.c \ vfs_buffered_file.c \ vfs_common.c \ - md5.c \ - log.c + md5.c INCLUDES = audio.h \ audstrings.h \ @@ -30,17 +29,16 @@ INCLUDES = audio.h \ tuple_formatter.h \ vfs.h \ vfs_async.h \ - vfs_buffer.h \ - vfs_buffered_file.h \ - md5.h \ - log.h + vfs_buffer.h \ + vfs_buffered_file.h \ + md5.h include ../../buildsys.mk include ../../extra.mk includesubdir = libaudcore -CPPFLAGS += -DHAVE_CONFIG_H ${LIB_CPPFLAGS} ${CFLAGS} -I.. -I../.. +CPPFLAGS += -DHAVE_CONFIG_H ${CFLAGS} -I.. -I../.. CFLAGS += ${LIB_CFLAGS} ${GLIB_CFLAGS} ${MOWGLI_CFLAGS} LIBS += ${GLIB_LIBS} ${MOWGLI_LIBS} diff --git a/src/libaudcore/audstrings.c b/src/libaudcore/audstrings.c index 71f99af..b3ef5ff 100644 --- a/src/libaudcore/audstrings.c +++ b/src/libaudcore/audstrings.c @@ -36,39 +36,6 @@ #include <ctype.h> /** - * Escapes characters that are special to the shell inside double quotes. - * - * @param string String to be escaped. - * @return Given string with special characters escaped. Must be freed with g_free(). - */ -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; -} - -/** * Performs in place replacement of Windows-style drive letter with '/' (slash). * * @param str String to be manipulated. @@ -119,10 +86,10 @@ str_has_prefix_nocase(const gchar * str, const gchar * prefix) return (str != NULL && (strncasecmp(str, prefix, strlen(prefix)) == 0)); } -gboolean -str_has_suffix_nocase(const gchar * str, const gchar * suffix) +gboolean str_has_suffix_nocase (const gchar * str, const gchar * suffix) { - return (str != NULL && strcasecmp(str + strlen(str) - strlen(suffix), suffix) == 0); + return (str && strlen (str) >= strlen (suffix) && ! strcasecmp (str + strlen + (str) - strlen (suffix), suffix)); } gboolean @@ -140,24 +107,15 @@ str_has_suffixes_nocase(const gchar * str, gchar * const *suffixes) return FALSE; } -gchar * -str_to_utf8_fallback(const gchar * str) -{ - gchar *out_str, *convert_str, *chr; - - if (!str) - return NULL; - - convert_str = g_strdup(str); - for (chr = convert_str; *chr; chr++) { - if (*chr & 0x80) - *chr = '?'; - } - - out_str = g_strconcat(convert_str, _(" (invalid UTF-8)"), NULL); - g_free(convert_str); +static gchar * (* str_to_utf8_impl) (const gchar *) = NULL; +static gchar * (* str_to_utf8_full_impl) (const gchar *, gssize, gsize *, + gsize *, GError * *) = NULL; - return out_str; +void str_set_utf8_impl (gchar * (* stu_impl) (const gchar *), + gchar * (* stuf_impl) (const gchar *, gssize, gsize *, gsize *, GError * *)) +{ + str_to_utf8_impl = stu_impl; + str_to_utf8_full_impl = stuf_impl; } /** @@ -166,11 +124,19 @@ str_to_utf8_fallback(const gchar * str) * @param str Local filename/path to convert. * @return String in UTF-8 encoding. Must be freed with g_free(). */ -gchar *(*str_to_utf8)(const gchar * str) = str_to_utf8_fallback; -gchar *(*chardet_to_utf8)(const gchar *str, gssize len, - gsize *arg_bytes_read, gsize *arg_bytes_write, - GError **arg_error) = NULL; +gchar * str_to_utf8 (const gchar * str) +{ + g_return_val_if_fail (str_to_utf8_impl, NULL); + return str_to_utf8_impl (str); +} + +gchar * str_to_utf8_full (const gchar * str, gssize len, gsize * bytes_read, + gsize * bytes_written, GError * * err) +{ + g_return_val_if_fail (str_to_utf8_full_impl, NULL); + return str_to_utf8_full_impl (str, len, bytes_read, bytes_written, err); +} #ifdef HAVE_EXECINFO_H # include <execinfo.h> @@ -230,31 +196,6 @@ str_skip_chars(const gchar * str, const gchar * chars) return str; } -const void * memfind (const void * mem, gint size, const void * token, gint - length) -{ - if (! length) - return mem; - - size -= length - 1; - - while (size > 0) - { - const void * maybe = memchr (mem, * (guchar *) token, size); - - if (maybe == NULL) - return NULL; - - if (! memcmp (maybe, token, length)) - return maybe; - - size -= (guchar *) maybe + 1 - (guchar *) mem; - mem = (guchar *) maybe + 1; - } - - return NULL; -} - gchar * convert_dos_path(gchar * path) { @@ -372,12 +313,18 @@ void string_decode_percent (gchar * s) string_decode_percent_2 (s, s); } -/* we encode any character except the "unreserved" characters of RFC 3986 and - * (optionally) the forward slash */ +/* We encode any character except the "unreserved" characters of RFC 3986 and + * (optionally) the forward slash. On Windows, we also (optionally) do not + * encode the colon. */ static gboolean is_legal_char (gchar c, gboolean is_filename) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= - '9') || (strchr ("-_.~", c) != NULL) || (is_filename && c == '/'); + '9') || (strchr ("-_.~", c) != NULL) || +#ifdef _WIN32 + (is_filename && strchr ("/:", c) != NULL); +#else + (is_filename && c == '/'); +#endif } static gchar make_hex_digit (gint i) @@ -490,30 +437,91 @@ void uri_check_utf8 (gchar * * uri, gboolean warn) } /* Like g_filename_to_uri, but converts the filename from the system locale to - * UTF-8 before percent-encoding. */ + * UTF-8 before percent-encoding. On Windows, replaces '\' with '/' and adds a + * leading '/'. */ gchar * filename_to_uri (const gchar * name) { gchar * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL); +#ifdef _WIN32 + string_replace_char (utf8, '\\', '/'); +#endif gchar * enc = string_encode_percent (utf8 ? utf8 : name, TRUE); g_free (utf8); +#ifdef _WIN32 + gchar * uri = g_strdup_printf ("file:///%s", enc); +#else gchar * uri = g_strdup_printf ("file://%s", enc); +#endif g_free (enc); return uri; } /* Like g_filename_from_uri, but converts the filename from UTF-8 to the system - * locale after percent-decoding. */ + * locale after percent-decoding. On Windows, strips the leading '/' and + * replaces '/' with '\'. */ gchar * uri_to_filename (const gchar * uri) { +#ifdef _WIN32 + g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL); + gchar buf[strlen (uri + 8) + 1]; + string_decode_percent_2 (uri + 8, buf); +#else g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL); gchar buf[strlen (uri + 7) + 1]; string_decode_percent_2 (uri + 7, buf); +#endif +#ifdef _WIN32 + string_replace_char (buf, '/', '\\'); +#endif gchar * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL); return name ? name : g_strdup (buf); } +/* Formats a URI for human-readable display. Percent-decodes and converts to + * UTF-8 (more aggressively than uri_to_utf8). For file:// URI's, converts to + * filename format (but in UTF-8). */ + +gchar * uri_to_display (const gchar * uri) +{ + gchar buf[strlen (uri) + 1]; + +#ifdef _WIN32 + if (! strncmp (uri, "file:///", 8)) + { + string_decode_percent_2 (uri + 8, buf); + string_replace_char (buf, '/', '\\'); + } +#else + if (! strncmp (uri, "file://", 7)) + string_decode_percent_2 (uri + 7, buf); +#endif + else + string_decode_percent_2 (uri, buf); + + return str_to_utf8 (buf); +} + +gchar * uri_get_extension (const gchar * uri) +{ + const gchar * slash = strrchr (uri, '/'); + if (! slash) + return NULL; + + gchar * lower = g_ascii_strdown (slash + 1, -1); + + gchar * qmark = strchr (lower, '?'); + if (qmark) + * qmark = 0; + + gchar * dot = strrchr (lower, '.'); + gchar * ext = dot ? g_strdup (dot + 1) : NULL; + + g_free (lower); + return ext; +} + void string_cut_extension(gchar *string) { gchar *period = strrchr(string, '.'); @@ -616,3 +624,71 @@ gint string_compare_encoded (const gchar * ap, const gchar * bp) return 0; } + +const void * memfind (const void * mem, gint size, const void * token, gint + length) +{ + if (! length) + return mem; + + size -= length - 1; + + while (size > 0) + { + const void * maybe = memchr (mem, * (guchar *) token, size); + + if (maybe == NULL) + return NULL; + + if (! memcmp (maybe, token, length)) + return maybe; + + size -= (guchar *) maybe + 1 - (guchar *) mem; + mem = (guchar *) maybe + 1; + } + + return NULL; +} + +gchar * +str_replace_fragment(gchar *s, gint size, const gchar *old, const gchar *new) +{ + gchar *ptr = s; + gint left = strlen(s); + gint avail = size - (left + 1); + gint oldlen = strlen(old); + gint newlen = strlen(new); + gint diff = newlen - oldlen; + + while (left >= oldlen) + { + if (strncmp(ptr, old, oldlen)) + { + left--; + ptr++; + continue; + } + + if (diff > avail) + break; + + if (diff != 0) + memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen); + + memcpy(ptr, new, newlen); + ptr += newlen; + left -= oldlen; + } + + return s; +} + +void +string_canonize_case(gchar *str) +{ + while (*str) + { + *str = g_ascii_toupper(*str); + str++; + } +}
diff --git a/src/libaudcore/audstrings.h b/src/libaudcore/audstrings.h index f15e0cf..bc64e97 100644 --- a/src/libaudcore/audstrings.h +++ b/src/libaudcore/audstrings.h @@ -31,8 +31,6 @@ G_BEGIN_DECLS -gchar *escape_shell_chars(const gchar * string); - gchar *str_append(gchar * str, const gchar * add_str); gchar *str_replace(gchar * str, gchar * new_str); void str_replace_in(gchar ** str, gchar * new_str); @@ -42,19 +40,17 @@ gboolean str_has_suffix_nocase(const gchar * str, const gchar * suffix); gboolean str_has_suffixes_nocase(const gchar * str, gchar * const *suffixes); gchar *str_assert_utf8(const gchar *str); -extern gchar *(*str_to_utf8)(const gchar * str); -gchar *str_to_utf8_fallback(const gchar * str); -extern gchar * (* chardet_to_utf8) (const gchar * string, gssize length, gsize * - bytes_read, gsize * bytes_written, GError * * error); + +void str_set_utf8_impl (gchar * (* stu_impl) (const gchar *), + gchar * (* stuf_impl) (const gchar *, gssize, gsize *, gsize *, GError * *)); +gchar * str_to_utf8 (const gchar * str); +gchar * str_to_utf8_full (const gchar * str, gssize len, gsize * bytes_read, + gsize * bytes_written, GError * * err); const gchar *str_skip_chars(const gchar * str, const gchar * chars); gchar *convert_dos_path(gchar * text); -extern gchar *(*chardet_to_utf8)(const gchar *str, gssize len, - gsize *arg_bytes_read, gsize *arg_bytes_write, - GError **arg_error); - gchar *filename_get_subtune(const gchar * filename, gint * track); gchar *filename_split_subtune(const gchar * filename, gint * track); @@ -67,6 +63,9 @@ gchar * uri_to_utf8 (const gchar * uri); void uri_check_utf8 (gchar * * uri, gboolean warn); gchar * filename_to_uri (const gchar * filename); gchar * uri_to_filename (const gchar * uri); +gchar * uri_to_display (const gchar * uri); + +gchar * uri_get_extension (const gchar * uri); void string_cut_extension(gchar *string); gint string_compare (const gchar * a, const gchar * b); @@ -75,6 +74,10 @@ gint string_compare_encoded (const gchar * a, const gchar * b); const void * memfind (const void * mem, gint size, const void * token, gint length); +gchar *str_replace_fragment(gchar *s, gint size, const gchar *old_str, const gchar *new_str); + +void string_canonize_case(gchar *string); + G_END_DECLS #endif /* AUDACIOUS_STRINGS_H */ diff --git a/src/libaudcore/index.c b/src/libaudcore/index.c index 9344e7b..0ee66db 100644 --- a/src/libaudcore/index.c +++ b/src/libaudcore/index.c @@ -36,7 +36,7 @@ struct index struct index * index_new (void) { - struct index * index = g_malloc (sizeof (struct index)); + struct index * index = g_slice_new (struct index); index->data = NULL; index->count = 0; @@ -50,7 +50,7 @@ struct index * index_new (void) void index_free (struct index * index) { g_free (index->data); - g_free (index); + g_slice_free (struct index, index); } gint index_count (struct index * index) @@ -58,6 +58,20 @@ gint index_count (struct index * index) return index->count; } +void index_allocate (struct index * index, gint size) +{ + if (size <= index->size) + return; + + if (! index->size) + index->size = 64; + + while (size > index->size) + index->size <<= 1; + + index->data = g_realloc (index->data, sizeof (void *) * index->size); +} + void index_set (struct index * index, gint at, void * value) { index->data[at] = value; @@ -68,27 +82,14 @@ void * index_get (struct index * index, gint at) return index->data[at]; } -static void resize_to (struct index * index, gint size) +static void make_room (struct index * index, gint at, gint count) { - if (size < 100) - size = (size + 9) / 10 * 10; - else if (size < 1000) - size = (size + 99) / 100 * 100; - else - size = (size + 999) / 1000 * 1000; + index_allocate (index, index->count + count); - if (index->size < size) - { - index->data = g_realloc (index->data, sizeof (void *) * size); - index->size = size; - } -} + if (at < index->count) + memmove (index->data + at + count, index->data + at, sizeof (void *) * + (index->count - at)); -static void make_room (struct index * index, gint at, gint count) -{ - resize_to (index, index->count + count); - memmove (index->data + at + count, index->data + at, sizeof (void *) * - (index->count - at)); index->count += count; } diff --git a/src/libaudcore/index.h b/src/libaudcore/index.h index 67b1781..0a3499b 100644 --- a/src/libaudcore/index.h +++ b/src/libaudcore/index.h @@ -27,6 +27,7 @@ struct index; struct index * index_new (void); void index_free (struct index * index); gint index_count (struct index * index); +void index_allocate (struct index * index, gint size); void index_set (struct index * index, gint at, void * value); void * index_get (struct index * index, gint at); void index_insert (struct index * index, gint at, void * value); diff --git a/src/libaudcore/log.c b/src/libaudcore/log.c deleted file mode 100644 index 089bfd0..0000000 --- a/src/libaudcore/log.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Logging mechanisms - * Copyright (c) 2009 Audacious 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ - -#include "log.h" -#include <stdio.h> -#include <string.h> - -/** Logfile creation timestamp format */ -#define AUD_LOG_CTIME_FMT "%c" - -/** Logfile entry timestamp format */ -#define AUD_LOG_LTIME_FMT "%H:%M:%S" - -/** - * Global log level setting, this determines what is logged in - * the logfile. Anything "above" this level will not be logged. - */ -static gint log_level = AUD_LOG_INFO; - -/** Global log file handle */ -static FILE *log_file = NULL; - -/** Mutex for protecting from threaded access */ -static GMutex *log_mutex = NULL; - -/** Hashtable that contains registered thread information */ -static GHashTable *log_thread_hash = NULL; - -/** Descriptive names for different log levels */ -const gchar *log_level_names[AUD_LOG_ALL] = { - "none", - "FATAL", - "ERROR", - "warning", - "info", - "DEBUG", - "DEBUG+", -}; - - -/** - * Create a time/date string based on given format string. - * Current adjusted local time is used as timestamp. - * - * @param[in] fmt Format for time/date string, as described in 'man strftime'. - * @return Newly allocated string, must be freed with g_free(). - */ -static gchar * -aud_log_timestr(const gchar *fmt) -{ - gchar tmp[256] = ""; - time_t stamp = time(NULL); - struct tm stamp_tm; - - if (stamp >= 0 && localtime_r(&stamp, &stamp_tm) != NULL) - strftime(tmp, sizeof(tmp), fmt, &stamp_tm); - - return g_strdup(tmp); -} - -/** - * The actual internal function that does the logfile output. - * Entry is prefixed with timestamp information, log level's name - * (as defined in #log_level_names), logging context and the message - * itself. Linefeeds are optional in format string, they are added - * automatically for compatibility with g_log(). - * - * @param[in] f File handle to write into. - * @param[in] ctx Logging context or NULL if no context / global context. - * @param[in] level Message's log level setting. - * @param[in] msg Log message string. - */ -static void -aud_log_msg(FILE *f, const gchar *ctx, gint level, const gchar *msg) -{ - gchar *timestamp; - GThread *thread = g_thread_self(); - gchar *name = (log_thread_hash != NULL) ? - g_hash_table_lookup(log_thread_hash, thread) : NULL; - - timestamp = aud_log_timestr(AUD_LOG_LTIME_FMT); - fprintf(f, "%s <", timestamp); - g_free(timestamp); - - if (name != NULL) - { - if (ctx != NULL) - fprintf(f, "%s|%s", ctx, name); - else - fprintf(f, "%s", name); - } - else - { - fprintf(f, "%s|%p", ctx != NULL ? ctx : "global", (void *) thread); - } - - fprintf(f, "> [%s]: %s", (level >= 0) ? log_level_names[level] : - log_level_names[AUD_LOG_INFO], msg); - - /* A small hack here to ease transition from g_log() etc. */ - if (msg[strlen(msg) - 1] != '\n') - fprintf(f, "\n"); - - fflush(f); -} - - -/** - * Varargs version of internal non-locked logging function. - * - * @param[in] f File handle to write into. - * @param[in] ctx Logging context or NULL if no context / global context. - * @param[in] level Message's log level setting. - * @param[in] fmt Message printf() style format string. - * @param[in] args Argument structure. - */ -static void -aud_do_logv(FILE *f, const gchar *ctx, gint level, const gchar *fmt, va_list args) -{ - gchar *msg = g_strdup_vprintf(fmt, args); - aud_log_msg(f, ctx, level, msg); - g_free(msg); -} - -/** - * Internal message logging function that does take the global lock. - * - * @param[in] f File handle to write into. - * @param[in] ctx Logging context or NULL if no context / global context. - * @param[in] level Message's log level setting. - * @param[in] fmt Message printf() style format string. - * @param[in] ... Printf() style arguments, if any. - */ -static void -aud_do_log(FILE *f, const gchar *ctx, gint level, const gchar *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - aud_do_logv(f, ctx, level, fmt, ap); - va_end(ap); -} - -/** - * Initialize logging subsystem. - * - * @param[in] filename Filename for logfile, or NULL to use stderr. - * @param[in] mode Open mode for fopen(). - * @param[in] level Default logging level. - */ -gint -aud_log_init(const gchar *filename, const gchar *mode, gint level) -{ - FILE *tmp; - gchar *timestamp; - - /* Open or set logging file descriptor */ - if (filename != NULL) - { - if ((tmp = fopen(filename, mode)) == NULL) - return -1; - } - else - tmp = NULL; - - /* Create mutex, unless it already is set */ - if (log_mutex != NULL || (log_mutex = g_mutex_new()) == NULL) - { - fclose(tmp); - return -3; - } - - /* Acquire mutex, set log_file etc. */ - g_mutex_lock(log_mutex); - if (log_file != NULL) - fclose(log_file); - - if (tmp == NULL) - log_file = stderr; - else - log_file = tmp; - - log_level = level; - - /* Logging starts here */ - timestamp = aud_log_timestr(AUD_LOG_CTIME_FMT); - aud_do_log(log_file, NULL, -1, "Logfile opened %s.\n", timestamp); - g_free(timestamp); - - /* Setup thread context hash table */ - if (log_thread_hash != NULL) - { - aud_do_log(log_file, NULL, -1, "Warning, log_thread_hash != NULL (%p)!", - log_thread_hash); - g_hash_table_destroy(log_thread_hash); - } - - log_thread_hash = g_hash_table_new_full( - g_direct_hash, g_direct_equal, NULL, g_free); - - g_mutex_unlock(log_mutex); - return 0; -} - -/** Internal helper function for #aud_log_close(). */ -static void -aud_log_print_hash(gpointer key, gpointer value, gpointer found) -{ - if (*(gboolean *)found == FALSE) - { - *(gboolean *)found = TRUE; - aud_do_log(log_file, NULL, -1, - "Warning, following lingering log thread contexts found:\n"); - } - - aud_do_log(log_file, NULL, -1, " - %p = '%s'\n", key, value); -} - -/** - * Shut down the logging subsystem. Logfile handle is closed, - * mutexes and such freed, etc. - */ -void -aud_log_close(void) -{ - GMutex *tmp; - gchar *timestamp; - - if ((tmp = log_mutex) != NULL) - { - g_mutex_lock(tmp); - - if (log_thread_hash != NULL) - { - gboolean found = FALSE; - g_hash_table_foreach(log_thread_hash, - aud_log_print_hash, &found); - - g_hash_table_destroy(log_thread_hash); - } - log_thread_hash = NULL; - - timestamp = aud_log_timestr(AUD_LOG_CTIME_FMT); - aud_do_log(log_file, NULL, -1, "Logfile closed %s.\n", timestamp); - g_free(timestamp); - - log_mutex = NULL; - - if (log_file != NULL) - fflush(log_file); - - if (log_file != stderr) - fclose(log_file); - - log_file = NULL; - g_mutex_unlock(tmp); - } -} - -/** - * Add symbolic name for given thread identifier. The identifier - * will be used in subsequent log messages originating from the - * thread. - * - * @param[in] thread Pointer to a GThread structure of the thread. - * @param[in] name String describing the thread. - */ -void -aud_log_add_thread_context(GThread *thread, const gchar *name) -{ - gchar *tmp = g_strdup(name), *old; - g_mutex_lock(log_mutex); - - old = g_hash_table_lookup(log_thread_hash, thread); - if (old != NULL) - aud_do_log(log_file, NULL, AUD_LOG_INFO, - "Warning, thread %p is already in context ('%s')!\n", thread, old); - - g_hash_table_insert(log_thread_hash, thread, tmp); - - aud_do_log(log_file, NULL, AUD_LOG_INFO, - "Thread %p name set to '%s'\n", thread, name); - - g_mutex_unlock(log_mutex); -} - -/** - * Removes identifier for thread, if present. If thread had not been - * added in first place (via #aud_log_add_thread_context()), a warning - * is logged instead. - * - * @param[in] thread Pointer to a GThread structure of the thread. - */ -void -aud_log_delete_thread_context(GThread *thread) -{ - gchar *old; - g_mutex_lock(log_mutex); - - old = g_hash_table_lookup(log_thread_hash, thread); - if (old == NULL) - { - aud_do_log(log_file, NULL, AUD_LOG_INFO, - "Warning, thread %p does not exist in context table!\n", thread); - } - else - { - aud_do_log(log_file, NULL, AUD_LOG_INFO, - "Thread %p name ('%s') deleted from context table.\n", thread, old); - g_hash_table_remove(log_thread_hash, thread); - } - - g_mutex_unlock(log_mutex); -} - -/** - * Write a log entry with variable arguments structure. - * - * @param[in] ctx Logging context or NULL if no context / global context. - * @param[in] level Message's log level setting. - * @param[in] fmt Message printf() style format string. - * @param[in] args Argument structure. - */ -void -aud_logv(const gchar *ctx, gint level, const gchar *fmt, va_list args) -{ - if (log_mutex == NULL || log_file == NULL) - aud_do_log(stderr, ctx, level, fmt, args); - else - { - g_mutex_lock(log_mutex); - if (level <= log_level) - aud_do_logv(log_file, ctx, level, fmt, args); - g_mutex_unlock(log_mutex); - } -} - -/** - * Write a log entry. - * - * @param[in] ctx Logging context or NULL if no context / global context. - * @param[in] level Message's log level setting. - * @param[in] fmt Message printf() style format string. - * @param[in] ... Optional printf() arguments. - */ -void -aud_log(const gchar *ctx, gint level, const gchar *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - aud_logv(ctx, level, fmt, ap); - va_end(ap); -} - -/** - * Write a log entry with file/func/line information. - * - * @param[in] ctx Logging context pointer or NULL if no context / global context. - * @param[in] level Message's log level setting. - * @param[in] file Filename (usually obtained through __FILE__ macro) - * @param[in] func Function name (usually obtained through __FUNCTION__ macro) - * @param[in] line Linenumber (usually obtained through __LINE__ macro) - * @param[in] fmt Message printf() style format string. - * @param[in] ... Optional printf() arguments. - */ -void -aud_log_line(const gchar *ctx, gint level, const gchar *file, const gchar *func, - gint line, const gchar *fmt, ...) -{ - gchar *msg, *str, *info = g_strdup_printf("(%s:%s:%d) ", file, func, line); - va_list ap; - - va_start(ap, fmt); - msg = g_strdup_vprintf(fmt, ap); - va_end(ap); - - str = g_strconcat(info, msg, NULL); - - if (log_mutex == NULL || log_file == NULL) - aud_log_msg(stderr, ctx, level, str); - else - { - g_mutex_lock(log_mutex); - aud_log_msg(log_file, ctx, level, str); - g_mutex_unlock(log_mutex); - } - - g_free(info); - g_free(msg); - g_free(str); -} diff --git a/src/libaudcore/log.h b/src/libaudcore/log.h deleted file mode 100644 index 769bd6d..0000000 --- a/src/libaudcore/log.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Logging and debug mechanisms - * Copyright (c) 2009 Audacious 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ -/** - * @file log.h - * @brief Informative logging API (aud_log(), aud_debug() and friends). - * Functions for logfile handling, log contexts, logging levels, etc. - * Also functions and macros for debug-level stuff. - */ - -#ifndef AUD_LOG_CTX -# define AUD_LOG_CTX NULL -#endif - -#ifndef AUDACIOUS_LOG_H -#define AUDACIOUS_LOG_H - -#include <glib.h> -#include <stdarg.h> - -G_BEGIN_DECLS - -/** Log levels from least noisy to noisiest */ -typedef enum { - AUD_LOG_NONE = 0, /**< Pseudo log-level for suppressing most log messages */ - AUD_LOG_FATAL_ERROR, - AUD_LOG_ERROR, - AUD_LOG_WARNING, - AUD_LOG_INFO, - AUD_LOG_DEBUG, /**< General debugging */ - AUD_LOG_DEBUG_INT, /**< Intensive debugging (more details) */ - AUD_LOG_ALL /**< Pseudo log-level for full logging */ -} AudLogLevel; - -gint aud_log_init(const gchar *filename, const gchar *mode, gint level); -void aud_log_close(void); - -void aud_log_add_thread_context(GThread *thread, const gchar *name); -void aud_log_delete_thread_context(GThread *thread); - -void aud_logv(const gchar *ctx, gint level, const gchar *fmt, va_list args) __attribute__ ((format(printf, 3, 0))); -void aud_log(const gchar *ctx, gint level, const gchar *fmt, ...) __attribute__ ((format(printf, 3, 4))); -void aud_log_line(const gchar *ctx, gint level, const gchar *file, const gchar *func, gint line, const gchar *fmt, ...) __attribute__ ((format(printf, 6, 7))); - - -/* These are here as a quick hack for transition from glib message system */ -//#define GLIB_COMPAT - -#ifdef GLIB_COMPAT -#undef g_message -#undef g_warning -#undef g_debug -#undef g_error -#undef g_critical -#define g_message(...) aud_log(AUD_LOG_CTX, AUD_LOG_INFO, __VA_ARGS__) -#define g_warning(...) aud_log(AUD_LOG_CTX, AUD_LOG_WARNING, __VA_ARGS__) -#define g_error(...) do { aud_log(AUD_LOG_CTX, AUD_LOG_ERROR, __VA_ARGS__); abort(); } while (0) -#define g_critical(...) do { aud_log(AUD_LOG_CTX, AUD_LOG_ERROR, __VA_ARGS__); abort(); } while (0) -#endif - -//@{ -/** Convenience wrapper message macros */ -#if defined(DEBUG) -# define aud_message(...) aud_log_line(AUD_LOG_CTX, AUD_LOG_INFO, __FILE__, __FUNCTION__, (gint) __LINE__, __VA_ARGS__) -# define aud_warning(...) aud_log_line(AUD_LOG_CTX, AUD_LOG_WARNING, __FILE__, __FUNCTION__, (gint) __LINE__ , __VA_ARGS__) -#else -# define aud_message(...) aud_log(AUD_LOG_CTX, AUD_LOG_INFO, __VA_ARGS__) -# define aud_warning(...) aud_log(AUD_LOG_CTX, AUD_LOG_WARNING, __VA_ARGS__) -#endif -//@} - -//@{ -/** Debug message macro and transitional aliases */ -#if defined(DEBUG) -# define AUDDBG(...) aud_log_line(AUD_LOG_CTX, AUD_LOG_DEBUG, __FILE__, __FUNCTION__, (gint) __LINE__, __VA_ARGS__) -# define aud_debug AUDDBG -# ifdef GLIB_COMPAT -# define g_debug AUDDBG -# endif -#else -# define AUDDBG(...) -# define aud_debug(...) -# ifdef GLIB_COMPAT -# define g_debug -# endif -#endif -//@} - -//@{ -/** Extra debug messages (more noisy, needs DEBUG > 1) */ -#if defined(DEBUG) && (DEBUG > 1) -# define AUDDBG_I(...) aud_log_line(AUD_LOG_CTX, AUD_LOG_DEBUG_INT, __FILE__, __FUNCTION__, (gint) __LINE__, __VA_ARGS__) -#else -# define AUDDBG_I(...) -#endif -//@} - -G_END_DECLS - -#endif /* AUDACIOUS_LOG_H */ diff --git a/src/libaudcore/stringpool.c b/src/libaudcore/stringpool.c index 7833069..b30889d 100644 --- a/src/libaudcore/stringpool.c +++ b/src/libaudcore/stringpool.c @@ -1,6 +1,7 @@ /* * Audacious * Copyright © 2009 William Pitcock <nenolod@atheme.org> + * Copyright © 2010 John Lindgren <john.lindgren@tds.net> * * 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 @@ -18,110 +19,71 @@ * Audacious or using our public API to be a derived work. */ -/* - * Note: This code used to do some normalization of strings: conversion to - * UTF-8, conversion of the empty string to NULL, and (optionally) conversion - * to uppercase. However, because such conversions can change the length of the - * string, they can lead to a double-free. - * - * Consider: - * - * stringpool_get is called twice with the same 99-character ISO-8859-1 string. - * The string is short enough to be cached, so stringpool_get returns a cached, - * 101-character UTF-8 string. stringpool_unref is then called twice - * with the cached string. Now that it has been converted, it is too long to be - * cached, so stringpool_unref simply frees it, twice. - * - * Therefore, it is essential for stringpool_get to return a string that is - * exactly the same as the one passed it. - * - * --jlindgren - */ - +#include <assert.h> #include <glib.h> #include <mowgli.h> -#include "audstrings.h" - -#define MAXLEN 100 - -static void -noopcanon(gchar *str) -{ - return; -} - -/** Structure to handle string refcounting. */ -typedef struct { - gint refcount; - gchar *str; -} PooledString; - static mowgli_patricia_t *stringpool_tree = NULL; static GStaticMutex stringpool_mutex = G_STATIC_MUTEX_INIT; -static inline gboolean stringpool_should_cache(const gchar *string) -{ - const gchar *end = memchr(string, '\0', MAXLEN + 1); - return end != NULL ? TRUE : FALSE; -} +#define MAXLEN 100 gchar * -stringpool_get(const gchar *str) +stringpool_get(gchar *str, gboolean take) { - PooledString *ps; - - g_return_val_if_fail(str != NULL, NULL); + if (str == NULL) + return NULL; - if (!stringpool_should_cache(str)) - return g_strdup(str); + if (strlen(str) > MAXLEN) + return take ? str : g_strdup(str); g_static_mutex_lock(&stringpool_mutex); if (stringpool_tree == NULL) - stringpool_tree = mowgli_patricia_create(noopcanon); + stringpool_tree = mowgli_patricia_create(NULL); - if ((ps = mowgli_patricia_retrieve(stringpool_tree, str)) != NULL) + mowgli_patricia_elem_t *elem = mowgli_patricia_elem_find(stringpool_tree, str); + if (elem != NULL) { - ps->refcount++; - - g_static_mutex_unlock(&stringpool_mutex); - return ps->str; + gint refcount = GPOINTER_TO_INT(mowgli_patricia_elem_get_data(elem)); + mowgli_patricia_elem_set_data(elem, GINT_TO_POINTER(refcount + 1)); + } + else + { + elem = mowgli_patricia_elem_add(stringpool_tree, str, GINT_TO_POINTER(1)); + assert(elem != NULL); } - ps = g_slice_new0(PooledString); - ps->refcount++; - ps->str = g_strdup(str); - mowgli_patricia_add(stringpool_tree, str, ps); + if (take) + g_free(str); + str = (gchar *)mowgli_patricia_elem_get_key(elem); g_static_mutex_unlock(&stringpool_mutex); - return ps->str; + + return str; } void stringpool_unref(gchar *str) { - PooledString *ps; - - g_return_if_fail(str != NULL); - - if (!stringpool_should_cache(str)) - { - g_free(str); + if (str == NULL) return; - } + + if (strlen(str) > MAXLEN) + return g_free(str); g_return_if_fail(stringpool_tree != NULL); g_static_mutex_lock(&stringpool_mutex); - ps = mowgli_patricia_retrieve(stringpool_tree, str); - if (ps != NULL && --ps->refcount <= 0) - { - mowgli_patricia_delete(stringpool_tree, str); - g_free(ps->str); - g_slice_free(PooledString, ps); - } + mowgli_patricia_elem_t *elem = mowgli_patricia_elem_find(stringpool_tree, str); + assert(elem != NULL); + + gint refcount = GPOINTER_TO_INT(mowgli_patricia_elem_get_data(elem)); + if (refcount == 1) + mowgli_patricia_elem_delete(stringpool_tree, elem); + else + mowgli_patricia_elem_set_data(elem, GINT_TO_POINTER(refcount - 1)); g_static_mutex_unlock(&stringpool_mutex); } diff --git a/src/libaudcore/stringpool.h b/src/libaudcore/stringpool.h index 985dfd3..0bd4255 100644 --- a/src/libaudcore/stringpool.h +++ b/src/libaudcore/stringpool.h @@ -32,10 +32,12 @@ * Otherwise, a new string is created in the pool with one reference. * * @param[in] str String to be poolified. + * @param[in] take Nonzero if the caller no longer needs str; in this case, the + * pool will eventually call g_free(str). * @return Reference to the pooled string, or NULL if the given * string was NULL or an error occured. */ -gchar *stringpool_get(const gchar *str); +gchar *stringpool_get(gchar *str, gboolean take); /** * Unreference a pooled string. When there are no references left, diff --git a/src/libaudcore/tuple.c b/src/libaudcore/tuple.c index e7677b1..878b2f8 100644 --- a/src/libaudcore/tuple.c +++ b/src/libaudcore/tuple.c @@ -25,10 +25,16 @@ #include <glib.h> #include <mowgli.h> +#include <audacious/i18n.h> + +#include "config.h" #include "tuple.h" #include "audstrings.h" #include "stringpool.h" +static gboolean set_string (Tuple * tuple, const gint nfield, + const gchar * field, gchar * string, gboolean take); + /** Ordered table of basic #Tuple field names and their #TupleValueType. */ const TupleBasicType tuple_fields[FIELD_LAST] = { @@ -82,32 +88,33 @@ static mowgli_heap_t *tuple_heap = NULL; static mowgli_heap_t *tuple_value_heap = NULL; static mowgli_object_class_t tuple_klass; -/** Global R/W lock for preserve data consistency of heaps */ -static GStaticRWLock tuple_rwlock = G_STATIC_RW_LOCK_INIT; +/** Global lock to preserve data consistency of heaps */ +static GStaticMutex tuple_mutex = G_STATIC_MUTEX_INIT; //@{ /** - * Convenience macro for read/write locking of the globally + * Convenience macros to lock the globally * used internal Tuple system structures. */ -#define TUPLE_LOCK_WRITE(X) g_static_rw_lock_writer_lock(&tuple_rwlock) -#define TUPLE_UNLOCK_WRITE(X) g_static_rw_lock_writer_unlock(&tuple_rwlock) -#define TUPLE_LOCK_READ(X) g_static_rw_lock_reader_lock(&tuple_rwlock) -#define TUPLE_UNLOCK_READ(X) g_static_rw_lock_reader_unlock(&tuple_rwlock) +#define TUPLE_LOCK_WRITE(X) g_static_mutex_lock (& tuple_mutex) +#define TUPLE_UNLOCK_WRITE(X) g_static_mutex_unlock (& tuple_mutex) +#define TUPLE_LOCK_READ(X) g_static_mutex_lock (& tuple_mutex) +#define TUPLE_UNLOCK_READ(X) g_static_mutex_unlock (& tuple_mutex) //@} -/* iterative destructor of tuple values. */ -static void -tuple_value_destroy(mowgli_dictionary_elem_t *delem, gpointer privdata) +static void tuple_value_destroy (TupleValue * value) { - TupleValue *value = (TupleValue *) delem->data; + if (value->type == TUPLE_STRING) + stringpool_unref (value->value.string); - if (value->type == TUPLE_STRING) { - stringpool_unref(value->value.string); - value->value.string = NULL; - } + memset (value, 0, sizeof (TupleValue)); + mowgli_heap_free (tuple_value_heap, value); +} - mowgli_heap_free(tuple_value_heap, value); +/* iterative destructor of tuple values. */ +static void tuple_value_destroy_cb (const gchar * key, void * data, void * priv) +{ + tuple_value_destroy (data); } static void @@ -117,22 +124,17 @@ tuple_destroy(gpointer data) gint i; TUPLE_LOCK_WRITE(); - mowgli_dictionary_destroy(tuple->dict, tuple_value_destroy, NULL); + mowgli_patricia_destroy(tuple->dict, tuple_value_destroy_cb, NULL); for (i = 0; i < FIELD_LAST; i++) - if (tuple->values[i]) { - TupleValue *value = tuple->values[i]; - - if (value->type == TUPLE_STRING) { - stringpool_unref(value->value.string); - value->value.string = NULL; - } - - mowgli_heap_free(tuple_value_heap, value); - } + { + if (tuple->values[i]) + tuple_value_destroy (tuple->values[i]); + } g_free(tuple->subtunes); + memset (tuple, 0, sizeof (Tuple)); mowgli_heap_free(tuple_heap, tuple); TUPLE_UNLOCK_WRITE(); } @@ -155,7 +157,7 @@ tuple_new_unlocked(void) memset(tuple, 0, sizeof(Tuple)); mowgli_object_init(mowgli_object(tuple), NULL, &tuple_klass, NULL); - tuple->dict = mowgli_dictionary_create(g_ascii_strcasecmp); + tuple->dict = mowgli_patricia_create(string_canonize_case); return tuple; } @@ -190,50 +192,38 @@ tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, Tuple * @param[in] filename Filename URI. * @param[in,out] tuple Tuple structure to manipulate. */ -void -tuple_set_filename(Tuple *tuple, const gchar *filename) +void tuple_set_filename (Tuple * tuple, const gchar * name) { - gchar *local = g_strdup(filename); - gchar *slash, *period, *question; - - string_decode_percent(local); - - /* Convert invalid UTF-8 URI's quietly. */ - if (! g_utf8_validate (local, -1, NULL)) + const gchar * slash; + if ((slash = strrchr (name, '/'))) { - gchar * utf8 = str_to_utf8 (local); - g_free (local); - local = utf8; + gchar path[slash - name + 2]; + memcpy (path, name, slash - name + 1); + path[slash - name + 1] = 0; + + set_string (tuple, FIELD_FILE_PATH, NULL, uri_to_display (path), TRUE); + name = slash + 1; } - slash = strrchr(local, '/'); - period = strrchr(local, '.'); - question = strrchr(local, '?'); + gchar buf[strlen (name) + 1]; + strcpy (buf, name); - if (slash != NULL) + gchar * c; + if ((c = strrchr (buf, '?'))) { - gchar temp = *(slash + 1); + gint sub; + if (sscanf (c + 1, "%d", & sub) == 1) + tuple_associate_int (tuple, FIELD_SUBSONG_ID, NULL, sub); - *(slash + 1) = 0; - tuple_associate_string(tuple, FIELD_FILE_PATH, NULL, local); - *(slash + 1) = temp; - tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, slash + 1); + * c = 0; } - if (question != NULL) - { - gint subtune; - - *question = 0; + gchar * base = uri_to_display (buf); - if (sscanf(question + 1, "%d", &subtune) == 1) - tuple_associate_int(tuple, FIELD_SUBSONG_ID, NULL, subtune); - } + if ((c = strrchr (base, '.'))) + set_string (tuple, FIELD_FILE_EXT, NULL, c + 1, FALSE); - if (period != NULL) - tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, period + 1); - - g_free(local); + set_string (tuple, FIELD_FILE_NAME, NULL, base, TRUE); } /** @@ -251,11 +241,12 @@ tuple_copy_value(TupleValue *src) if (src == NULL) return NULL; res = mowgli_heap_alloc(tuple_value_heap); + g_strlcpy(res->name, src->name, TUPLE_NAME_MAX); res->type = src->type; switch (src->type) { case TUPLE_STRING: - res->value.string = stringpool_get(src->value.string); + res->value.string = stringpool_get (src->value.string, FALSE); break; case TUPLE_INT: res->value.integer = src->value.integer; @@ -278,7 +269,7 @@ tuple_copy(const Tuple *src) { Tuple *dst; TupleValue * tv, * copied; - mowgli_dictionary_iteration_state_t state; + mowgli_patricia_iteration_state_t state; gint i; g_return_val_if_fail(src != NULL, NULL); @@ -292,10 +283,10 @@ tuple_copy(const Tuple *src) dst->values[i] = tuple_copy_value(src->values[i]); /* Copy dictionary contents */ - MOWGLI_DICTIONARY_FOREACH (tv, & state, src->dict) + MOWGLI_PATRICIA_FOREACH (tv, & state, src->dict) { if ((copied = tuple_copy_value (tv)) != NULL) - mowgli_dictionary_add (dst->dict, state.cur->key, copied); + mowgli_patricia_add (dst->dict, copied->name, copied); } /* Copy subtune number information */ @@ -386,7 +377,7 @@ tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, Tuple return NULL; } } else { - value = mowgli_dictionary_retrieve(tuple->dict, tfield); + value = mowgli_patricia_retrieve(tuple->dict, tfield); } if (value != NULL) { @@ -399,15 +390,45 @@ tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, Tuple /* Allocate a new value */ value = mowgli_heap_alloc(tuple_value_heap); value->type = ftype; + if (nfield >= 0) + { + value->name[0] = 0; tuple->values[nfield] = value; + } else - mowgli_dictionary_add(tuple->dict, tfield, value); + { + g_strlcpy (value->name, tfield, TUPLE_NAME_MAX); + mowgli_patricia_add(tuple->dict, tfield, value); + } } return value; } +static gboolean set_string (Tuple * tuple, const gint nfield, + const gchar * field, gchar * string, gboolean take) +{ + TUPLE_LOCK_WRITE (); + + TupleValue * value = tuple_associate_data (tuple, nfield, field, + TUPLE_STRING); + if (! value) + { + if (take) + g_free (string); + return FALSE; + } + + if (! string) + value->value.string = NULL; + else + value->value.string = stringpool_get (string, take); + + TUPLE_UNLOCK_WRITE (); + return TRUE; +} + /** * Associates copy of given string to a field in specified #Tuple. * If field already exists, old value is freed and replaced. @@ -421,29 +442,18 @@ tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, Tuple * @param[in] string String to be associated to given field in Tuple. * @return TRUE if operation was succesful, FALSE if not. */ -gboolean -tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, const gchar *string) -{ - TupleValue *value; - TUPLE_LOCK_WRITE(); - if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_STRING)) == NULL) - return FALSE; - - if (string == NULL) - value->value.string = NULL; - else if (g_utf8_validate (string, -1, NULL)) - value->value.string = stringpool_get (string); - else +gboolean tuple_associate_string (Tuple * tuple, const gint nfield, + const gchar * field, const gchar * string) +{ + if (string && ! g_utf8_validate (string, -1, NULL)) { fprintf (stderr, "Invalid UTF-8: %s.\n", string); - gchar * copy = str_to_utf8 (string); - value->value.string = stringpool_get (copy); - g_free (copy); + return set_string (tuple, nfield, field, str_to_utf8 (string), TRUE); } - TUPLE_UNLOCK_WRITE(); - return TRUE; + gboolean ret = set_string (tuple, nfield, field, (gchar *) string, FALSE); + return ret; } /** @@ -464,9 +474,15 @@ tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, cons gboolean tuple_associate_string_rel (Tuple * tuple, const gint nfield, const gchar * field, gchar * string) { - gboolean ret = tuple_associate_string (tuple, nfield, field, string); - g_free (string); - return ret; + if (string && ! g_utf8_validate (string, -1, NULL)) + { + fprintf (stderr, "Invalid UTF-8: %s.\n", string); + gchar * copy = str_to_utf8 (string); + g_free (string); + string = copy; + } + + return set_string (tuple, nfield, field, string, TRUE); } /** @@ -521,24 +537,15 @@ tuple_disassociate(Tuple *tuple, const gint cnfield, const gchar *field) TUPLE_LOCK_WRITE(); if (nfield < 0) /* why _delete()? because _delete() returns the dictnode's data on success */ - value = mowgli_dictionary_delete(tuple->dict, field); + value = mowgli_patricia_delete(tuple->dict, field); else { value = tuple->values[nfield]; tuple->values[nfield] = NULL; } - if (value == NULL) { - TUPLE_UNLOCK_WRITE(); - return; - } + if (value) + tuple_value_destroy (value); - /* Free associated data */ - if (value->type == TUPLE_STRING) { - stringpool_unref(value->value.string); - value->value.string = NULL; - } - - mowgli_heap_free(tuple_value_heap, value); TUPLE_UNLOCK_WRITE(); } @@ -567,7 +574,7 @@ TupleValueType tuple_get_value_type (const Tuple * tuple, gint cnfield, TUPLE_LOCK_READ(); if (nfield < 0) { TupleValue *value; - if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) != NULL) + if ((value = mowgli_patricia_retrieve(tuple->dict, field)) != NULL) type = value->type; } else { if (tuple->values[nfield]) @@ -603,7 +610,7 @@ const gchar * tuple_get_string (const Tuple * tuple, gint cnfield, const gchar * TUPLE_LOCK_READ(); if (nfield < 0) - value = mowgli_dictionary_retrieve(tuple->dict, field); + value = mowgli_patricia_retrieve(tuple->dict, field); else value = tuple->values[nfield]; @@ -644,7 +651,7 @@ gint tuple_get_int (const Tuple * tuple, gint cnfield, const gchar * field) TUPLE_LOCK_READ(); if (nfield < 0) - value = mowgli_dictionary_retrieve(tuple->dict, field); + value = mowgli_patricia_retrieve(tuple->dict, field); else value = tuple->values[nfield]; @@ -659,3 +666,38 @@ gint tuple_get_int (const Tuple * tuple, gint cnfield, const gchar * field) return 0; } } + +#define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \ + __VA_ARGS__) + +void tuple_set_format (Tuple * t, const gchar * format, gint chans, gint rate, + gint brate) +{ + if (format) + tuple_associate_string (t, FIELD_CODEC, NULL, format); + + gchar buf[32]; + buf[0] = 0; + + if (chans > 0) + { + if (chans == 1) + APPEND (buf, _("Mono")); + else if (chans == 2) + APPEND (buf, _("Stereo")); + else + APPEND (buf, _("%d channels"), chans); + + if (rate > 0) + APPEND (buf, ", "); + } + + if (rate > 0) + APPEND (buf, "%d kHz", rate / 1000); + + if (buf[0]) + tuple_associate_string (t, FIELD_QUALITY, NULL, buf); + + if (brate > 0) + tuple_associate_int (t, FIELD_BITRATE, NULL, brate); +} diff --git a/src/libaudcore/tuple.h b/src/libaudcore/tuple.h index 3ef8ca4..bb277bf 100644 --- a/src/libaudcore/tuple.h +++ b/src/libaudcore/tuple.h @@ -80,7 +80,7 @@ enum { FIELD_GAIN_GAIN_UNIT, FIELD_GAIN_PEAK_UNIT, - FIELD_COMPOSER, /**< Composer of song, if different than artist. */ + FIELD_COMPOSER, /**< Composer of song, if different than artist. */ /* Special field, must always be last */ FIELD_LAST @@ -99,7 +99,10 @@ typedef struct { extern const TupleBasicType tuple_fields[FIELD_LAST]; +#define TUPLE_NAME_MAX 20 + typedef struct { + gchar name[TUPLE_NAME_MAX]; /* for standard fields, the empty string */ TupleValueType type; union { gchar *string; @@ -113,7 +116,7 @@ typedef struct { */ typedef struct _Tuple { mowgli_object_t parent; - mowgli_dictionary_t *dict; /**< Mowgli dictionary for holding other than basic values. */ + mowgli_patricia_t *dict; /**< Mowgli dictionary for holding other than basic values. */ TupleValue *values[FIELD_LAST]; /**< Basic #Tuple values, entry is NULL if not set. */ gint nsubtunes; /**< Number of subtunes, if any. Values greater than 0 mean that there are subtunes and #subtunes array @@ -140,6 +143,15 @@ const gchar * tuple_get_string (const Tuple * tuple, gint nfield, const gchar * gint tuple_get_int (const Tuple * tuple, gint nfield, const gchar * field); #define tuple_free(x) mowgli_object_unref(x); +/* Fills in format-related fields (specifically FIELD_CODEC, FIELD_QUALITY, and + * FIELD_BITRATE. Plugins should use this function instead of setting these + * fields individually so that the style is consistent across file formats. + * <format> should be a brief description such as "Microsoft WAV", "MPEG-1 layer + * 3", "Audio CD", and so on. <samplerate> is in Hertz. <bitrate> is in 1000 + * bits per second. */ +void tuple_set_format (Tuple * tuple, const gchar * format, gint channels, gint + samplerate, gint bitrate); + G_END_DECLS #endif /* AUDACIOUS_TUPLE_H */ diff --git a/src/libaudcore/tuple_compiler.c b/src/libaudcore/tuple_compiler.c index 18f84a5..c930f5b 100644 --- a/src/libaudcore/tuple_compiler.c +++ b/src/libaudcore/tuple_compiler.c @@ -580,7 +580,7 @@ static TupleValue * tf_get_fieldref (TupleEvalVar * var, const Tuple * tuple) { if (var->type == TUPLE_VAR_FIELD && var->fieldref == NULL) { if (var->fieldidx < 0) - var->fieldref = mowgli_dictionary_retrieve(tuple->dict, var->name); + var->fieldref = mowgli_patricia_retrieve(tuple->dict, var->name); else var->fieldref = tuple->values[var->fieldidx]; } @@ -819,7 +819,7 @@ static gboolean tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode * gchar * tuple_formatter_eval (TupleEvalContext * ctx, TupleEvalNode * expr, const Tuple * tuple) { - gchar *res = g_strdup(""); + gchar *res = NULL; gssize resmax = 0, reslen = 0; assert(ctx != NULL); assert(tuple != NULL); diff --git a/src/libaudcore/tuple_formatter.c b/src/libaudcore/tuple_formatter.c index 1124b38..4738ecb 100644 --- a/src/libaudcore/tuple_formatter.c +++ b/src/libaudcore/tuple_formatter.c @@ -42,7 +42,7 @@ #ifdef TUPLE_USE_COMPILER # include "tuple_compiler.h" -static GStaticRWLock tuplec_rwlock = G_STATIC_RW_LOCK_INIT; +static GStaticMutex tuplec_mutex = G_STATIC_MUTEX_INIT; #endif #ifdef _DEBUG @@ -544,7 +544,8 @@ gchar * tuple_formatter_process_string (const Tuple * tuple, const gchar * strin } #ifdef TUPLE_USE_COMPILER - g_static_rw_lock_writer_lock(&tuplec_rwlock); + g_static_mutex_lock (& tuplec_mutex); + if (last_string == NULL || (last_string != NULL && strcmp(last_string, string))) { @@ -576,14 +577,13 @@ gchar * tuple_formatter_process_string (const Tuple * tuple, const gchar * strin #endif tuple_evalctx_reset(last_ctx); - g_static_rw_lock_writer_unlock(&tuplec_rwlock); - g_static_rw_lock_reader_lock(&tuplec_rwlock); result = tuple_formatter_eval(last_ctx, last_ev, tuple); if (last_ctx->iserror) { g_warning("[TuplezEV]: %s", last_ctx->errmsg); } - g_static_rw_lock_reader_unlock(&tuplec_rwlock); + + g_static_mutex_unlock (& tuplec_mutex); return result; #else diff --git a/src/libaudcore/vfs.c b/src/libaudcore/vfs.c index 09a5302..b05b47e 100644 --- a/src/libaudcore/vfs.c +++ b/src/libaudcore/vfs.c @@ -17,6 +17,8 @@ * Audacious or using our public API to be a derived work. */ +#include <inttypes.h> + #include "vfs.h" #include "audstrings.h" #include <stdio.h> @@ -25,49 +27,120 @@ #include <sys/types.h> #include <string.h> +#include <mowgli.h> -/** - * GList of #VFSConstructor objects holding all the registered - * VFS transports. - */ -GList *vfs_transports = NULL; /* temporary. -nenolod */ +/* Audacious core provides us with a function that looks up a VFS transport for + * a given URI scheme. Since this function will load plugins as needed, it can + * only be called from the main thread. When VFS is used from parallel threads, + * vfs_prepare must be called from the main thread to look up any needed + * transports beforehand. */ +typedef struct { + VFSConstructor * transport; + gboolean prepared; +} LookupNode; -/** - * Registers a #VFSConstructor vtable with the VFS system. - * - * @param vtable The #VFSConstructor vtable to register. - */ -void vfs_register_transport (VFSConstructor * vtable) +static GStaticMutex mutex = G_STATIC_MUTEX_INIT; +static GThread * lookup_thread = NULL; +static VFSConstructor * (* lookup_func) (const gchar * scheme) = NULL; +static mowgli_patricia_t * lookup_table = NULL; + +void vfs_set_lookup_func (VFSConstructor * (* func) (const gchar * scheme)) { - vfs_transports = g_list_append (vfs_transports, vtable); + g_static_mutex_lock (& mutex); + + lookup_thread = g_thread_self (); + lookup_func = func; + + if (! lookup_table) + lookup_table = mowgli_patricia_create (NULL); + + g_static_mutex_unlock (& mutex); } -static VFSConstructor * -vfs_get_constructor(const gchar *path) +static VFSConstructor * do_lookup (const gchar * scheme, gboolean prepare) { - VFSConstructor *vtable = NULL; - GList *node; + g_return_val_if_fail (lookup_thread && lookup_func && lookup_table, NULL); - if (path == NULL) - return NULL; + LookupNode * node = mowgli_patricia_retrieve (lookup_table, scheme); + if (! node) + { + node = g_slice_new (LookupNode); + node->transport = NULL; + node->prepared = FALSE; + mowgli_patricia_add (lookup_table, scheme, node); + } - for (node = vfs_transports; node != NULL; node = g_list_next(node)) + if (prepare) + node->prepared = TRUE; + + /* vfs_prepare can only be called from the main thread. vfs_fopen can only + * be called from the main thread unless vfs_prepare has been called. */ + if (prepare || ! node->prepared) + g_return_val_if_fail (g_thread_self () == lookup_thread, NULL); + + /* do the actual lookup if needed, but only in the main thread */ + if (! node->transport && g_thread_self () == lookup_thread) { - VFSConstructor *vtptr = (VFSConstructor *) node->data; + node->transport = lookup_func (scheme); + /* This is normal for custom URI schemes. + if (! node->transport) + fprintf (stderr, "No transport plugin found for URI scheme \"%s\".\n", scheme); */ + } + + return node->transport; +} + +void vfs_prepare (const gchar * scheme) +{ + g_static_mutex_lock (& mutex); + do_lookup (scheme, TRUE); + g_static_mutex_unlock (& mutex); +} - if (!strncasecmp(path, vtptr->uri_id, strlen(vtptr->uri_id))) +void vfs_prepare_filename (const gchar * path) +{ + const gchar * s = strstr (path, "://"); + g_return_if_fail (s); + gchar scheme[s - path + 1]; + strncpy (scheme, path, s - path); + scheme[s - path] = 0; + + vfs_prepare (scheme); +} + +static gboolean verbose = FALSE; + +void vfs_set_verbose (gboolean set) +{ + verbose = set; +} + +static void logger (const gchar * format, ...) +{ + static gchar last[256] = ""; + static gint repeated = 0; + + gchar buf[256]; + + va_list args; + va_start (args, format); + vsnprintf (buf, sizeof buf, format, args); + va_end (args); + + if (! strcmp (buf, last)) + repeated ++; + else + { + if (repeated) { - vtable = vtptr; - break; + printf ("VFS: (last message repeated %d times)\n", repeated); + repeated = 0; } + + fputs (buf, stdout); + strcpy (last, buf); } - - /* No transport vtable has been registered, bail. */ - if (vtable == NULL) - g_warning("Could not open '%s', no transport plugin available.", path); - - return vtable; } /** @@ -82,20 +155,37 @@ VFSFile * vfs_fopen(const gchar * path, const gchar * mode) { + g_return_val_if_fail (path && mode, NULL); + g_return_val_if_fail (lookup_func, NULL); + VFSFile *file; VFSConstructor *vtable = NULL; - if (path == NULL || mode == NULL || (vtable = vfs_get_constructor(path)) == NULL) + const gchar * s = strstr (path, "://"); + g_return_val_if_fail (s, NULL); + gchar scheme[s - path + 1]; + strncpy (scheme, path, s - path); + scheme[s - path] = 0; + + g_static_mutex_lock (& mutex); + vtable = do_lookup (scheme, FALSE); + g_static_mutex_unlock (& mutex); + + if (! vtable) return NULL; file = vtable->vfs_fopen_impl(path, mode); + if (verbose) + logger ("VFS: <%p> open (mode %s) %s\n", file, mode, path); + if (file == NULL) return NULL; file->uri = g_strdup(path); file->base = vtable; file->ref = 1; + file->sig = VFS_SIG; return file; } @@ -109,10 +199,12 @@ vfs_fopen(const gchar * path, gint vfs_fclose(VFSFile * file) { - gint ret = 0; + g_return_val_if_fail (file && file->sig == VFS_SIG, -1); - if (file == NULL) - return -1; + if (verbose) + printf ("VFS: <%p> close\n", file); + + gint ret = 0; if (--file->ref > 0) return -1; @@ -121,7 +213,9 @@ vfs_fclose(VFSFile * file) ret = -1; g_free(file->uri); - g_free(file); + + memset (file, 0, sizeof (VFSFile)); + g_free (file); return ret; } @@ -137,10 +231,15 @@ vfs_fclose(VFSFile * file) */ gint64 vfs_fread (void * ptr, gint64 size, gint64 nmemb, VFSFile * file) { - if (file == NULL) - return 0; + g_return_val_if_fail (file && file->sig == VFS_SIG, 0); + + gint64 readed = file->base->vfs_fread_impl (ptr, size, nmemb, file); + + if (verbose) + logger ("VFS: <%p> read %"PRId64" elements of size %"PRId64" = " + "%"PRId64"\n", file, nmemb, size, readed); - return file->base->vfs_fread_impl(ptr, size, nmemb, file); + return readed; } /** @@ -154,23 +253,30 @@ gint64 vfs_fread (void * ptr, gint64 size, gint64 nmemb, VFSFile * file) */ gint64 vfs_fwrite (const void * ptr, gint64 size, gint64 nmemb, VFSFile * file) { - if (file == NULL) - return 0; + g_return_val_if_fail (file && file->sig == VFS_SIG, 0); + + gint64 written = file->base->vfs_fwrite_impl (ptr, size, nmemb, file); + + if (verbose) + logger ("VFS: <%p> write %"PRId64" elements of size %"PRId64" = " + "%"PRId64"\n", file, nmemb, size, written); - return file->base->vfs_fwrite_impl(ptr, size, nmemb, file); + return written; } /** * Reads a character from a VFS stream. * * @param file #VFSFile object that represents the VFS stream. - * @return On success, a character. Otherwise, -1. + * @return On success, a character. Otherwise, EOF. */ gint vfs_getc(VFSFile *file) { - if (file == NULL) - return -1; + g_return_val_if_fail (file && file->sig == VFS_SIG, EOF); + + if (verbose) + logger ("VFS: <%p> getc\n", file); return file->base->vfs_getc_impl(file); } @@ -180,13 +286,15 @@ vfs_getc(VFSFile *file) * * @param c The character to push back. * @param file #VFSFile object that represents the VFS stream. - * @return On success, 0. Otherwise, -1. + * @return On success, 0. Otherwise, EOF. */ gint vfs_ungetc(gint c, VFSFile *file) { - if (file == NULL) - return -1; + g_return_val_if_fail (file && file->sig == VFS_SIG, EOF); + + if (verbose) + logger ("VFS: <%p> ungetc\n", file); return file->base->vfs_ungetc_impl(c, file); } @@ -209,8 +317,12 @@ vfs_fseek(VFSFile * file, gint64 offset, gint whence) { - if (file == NULL) - return 0; + g_return_val_if_fail (file && file->sig == VFS_SIG, -1); + + if (verbose) + logger ("VFS: <%p> seek to %"PRId64" from %s\n", file, offset, whence == + SEEK_CUR ? "current" : whence == SEEK_SET ? "beginning" : whence == + SEEK_END ? "end" : "invalid"); return file->base->vfs_fseek_impl(file, offset, whence); } @@ -223,8 +335,10 @@ vfs_fseek(VFSFile * file, void vfs_rewind(VFSFile * file) { - if (file == NULL) - return; + g_return_if_fail (file && file->sig == VFS_SIG); + + if (verbose) + logger ("VFS: <%p> rewind\n", file); file->base->vfs_rewind_impl(file); } @@ -235,13 +349,17 @@ vfs_rewind(VFSFile * file) * @param file #VFSFile object that represents the VFS stream. * @return On success, the current position. Otherwise, -1. */ -glong +gint64 vfs_ftell(VFSFile * file) { - if (file == NULL) - return -1; + g_return_val_if_fail (file && file->sig == VFS_SIG, -1); + + gint64 told = file->base->vfs_ftell_impl (file); + + if (verbose) + logger ("VFS: <%p> tell = %"PRId64"\n", file, told); - return file->base->vfs_ftell_impl(file); + return told; } /** @@ -253,10 +371,14 @@ vfs_ftell(VFSFile * file) gboolean vfs_feof(VFSFile * file) { - if (file == NULL) - return FALSE; + g_return_val_if_fail (file && file->sig == VFS_SIG, TRUE); + + gboolean eof = file->base->vfs_feof_impl (file); - return (gboolean) file->base->vfs_feof_impl(file); + if (verbose) + logger ("VFS: <%p> eof = %s\n", file, eof ? "yes" : "no"); + + return eof; } /** @@ -268,8 +390,10 @@ vfs_feof(VFSFile * file) */ gint vfs_ftruncate (VFSFile * file, gint64 length) { - if (file == NULL) - return -1; + g_return_val_if_fail (file && file->sig == VFS_SIG, -1); + + if (verbose) + logger ("VFS: <%p> truncate to %"PRId64"\n", file, length); return file->base->vfs_ftruncate_impl(file, length); } @@ -280,13 +404,16 @@ gint vfs_ftruncate (VFSFile * file, gint64 length) * @param file #VFSFile object that represents the VFS stream. * @return On success, the size of the file in bytes. Otherwise, -1. */ -gint64 -vfs_fsize(VFSFile * file) +gint64 vfs_fsize (VFSFile * file) { - if (file == NULL) - return -1; + g_return_val_if_fail (file && file->sig == VFS_SIG, -1); + + gint64 size = file->base->vfs_fsize_impl (file); - return file->base->vfs_fsize_impl(file); + if (verbose) + logger ("VFS: <%p> size = %"PRId64"\n", file, size); + + return size; } /** @@ -380,16 +507,7 @@ vfs_dup(VFSFile *in) gboolean vfs_is_remote(const gchar * path) { - VFSConstructor *vtable = NULL; - - if (path == NULL || (vtable = vfs_get_constructor(path)) == NULL) - return FALSE; - - /* check if vtable->uri_id is file:// or not, for now. */ - if (!strncasecmp("file://", vtable->uri_id, strlen(vtable->uri_id))) - return FALSE; - else - return TRUE; + return strncasecmp (path, "file://", 7) ? TRUE : FALSE; } /** diff --git a/src/libaudcore/vfs.h b/src/libaudcore/vfs.h index da39157..b9d2cca 100644 --- a/src/libaudcore/vfs.h +++ b/src/libaudcore/vfs.h @@ -36,7 +36,9 @@ G_BEGIN_DECLS /** @struct VFSFile */ typedef struct _VFSFile VFSFile; /** @struct VFSConstructor */ -typedef struct _VFSConstructor VFSConstructor; +typedef const struct _VFSConstructor VFSConstructor; + +#define VFS_SIG ('V' | ('F' << 8) | ('S' << 16)) /** * @struct _VFSFile @@ -48,6 +50,7 @@ struct _VFSFile { gpointer handle; /**< Opaque data used by the transport plugins */ VFSConstructor *base; /**< The base vtable used for VFS functions */ gint ref; /**< The amount of references that the VFSFile object has */ + gint sig; /**< Used to detect invalid or twice-closed objects */ }; /** @@ -57,9 +60,6 @@ struct _VFSFile { * nature. VFS base vtables are registered via vfs_register_transport(). */ struct _VFSConstructor { - /** The URI identifier, e.g. "file" would handle "file://" streams. */ - gchar * uri_id; - /** A function pointer which points to a fopen implementation. */ VFSFile * (* vfs_fopen_impl) (const gchar * filename, const gchar * mode); /** A function pointer which points to a fclose implementation. */ @@ -118,7 +118,7 @@ gint vfs_fprintf (VFSFile * stream, gchar const * format, ...) __attribute__ gint vfs_fseek (VFSFile * file, gint64 offset, gint whence) WARN_RETURN; void vfs_rewind (VFSFile * file); -glong vfs_ftell (VFSFile * file) WARN_RETURN; +gint64 vfs_ftell (VFSFile * file) WARN_RETURN; gint64 vfs_fsize (VFSFile * file) WARN_RETURN; gint vfs_ftruncate (VFSFile * file, gint64 length) WARN_RETURN; @@ -146,7 +146,11 @@ gboolean vfs_is_remote (const gchar * path) WARN_RETURN; void vfs_file_get_contents (const gchar * filename, void * * buf, gint64 * size); -void vfs_register_transport (VFSConstructor * vtable); +void vfs_set_lookup_func (VFSConstructor * (* func) (const gchar * scheme)); +void vfs_prepare (const gchar * scheme); +void vfs_prepare_filename (const gchar * filename); + +void vfs_set_verbose (gboolean verbose); #undef WARN_RETURN diff --git a/src/libaudcore/vfs_async.c b/src/libaudcore/vfs_async.c index b4e462f..cb4acd5 100644 --- a/src/libaudcore/vfs_async.c +++ b/src/libaudcore/vfs_async.c @@ -56,6 +56,8 @@ vfs_async_file_get_contents_worker(gpointer data) void vfs_async_file_get_contents(const gchar *filename, VFSConsumer cons_f, gpointer userdata) { + vfs_prepare_filename (filename); + VFSAsyncTrampoline *tr; tr = g_slice_new0(VFSAsyncTrampoline); diff --git a/src/libaudcore/vfs_buffer.c b/src/libaudcore/vfs_buffer.c index e023e67..33240f3 100644 --- a/src/libaudcore/vfs_buffer.c +++ b/src/libaudcore/vfs_buffer.c @@ -194,7 +194,6 @@ buffer_vfs_fsize_impl(VFSFile * file) } static VFSConstructor buffer_const = { - NULL, // not a normal VFS class buffer_vfs_fopen_impl, buffer_vfs_fclose_impl, buffer_vfs_fread_impl, @@ -236,6 +235,7 @@ vfs_buffer_new(gpointer data, gsize size) handle->handle = buffer; handle->base = &buffer_const; handle->ref = 1; + handle->sig = VFS_SIG; return handle; } diff --git a/src/libaudcore/vfs_buffered_file.c b/src/libaudcore/vfs_buffered_file.c index 2264e15..064d697 100644 --- a/src/libaudcore/vfs_buffered_file.c +++ b/src/libaudcore/vfs_buffered_file.c @@ -199,7 +199,6 @@ buffered_file_vfs_metadata_impl(VFSFile * file, const gchar * field) } VFSConstructor buffered_file_const = { - NULL, // not a normal VFS class buffered_file_vfs_fopen_impl, buffered_file_vfs_fclose_impl, buffered_file_vfs_fread_impl, @@ -263,6 +262,7 @@ vfs_buffered_file_new_from_uri(const gchar *uri) handle->base = &buffered_file_const; handle->uri = g_strdup(uri); handle->ref = 1; + handle->sig = VFS_SIG; return handle; } diff --git a/src/libaudgui/Makefile b/src/libaudgui/Makefile index 448c353..cbf6678 100644 --- a/src/libaudgui/Makefile +++ b/src/libaudgui/Makefile @@ -9,7 +9,7 @@ SRCS = confirm.c \ infopopup.c \ infowin.c \ init.c \ - library-store.c \ + list.c \ ui_gtk.c \ ui_fileopener.c \ ui_urlopener.c \ @@ -23,14 +23,15 @@ SRCS = confirm.c \ util.c INCLUDES = libaudgui.h \ - libaudgui-gtk.h + libaudgui-gtk.h \ + list.h include ../../buildsys.mk include ../../extra.mk includesubdir = libaudgui -CPPFLAGS += -DHAVE_CONFIG_H ${LIB_CPPFLAGS} -I.. -I../.. ${GLIB_CFLASG} ${GTK_CFLAGS} ${DBUS_CFLAGS} ${LIBMCS_CFLAGS} ${AUDACIOUS_DEFINES} ${REGEX_CFLAGS} +CPPFLAGS += -DHAVE_CONFIG_H -I.. -I../.. ${GLIB_CFLASG} ${GTK_CFLAGS} ${DBUS_CFLAGS} ${LIBMCS_CFLAGS} ${AUDACIOUS_DEFINES} ${REGEX_CFLAGS} CFLAGS += -std=gnu99 ${LIB_CFLAGS} LDFLAGS += $(AUDLDFLAGS) LIBS += -lm ${GLIB_LIBS} ${MOWGLI_LIBS} ${GTK_LIBS} ${REGEX_LIBS} -L../libaudcore -laudcore diff --git a/src/libaudgui/audacious_logo.xpm b/src/libaudgui/audacious_logo.xpm deleted file mode 100644 index 4cacca2..0000000 --- a/src/libaudgui/audacious_logo.xpm +++ /dev/null @@ -1,454 +0,0 @@ -/* XPM */ -static char * audacious_logo_xpm[] = { -"488 200 251 2", -" c #FEFEFE", -". c #FDFDFD", -"+ c #FCFCFC", -"@ c #FBFBFB", -"# c #FAFAFA", -"$ c #F9F9F9", -"% c #F8F8F8", -"& c #F7F7F7", -"* c #F6F6F6", -"= c #F5F5F5", -"- c #F4F4F4", -"; c #F3F3F3", -"> c #F2F2F2", -", c #F1F1F1", -"' c #EFEFEF", -") c #EEEEEE", -"! c #EDEDED", -"~ c #ECECEC", -"{ c #EBEBEB", -"] c #F0F0F0", -"^ c #E9E9E9", -"/ c #E7E7E7", -"( c #E5E5E5", -"_ c #E4E4E4", -": c #E3E3E3", -"< c #E2E2E2", -"[ c #E1E1E1", -"} c #E0E0E0", -"| c #EAEAEA", -"1 c #E6E6E6", -"2 c #DFDFDF", -"3 c #DCDCDC", -"4 c #DADADA", -"5 c #D8D8D8", -"6 c #D6D6D6", -"7 c #D5D5D5", -"8 c #D4D4D4", -"9 c #D3D3D3", -"0 c #D7D7D7", -"a c #CFCFCF", -"b c #CCCCCC", -"c c #CACACA", -"d c #C8C8C8", -"e c #C6C6C6", -"f c #C5C5C5", -"g c #C4C4C4", -"h c #C3C3C3", -"i c #C2C2C2", -"j c #C7C7C7", -"k c #D1D1D1", -"l c #CBCBCB", -"m c #BFBFBF", -"n c #BCBCBC", -"o c #B8B8B8", -"p c #B5B5B5", -"q c #B4B4B4", -"r c #B1B1B1", -"s c #B0B0B0", -"t c #AFAFAF", -"u c #CDCDCD", -"v c #AEAEAE", -"w c #A9A9A9", -"x c #A4A4A4", -"y c #A2A2A2", -"z c #9F9F9F", -"A c #9C9C9C", -"B c #9B9B9B", -"C c #9A9A9A", -"D c #999999", -"E c #CECECE", -"F c #B7B7B7", -"G c #ADADAD", -"H c #A5A5A5", -"I c #9D9D9D", -"J c #969696", -"K c #919191", -"L c #8E8E8E", -"M c #8A8A8A", -"N c #888888", -"O c #878787", -"P c #868686", -"Q c #858585", -"R c #848484", -"S c #838383", -"T c #ABABAB", -"U c #959595", -"V c #8B8B8B", -"W c #7D7D7D", -"X c #797979", -"Y c #767676", -"Z c #727272", -"` c #717171", -" . c #707070", -".. c #6F6F6F", -"+. c #6D6D6D", -"@. c #757575", -"#. c #7C7C7C", -"$. c #C9C9C9", -"%. c #BBBBBB", -"&. c #929292", -"*. c #7B7B7B", -"=. c #6C6C6C", -"-. c #656565", -";. c #616161", -">. c #5E5E5E", -",. c #5C5C5C", -"'. c #5B5B5B", -"). c #5A5A5A", -"!. c #585858", -"~. c #575757", -"{. c #7A7A7A", -"]. c #D2D2D2", -"^. c #636363", -"/. c #555555", -"(. c #515151", -"_. c #4D4D4D", -":. c #4B4B4B", -"<. c #4A4A4A", -"[. c #494949", -"}. c #484848", -"|. c #474747", -"1. c #464646", -"2. c #505050", -"3. c #5F5F5F", -"4. c #545454", -"5. c #4C4C4C", -"6. c #454545", -"7. c #404040", -"8. c #3C3C3C", -"9. c #3B3B3B", -"0. c #3A3A3A", -"a. c #393939", -"b. c #383838", -"c. c #363636", -"d. c #353535", -"e. c #444444", -"f. c #BABABA", -"g. c #333333", -"h. c #2F2F2F", -"i. c #2D2D2D", -"j. c #2C2C2C", -"k. c #2B2B2B", -"l. c #2A2A2A", -"m. c #282828", -"n. c #7E7E7E", -"o. c #6B6B6B", -"p. c #262626", -"q. c #242424", -"r. c #222222", -"s. c #212121", -"t. c #202020", -"u. c #1E1E1E", -"v. c #1D1D1D", -"w. c #292929", -"x. c #373737", -"y. c #A1A1A1", -"z. c #8C8C8C", -"A. c #787878", -"B. c #666666", -"C. c #535353", -"D. c #303030", -"E. c #1B1B1B", -"F. c #191919", -"G. c #181818", -"H. c #171717", -"I. c #161616", -"J. c #151515", -"K. c #B3B3B3", -"L. c #898989", -"M. c #4E4E4E", -"N. c #3F3F3F", -"O. c #131313", -"P. c #121212", -"Q. c #111111", -"R. c #0F0F0F", -"S. c #0E0E0E", -"T. c #B2B2B2", -"U. c #737373", -"V. c #3D3D3D", -"W. c #313131", -"X. c #141414", -"Y. c #101010", -"Z. c #0C0C0C", -"`. c #0B0B0B", -" + c #272727", -".+ c #2E2E2E", -"++ c #1A1A1A", -"@+ c #0A0A0A", -"#+ c #080808", -"$+ c #070707", -"%+ c #090909", -"&+ c #060606", -"*+ c #050505", -"=+ c #0D0D0D", -"-+ c #040404", -";+ c #020202", -">+ c #010101", -",+ c #000000", -"'+ c #4F4F4F", -")+ c #C1C1C1", -"!+ c #A7A7A7", -"~+ c #696969", -"{+ c #D9D9D9", -"]+ c #808080", -"^+ c #979797", -"/+ c #B6B6B6", -"(+ c #A8A8A8", -"_+ c #D0D0D0", -":+ c #A3A3A3", -"<+ c #7F7F7F", -"[+ c #949494", -"}+ c #686868", -"|+ c #030303", -"1+ c #AAAAAA", -"2+ c #323232", -"3+ c #909090", -"4+ c #676767", -"5+ c #9E9E9E", -"6+ c #252525", -"7+ c #1F1F1F", -"8+ c #BDBDBD", -"9+ c #6E6E6E", -"0+ c #595959", -"a+ c #ACACAC", -"b+ c #818181", -"c+ c #565656", -"d+ c #8D8D8D", -"e+ c #939393", -"f+ c #A6A6A6", -"g+ c #525252", -"h+ c #232323", -"i+ c #424242", -"j+ c #B9B9B9", -"k+ c #989898", -"l+ c #343434", -"m+ c #414141", -"n+ c #1C1C1C", -"o+ c #777777", -"p+ c #BEBEBE", -"q+ c #6A6A6A", -"r+ c #8F8F8F", -"s+ c #A0A0A0", -"t+ c #C0C0C0", -"u+ c #434343", -"v+ c #747474", -"w+ c #3E3E3E", -"x+ c #646464", -"y+ c #626262", -"z+ c #606060", -"A+ c #828282", -"B+ c #5D5D5D", -" . + @ # $ % & * = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = * & % $ # @ + . ", -" . + # $ & = - > , ' ) ) ! ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ! ) ) ' , > - = & $ # + . ", -" . @ # % = ; ] ! { ^ / ( _ : : < < [ [ [ [ [ [ [ } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } [ [ [ [ [ [ < < < : _ ( / ^ { ! ] ; = % # @ . ", -". + # & - , ! | 1 < 2 3 4 5 6 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 7 7 6 5 4 3 2 < 1 | ! , - & # + . ", -"+ # % - , ~ / [ 3 0 9 a b c d e f g g h h h h h h i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i h h h h h g g f e j c b a 9 0 3 [ / ~ ] - % # + ", -"@ $ = , ~ 1 2 5 k l g m n o p q r r s s s t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t s s r r q p o n m g l k 5 2 ( { , = $ @ ", -"# & ; ! / 2 6 u g n p v w x y z A A B B C C C C C D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D C C C C C B A A z y x w v p n g u 6 2 / ! ; & # ", -"$ = ] ^ [ 5 E i F G H I J K L M N O P P Q R R R R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R R R R P P O N M L K J I x G F i E 5 [ ^ ] = $ ", -"% - ) 1 3 k g F T z U V R W X Y Z ` . ...+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+. . .` Z @.X #.R V U z T F g k 3 1 ) - % ", -"& > { < 0 $.%.G z &.Q *.Z =.-.;.>.,.'.'.).!.!.!.!.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.!.!.!.!.'.'.,.>.;.-.=.Z {.Q K z G %.$.0 < { > & ", -"* , ^ 2 ].f q H U P X +.^.,./.(._.:.<.[.}.|.|.|.|.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.|.|.|.|.[.<.:._.2./.'.^.+.X P U H q f ].2 ^ ] * ", -"= ' / 3 a m v B V {.=.3.4.5.6.7.8.9.0.a.b.c.c.c.c.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.c.c.c.c.a.0.9.8.7.e.5.4.3.=.{.V B v m a 3 / ' = ", -"= ) ( 4 l f.w J S Z ;.4.[.7.a.g.h.i.j.k.l.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.k.j.i.h.g.a.7.[.4.;.` S J w f.l 4 ( ) = ", -"- ! _ 5 $.F x K n.o.).5.7.b.h.l.p.q.r.s.t.u.u.u.u.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.u.u.u.u.s.r.q.p.w.h.x.7.5.).o.#.K x F $.5 _ ! - ", -"- ! : 6 j p y.z.A.B.C.6.0.D.m.r.v.E.F.G.H.I.I.I.I.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.I.I.I.I.G.F.E.v.r.m.D.a.6.C.B.A.z.y.q j 6 : ! - ", -"; ~ < 7 f K.z L.@.;.M.N.g.w.s.E.H.J.O.P.Q.R.R.R.R.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.R.R.R.R.P.O.J.H.E.s.w.g.N.M.;.@.L.z T.f 7 < ~ ; ", -"; ~ < 7 g T.I O U.3._.V.W.m.u.G.X.P.Y.R.S.Z.Z.Z.Z.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.Z.Z.Z.Z.R.Y.P.X.G.u. +W.V._.3.U.O I r g 7 < ~ ; ", -"; ~ [ 9 h s A O .,.[.9..+q.++J.Y.S.Z.`.@+#+#+#+#+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+#+#+#+#+`.Z.S.Y.J.++q.i.9.[.,. .Q A s h 9 [ ~ ; ", -"; ~ [ 9 h s B P .'.}.a.j.r.G.O.S.Z.@+%+#+&+&+&+&+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+&+&+&+&+%+@+Z.S.O.G.r.j.a.}.'. .Q C s h 9 [ ~ ; ", -"; ~ [ 9 i s C Q ..'.}.b.k.s.H.P.=+`.%+#+$+*+*+*+*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*+*+*+*+#+%+`.=+P.H.s.k.b.}.)...R C s i 9 [ ~ ; ", -"; ~ [ 9 i s C Q ..'.}.b.k.s.H.P.=+`.%+#+$+*+*+*+*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*+*+*+*+#+%+`.=+P.H.s.k.b.}.)...R C s i 9 [ ~ ; ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+F.'+#.A f.d ].6 l )+!+P ;.l.;+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+E.~+F {+{+{+{+{+{+{+{+{+{+{+{+{+{+$.]+.+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+k.&.5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+v e.;+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+++&.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+v .+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>.k {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 n.#+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+R.^+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r t.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+++/+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l W.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+++C.P (+g _+0 E )+:+<+}.Y.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+t.g {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k 9.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+&+4.(+{+{+{+{+{+{+{+{+{+{+{+{+6 [+9.>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+-+W.}+K r d k 6 b m y.]+5.X.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+G.m {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+_+j.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+|+~.h {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+1+2+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+m.W d {+{+{+{+{+{+{+{+{+{+{+{+{+y 2.*+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+E.C.R x )+u 0 k j s 3+4+h.|+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+%+K.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 s n {+{+{+{+{+{+{+{+{+{+{+{+j F.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+E.C.R x )+u 0 k j s 3+4+h.|+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+s.G {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+Q @+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+k.C {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+)+'.-+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+=+>.v {+{+{+{+{+{+{+{+{+{+{+{+{+j *.p.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+N {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5+r.,+,+ +y {+{+{+{+{+{+{+{+{+{+{+!+|+,+,+,+,+,+,+,+,+,+,+,+,+,+=+>.v {+{+{+{+{+{+{+{+{+{+{+{+{+j *.p.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+6+>.3+s $.9 6 l F J }+h.;+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+[._+{+{+{+{+{+{+{+{+{+u S *.f.{+{+{+{+{+{+{+{+{+{+F s.,+,+,+,+,+,+,+,+,+,+,+,+S.S 5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+K.W.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+7+!.M T e k 0 u 8+I @.8.#+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+Z.9+b {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+^+l.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+[.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+A.>+,+,+,+,+|+K {+{+{+{+{+{+{+{+{+{+{+o.,+,+,+,+,+,+,+,+,+,+,+Z.9+b {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+^+l.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+@+3.q {+{+{+{+{+{+{+{+{+{+{+{+8+~+S.,+,+,+,+,+,+,+,+,+,+,+,+,+>.{+{+{+{+{+{+{+{+{+{+y X.,+,+|+;.{+{+{+{+{+{+{+{+{+{+$.h.,+,+,+,+,+,+,+,+,+,+W.m {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 4+;+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+$+0+a+{+{+{+{+{+{+{+{+{+{+{+{+u b+6+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+[.g {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 n.=+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+$+b. .^+o c 7 8 $./+U +.d.*+,+,+,+,+,+,+,+,+,+,+,+,+,+,+=+g {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+8+*+,+,+,+,+,+,+=+a {+{+{+{+{+{+{+{+{+{+9 v.,+,+,+,+,+,+,+,+,+[.g {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 n.=+,+,+,+,+,+,+,+,+,+,+,+,+,+-+>.c {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+u ~+&+,+,+,+,+,+,+,+,+,+,+c+{+{+{+{+{+{+{+{+{+{+)+@+,+,+,+,+,+{.{+{+{+{+{+{+{+{+{+{+b +,+,+,+,+,+,+,+,+_.7 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+d+*+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+$+b. .^+o c 7 8 $./+U +.d.*+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+|+0+f {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 L ++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+%+N {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+n j.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+j.S u {+{+{+{+{+{+{+{+{+{+{+{+c n.p.,+,+,+,+,+,+,+,+,+,+,+,+Y {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+^.,+,+,+,+,+,+,+,+!+{+{+{+{+{+{+{+{+{+{+{+e+,+,+,+,+,+,+,+%+N {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+n j.,+,+,+,+,+,+,+,+,+,+,+q.r {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+o l.,+,+,+,+,+,+,+,+x.0 {+{+{+{+{+{+{+{+{+{+U ,+,+,+,+,+,+5.{+{+{+{+{+{+{+{+{+{+{+h O.,+,+,+,+,+,+0+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+C -+,+,+,+,+,+,+,+,+,+,+,+,+,+j.S u {+{+{+{+{+{+{+{+{+{+{+{+c n.p.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+r.v {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+_+>.>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+X.s {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+8 }.,+,+,+,+,+,+,+,+,+,+,+,+,+k.A {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+J 6+,+,+,+,+,+,+,+,+,+O.k {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+e.,+,+,+,+,+,+,+,+f+{+{+{+{+{+{+{+{+{+{+{+{+6+,+,+,+,+,+X.s {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+8 }.,+,+,+,+,+,+,+,+,+|.k {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+9 M.,+,+,+,+,+,+P.f {+{+{+{+{+{+{+{+{+{+{+_+m.,+,+,+,+>+:+{+{+{+{+{+{+{+{+{+{+{+{+5+>+,+,+,+,+5.5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+M >+,+,+,+,+,+,+,+,+,+,+k.A {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+J 6+,+,+,+,+,+,+,+,+,+,+,+,+,+,+}._+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+[+@+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+F.8+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 c+,+,+,+,+,+,+,+,+,+,+%+W 0 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+6 @.&+,+,+,+,+,+,+,+U.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+e.,+,+,+,+,+,+,+,+K.{+{+{+{+{+{+{+{+{+{+{+{+z.,+,+,+,+F.8+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 c+,+,+,+,+,+,+,+c+5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+,.,+,+,+,+,+L.{+{+{+{+{+{+{+{+{+{+{+{+{+d +,+,+,+g+{+{+{+{+{+{+{+{+{+{+{+{+{+{+2.,+,+,+w.9 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+3.,+,+,+,+,+,+,+,+%+W 0 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+6 @.&+,+,+,+,+,+,+,+,+,+,+,+).5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+a+Q.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+Q.F {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 |.,+,+,+,+,+,+,+,+h+p {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+v u.,+,+,+,+,+|+f {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k.,+,+,+,+,+,+,+>+l {+{+{+{+{+{+{+{+{+{+{+{+8 S.,+,+Q.F {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 |.,+,+,+,+,+:.5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+_.,+,+,+j.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+f i+j.9+9 {+{+{+{+{+{+{+{+{+{+{+{+{+{+e %+,+%+f.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+7 l.,+,+,+,+,+,+h+p {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+v u.,+,+,+,+,+,+,+,+,+g+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+v `.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+|+y.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+9 m.,+,+,+,+,+,+2+l {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+d j.,+,+,+,+7.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+6 q m d j+).,+,+,+,+,+,+,+,+u.{+{+{+{+{+{+{+{+{+{+{+{+{+{+~.,+|+y.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+9 m.,+,+,+m.9 {+{+{+{+{+{+{+{+{+{+{+{+{+{+7 f.G &.-.}+S f.{+{+{+{+{+{+8 l.,+,+k+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+3.,+` {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+T ;+,+,+,+,+2+l {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+d j.,+,+,+,+,+,+,+l+6 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+B ;+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+=.{+{+{+{+{+{+{+{+{+{+{+c 1+^+V '+W.k..+V.>.L E {+{+{+{+{+{+{+{+{+{+{+{+f.@+,+,+,+,+W.k {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+a j.,+,+,+b+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l b+m+`.,+,+,+,+,+,+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+I ,+=.{+{+{+{+{+{+{+{+{+{+{+c 1+^+V '+W.k..+V.>.L E {+{+{+{+{+{+{+{+{+{+{+{+f.@+,+&+F {+{+{+{+{+{+{+{+{+{+{+{+{+y [.#+,+,+,+,+,+,+>+x.a+{+{+{+{+{+f.#+n+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+7 W !.:+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+n E.7 {+{+{+{+{+{+{+{+{+{+{+{+{+{+l A A.o+o+U l {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+(.,+,+,+W.k {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+a j.,+,+,+,+,+Y.i {+{+{+{+{+{+{+{+{+{+{+{+g C f.G I I I :+p+{+{+{+{+{+{+{+{+{+{+-.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+q.6 {+{+{+{+{+{+{+{+{+8 [.>+,+,+,+,+,+,+,+,+,+,+#+M.p {+{+{+{+{+{+{+{+{+{+{+@.,+,+,+s.l {+{+{+{+{+q k.Q.;.8 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+j u.,+,+f.{+{+{+{+{+{+{+{+{+{+{+{+{+{+6 .%+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+_+w.6 {+{+{+{+{+{+{+{+{+8 [.>+,+,+,+,+,+,+,+,+,+,+#+M.p {+{+{+{+{+{+{+{+{+{+{+@.,+q+{+{+{+{+{+{+{+{+{+{+{+{+e+7+,+,+,+,+,+,+,+,+,+,+,+-+r+{+{+{+{+{+o.o.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+b W.,+,+;+..{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+q {+{+{+{+{+{+{+{+{+{+{+{+K.;.u.>+,+,+,+,+,+;+m.>.T {+{+{+{+{+{+{+{+{+{+{+{+{+f.|+,+s.l {+{+{+{+{+q k.Q.;.8 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+j u.,+,+,+,+S {+{+{+{+{+{+{+{+{+u P }.J.,+,+,+,+,+,+,+,+,+j./+{+{+{+{+{+{+{+{+8 7+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+s+{+{+{+{+{+{+{+{+{+5 7.,+,+,+,+,+,+,+,+,+,+,+,+,+,+-+=.{+{+{+{+{+{+{+{+{+{+6 t.,+$+T.{+{+{+{+{+t+O.,+,+,+_.{+{+{+{+{+{+{+{+{+f.g.$+6+#.0 {+{+{+{+{+{+t $+O.{+{+{+{+{+{+{+{+{+{+{+{+{+{+f.i.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+_+{+{+{+{+{+{+{+{+{+5 7.,+,+,+,+,+,+,+,+,+,+,+,+,+,+-+=.{+{+{+{+{+{+{+{+{+{+6 D.a {+{+{+{+{+{+{+{+{+{+$.m+,+,+,+,+,+,+,+,+,+,+,+,+,+,+#+%.{+{+{+{+_+%.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+m+,+,+,+,+,+N {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+e |.;+,+,+,+,+,+,+,+,+,+,+,+>+'+E {+{+{+{+{+{+{+{+{+{+{+{+|.$+T.{+{+{+{+{+t+O.,+,+,+_.{+{+{+{+{+{+{+{+{+f.g.$+6+#.0 {+{+{+{+{+{+t $+,+,+l.5 {+{+{+{+{+{+{+{+o+=+,+,+,+,+,+,+,+,+,+,+,+,+,+S.t {+{+{+{+{+{+{+{+^+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+d.{+{+{+{+{+{+{+{+{+{+k+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+;.{+{+{+{+{+{+{+{+{+{+L.,+A.{+{+{+{+{+6 j.,+,+,+,+>+(+{+{+{+{+{+{+{+_+v.,+,+,+,+a.k {+{+{+{+{+{+@.d.{+{+{+{+{+{+{+{+{+{+{+{+{+w S.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+k.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+;.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+f.7+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+C.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+H ,+,+,+,+,+,+|.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+o J.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+r.o {+{+{+{+{+{+{+{+{+{+{+D A.{+{+{+{+{+6 j.,+,+,+,+>+(+{+{+{+{+{+{+{+_+v.,+,+,+,+a.k {+{+{+{+{+{+@.,+,+e+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+n+9 {+{+{+{+{+{+{+{+m.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+B {+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+x {+{+{+{+{+{+{+{+{+6 7.6 {+{+{+{+{+*.,+,+,+,+,+,+'.{+{+{+{+{+{+{+]+,+,+,+,+,+,+'.{+{+{+{+{+{+6 {.{+{+{+{+{+{+{+{+{+{+{+{+b I.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+h+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+x {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l n+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+s.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+@.,+,+,+,+,+,+h.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+E n+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+X.n {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+*.,+,+,+,+,+,+'.{+{+{+{+{+{+{+]+,+,+,+,+,+,+'.{+{+{+{+{+{+6 6+n+{+{+{+{+{+{+{+{+P ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+k+{+{+{+{+{+{+{+{+N ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+G.5 {+{+{+{+{+{+{+{+{+{+_+6+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+u+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k.,+,+,+,+,+,+s.{+{+{+{+{+{+{+_.,+,+,+,+,+,+Z.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+Z ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+p.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+_+6+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+u+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+N.,+,+,+,+,+,+,+,+-+X.X.X.S.,+,+,+,+>+Q {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+n+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+>.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+t.].{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k.,+,+,+,+,+,+s.{+{+{+{+{+{+{+_.,+,+,+,+,+,+Z.{+{+{+{+{+{+{+^+=.{+{+{+{+{+{+{+0 ++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+|+%.{+{+{+{+{+{+{+{+a %+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+^.{+{+{+{+{+{+{+{+{+{+{+{+k N A.I !+!+!+f+o+-+,+,+,+,+,+,+,+,+$+9 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+$+,+,+,+,+,+,+,+5+{+{+{+{+{+{+0+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+6+,+,+,+,+,+,+,+,+>+m+^.<.>+,+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k N A.I !+!+!+f+o+-+,+,+,+,+,+,+,+,+$+9 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+D ,+,+,+,+,+,+,+,+m+h {+{+{+{+:+2+,+Z.P {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+S.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+t+*+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+4+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+$+,+,+,+,+,+,+,+5+{+{+{+{+{+{+0+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+0 {+{+{+{+{+{+{+s+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+ .{+{+{+{+{+{+{+{+{+{+e.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+5+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+q.,+,+,+,+,+,+,+,+,+f+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+&+,+,+,+,+,+,+,+..{+{+{+{+{+{+Y ,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l >+,+,+,+,+,+,+,+>+o+{+{+{+W ,+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+q.,+,+,+,+,+,+,+,+,+f+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+e.,+,+,+,+,+,+,+q.7 {+{+{+{+{+{+{+t+_+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+P.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+-.,+,+,+,+,+,+,+,+i+U J J r+7+,+,+,+,+,+,+,+%+E {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+&+,+,+,+,+,+,+,+..{+{+{+{+{+{+Y ,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+q+,+,+,+,+,+,+,+,+,+,+h+:+<+h+,+,+&+v+{+{+{+{+{+{+{+{+{+{+{+*.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+|+_+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k M ^.d.,+,+,+,+,+,+,+,+,+,+b+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+G.,+,+,+,+,+,+,+A.{+{+{+{+{+{+'+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+(+,+,+,+,+,+,+,+>+o+{+{+{+{+{+%+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k M ^.d.,+,+,+,+,+,+,+,+,+,+b+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+X.,+,+,+,+,+,+,+M {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+=.,+,+,+,+,+,+s.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 H.,+,+,+,+,+,+,+4.{+{+{+{+{+g 7+,+,+,+,+,+,+,+O {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+G.,+,+,+,+,+,+,+A.{+{+{+{+{+{+'+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5.,+,+,+,+,+,+,+,+,+,+++z s )+T.s+f {+{+{+{+{+{+{+{+{+{+{+{+(+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+7+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 K ,.w+Z.,+,+,+,+,+,+,+,+,+,+,+,+,+>.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+d.,+,+,+,+,+,+,+U {+{+{+{+{+{+++,+,+,+,+,+,+%+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+L ,+,+,+,+,+,+,+|.{+{+{+{+{+{+E.,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 K ,.w+Z.,+,+,+,+,+,+,+,+,+,+,+,+,+>.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+|+,+,+,+,+,+,+>+l {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+'.,+,+,+,+,+,+q.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+a+,+,+,+,+,+,+,+%+b {+{+{+{+{+{+K ,+,+,+,+,+,+,+C.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+d.,+,+,+,+,+,+,+U {+{+{+{+{+{+++,+,+,+,+,+,+%+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+I.*.{+{+{+{+{+{+{+{+{+{+{+{+{+j ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+7.{+{+{+{+{+{+{+{+{+{+{+{+{+n h.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+N.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+C.,+,+,+,+,+,+,+(+{+{+{+{+{+{+|+,+,+,+,+,+,+0.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+<+,+,+,+,+,+,+,+ .{+{+{+{+{+{+d.,+,+,+,+,+,+,+ +{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+n h.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+N.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+&+,+,+,+,+,+,+G.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+..,+,+,+,+,+,+X.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+n.,+,+,+,+,+,+,+[.{+{+{+{+{+{+{+v ,+,+,+,+,+,+,+i+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+C.,+,+,+,+,+,+,+(+{+{+{+{+{+{+|+,+,+,+,+,+,+0.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+L ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+o.{+{+{+{+{+{+{+{+{+{+{+{+{+-+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+2.{+{+{+{+{+{+{+{+{+{+{+{+v P.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+C.,+,+,+,+,+,+,+:+{+{+{+{+{+)+,+,+,+,+,+,+,+e.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+Z ,+,+,+,+,+,+,+ .{+{+{+{+{+{+a.,+,+,+,+,+,+,+7+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+v P.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+X.,+,+,+,+,+,+ +{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+X.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+..,+,+,+,+,+,+,+/.{+{+{+{+{+{+{+(+,+,+,+,+,+,+,+k.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+C.,+,+,+,+,+,+,+:+{+{+{+{+{+)+,+,+,+,+,+,+,+e.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+6 v.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+4.].{+{+{+{+{+{+{+{+{+{+{+S.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+).{+{+{+{+{+{+{+{+{+{+{+p+O.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+g+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+1.,+,+,+,+,+,+,+P {+{+{+{+{+3+,+,+,+,+,+,+,+e.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+9+,+,+,+,+,+,+,++.{+{+{+{+{+{+0+,+,+,+,+,+,+,+i.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+p+O.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+g+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+p.,+,+,+,+,+,+v.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+++{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+#.,+,+,+,+,+,+,+W.{+{+{+{+{+{+{+d+,+,+,+,+,+,+,+&+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+1.,+,+,+,+,+,+,+P {+{+{+{+{+3+,+,+,+,+,+,+,+e.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+D >+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+_.{+{+{+{+{+{+{+{+{+{+{+Y.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+'.{+{+{+{+{+{+{+{+{+{+{+l+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+&.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+x+,+,+,+,+,+,+,+^.{+{+{+{+{+A.,+,+,+,+,+,+,+i+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+,+e.{+{+{+{+{+{++.,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+&.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0.,+,+,+,+,+,+,+e+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+v.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+U ,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+y+,+,+,+,+,+,+,+-+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+x+,+,+,+,+,+,+,+^.{+{+{+{+{+A.,+,+,+,+,+,+,+i+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+z+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+&+b {+{+{+{+{+{+{+{+{+{+*+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+(.{+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+P.1+o F c.,+,+,+,+,+,+,+,+z {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+A+,+,+,+,+,+,+,+|.{+{+{+{+{+~+,+,+,+,+,+,+,+q.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+<+,+,+,+,+,+,+,+*+t+{+{+{+{+{++.,+,+,+,+,+,+,+D.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+P.1+o F c.,+,+,+,+,+,+,+,+z {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+X ,+,+,+,+,+,+,+J.e {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+-+k {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+/+,+,+,+,+,+,+,+j.{+{+{+{+{+{+{+6+,+,+,+,+,+,+,+=+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+A+,+,+,+,+,+,+,+|.{+{+{+{+{+~+,+,+,+,+,+,+,+q.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 M.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+O {+{+{+{+{+{+{+{+{+u ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+m+{+{+{+{+{+{+{+{+{+{+o+,+,+,+,+,+,+,+,+'+{+{+{++.,+,+,+,+,+,+,+,+/+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+L.,+,+,+,+,+,+,+h+{+{+{+{+{+~.,+,+,+,+,+,+,+%+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+z ,+,+,+,+,+,+,+,+|.{+{+{+{+{+,.,+,+,+,+,+,+,+k.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+o+,+,+,+,+,+,+,+,+'+{+{+{++.,+,+,+,+,+,+,+,+/+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+)+|+,+,+,+,+,+,+,+j.E {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+,+r {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+I.,+,+,+,+,+,+*+d {+{+{+{+{+(+,+,+,+,+,+,+,+,+ +{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+L.,+,+,+,+,+,+,+h+{+{+{+{+{+~.,+,+,+,+,+,+,+%+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+<+F.,+,+,+,+,+,+,+,+,+,+,+,+,+,+'+{+{+{+{+{+{+{+{+{+a+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+t.{+{+{+{+{+{+{+{+{+{+o+,+,+,+,+,+,+,+,+w.6 {+7 9.,+,+,+,+,+,+,+;+8 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+^+{+{+{+)+P.,+,+,+,+,+,+,+S.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+a *+,+,+,+,+,+,+,+,+0+k {+{+t Z.,+,+,+,+,+,+,+7+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+o+,+,+,+,+,+,+,+,+w.6 {+7 9.,+,+,+,+,+,+,+;+8 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+_.,+,+,+,+,+,+,+,+k.A {+{+{+8 d+2.4.f {+{+{+{+{+{+{+` {+{+{+{+{+{+{+{+{+{+{+{+{+ .,+,+,+,+,+,+,+C {+{+{+{+{+{+{+{+{+{+{+{+c x {+{+{+{+{+{+{+{+_.,+,+,+,+,+,+,+x+{+{+{+{+5 l+,+,+,+,+,+,+,+,+x+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+^+{+{+{+)+P.,+,+,+,+,+,+,+S.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+6 U n.N.-+,+,+,+,+,+,+,+,+,+,+C.{+{+{+{+{+{+{+{+{+S ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+-+].{+{+{+{+{+{+{+{+{+*.,+,+,+,+,+,+,+,+,+r.C.2+,+,+,+,+,+,+,+,+#+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+$+}+r+#.P.,+,+,+,+,+,+,+,+@+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+i+,+,+,+,+,+,+,+,+,+H.}+(.*+,+,+,+,+,+,+,+,+R.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+*.,+,+,+,+,+,+,+,+,+r.C.2+,+,+,+,+,+,+,+,+#+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+%.*+,+,+,+,+,+,+,+,+,+`.S.S.#+,+,+,+h+k {+{+{+{+{+l $+z.{+{+{+{+{+{+{+{+{+{+{+{+C.,+,+,+,+,+,+,+@.{+{+{+{+{+{+{+{+{+{+{+{+~.g+{+{+{+{+{+{+{+{+X ,+,+,+,+,+,+,+>+Z 5 {+0 >.,+,+,+,+,+,+,+,+&+)+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r+,+,+,+,+,+,+,+,+$+}+r+#.P.,+,+,+,+,+,+,+,+@+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+7 [+e+T.5 {+{+{+z.,+,+,+,+,+,+,+,+,+,+@.{+{+{+{+{+{+{+{+{+<.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+y.{+{+{+{+{+{+{+{+{+v ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+f.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+^+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+f+a {+{+{+{+{+{+{+{+{+y >+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+i {+{+{+{+{+{+{+{+{+{+{+{+9 {+{+{+{+{+{+{+{+{+v ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+f.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+^.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+I {+{+{+{+{+#.,+X.d {+{+{+{+{+{+{+{+{+{+{+g.,+,+,+,+,+,+,+<.{+{+{+{+{+{+{+{+{+{+{+:+;+`._+{+{+{+{+{+{+{+m |+,+,+,+,+,+,+,+,+6+C.q.,+,+,+,+,+,+,+,+,+o.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+^+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k .+,+,+,+Y.N.m+m+r.,+,+,+,+,+,+,+,+,+*+m {+{+{+{+{+{+{+{+9 S.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+4+{+{+{+{+{+{+{+{+{+{+++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+Q {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l #+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+ .U.{+{+{+{+{+{+{+{+{+{+i+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+G {+{+{+{+{+{+{+{+{+{+{+s+4+{+{+{+{+{+{+{+{+{+{+++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+Q {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+E u.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+*+e {+{+{+{+8 F.,+,+V.{+{+{+{+{+{+{+{+{+{+{+c+,+,+,+,+,+,+,+e.{+{+{+{+{+{+{+{+{+{+f J.,+,+{.{+{+{+{+{+{+{+{+_.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+i.7 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l #+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+A+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+..{+{+{+{+{+{+{+{+{+L ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+++{+{+{+{+{+{+{+{+{+{+W ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+3+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+x+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+#+{+{+{+{+{+{+{+{+{+b.`.g {+{+{+{+{+{+{+{+{+f ++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+r.6 {+{+{+{+{+{+{+{+{+{+6 p.++{+{+{+{+{+{+{+{+{+{+W ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+3+{+{+{+{+{+{+{+{+{+_+{+{+{+{+{+{+{+{+{+{+F n+,+,+,+,+,+,+,+,+,+,+,+,+,+,+z+{+{+{+{+{+Y ,+,+,+,+>.{+{+{+{+{+{+{+{+{+{+p+%+,+,+,+,+,+,+b+{+{+{+{+{+{+{+{+{+a i.,+,+,+H.9 {+{+{+{+{+{+{+e G.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+J.m {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+x+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+#+{+{+{+{+{+{+{+{+{+b {+{+{+{+{+{+]+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+w+5 {+{+{+{+{+{+{+{+{+.+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+z {+{+{+{+{+{+{+{+{+{+c+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+++_+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+_+++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+d.{+{+{+{+{+{+{+{+u $+,+[.{+{+{+{+{+{+{+{+{+{+j+7+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+R.q {+{+{+{+{+{+{+{+{+{+{+@.,+,+z {+{+{+{+{+{+{+{+{+{+c+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+++_+{+{+{+{+{+{+{+{+0 s.q {+{+{+{+{+{+{+{+{+{+_+o.S.,+,+,+,+,+,+,+,+,+,+,+_.6 {+{+{+{+m %+,+,+,+,+,+4+{+{+{+{+{+{+{+{+{+{+z.|+,+,+,+,+8.6 {+{+{+{+{+{+{+{+b l+,+,+,+,+,++.{+{+{+{+{+{+{+{+p ++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+R.r {+{+{+{+{+{+{+{+{+f.9 {+{+{+{+{+{+{+{+_+++,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+d.{+{+{+{+{+{+{+{+u 2+{+{+{+{+{+{+l Y.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+h+u {+{+{+{+{+{+{+{+{+B ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+b.{+{+{+{+{+{+{+{+{+{+{+{.&+,+,+,+,+,+>+j.9+L 3+6.;+,+,+,+G.q {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+(+&+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+]+{+{+{+{+{+{+{+{+S ,+,+,+M {+{+{+{+{+{+{+{+{+{+E y+&+,+,+,+,+,+v.M l k F *.a.$+,+H.G {+{+{+{+{+{+{+{+{+{+{+t $+,+,+b.{+{+{+{+{+{+{+{+{+{+{+{.&+,+,+,+,+,+>+j.9+L 3+6.;+,+,+,+G.q {+{+{+{+{+{+{+{+{+z.,+p.9 {+{+{+{+{+{+{+{+{+{+{+_+&.b+b+]+'+7+%+|+X.6.I {+{+{+{+{+7 h.,+,+,+,+,+,+,+g+9 {+{+{+{+{+{+{+{+{+:+8.%+E.9+6 {+{+{+{+{+{+{+{+n 6+,+,+,+,+,+,+#+o {+{+{+{+{+{+{+{+c _.>+,+,+,+,+,+,+,+,+,+,+,+,+++q {+{+{+{+{+{+{+{+{+0 D.O {+{+{+{+{+{+{+{+{+(+&+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+]+{+{+{+{+{+{+{+{+S ,+P {+{+{+{+{+{+B $+,+,+,+,+,+,+,+,+,+,+,+,+,+w.8+{+{+{+{+{+{+{+{+{+7 s.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+:+{+{+{+{+{+{+{+{+{+{+{+r C.S.,+|+/.q {+{+{+{+{+o =.4.{.E {+{+{+{+{+{+{+{+{+6 C.{+{+{+{+{+{+{+{+{+{+J $+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.6 {+{+{+{+{+{+{+{+j.,+,+,+`.F {+{+{+{+{+{+{+{+{+{+{+t+9+u+x.N.A+7 {+{+{+{+{+{+{+b {+{+{+{+{+{+{+{+{+{+{+{+b u.,+,+,+>+:+{+{+{+{+{+{+{+{+{+{+{+r C.S.,+|+/.q {+{+{+{+{+o =.4.{.E {+{+{+{+{+{+{+{+{+6 h+,+,+5.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+C.,+,+,+,+,+,+,+,+,+m.q {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+L.Z.,+,+,+,+,+,+,+,+m.9 {+{+{+{+{+{+{+{+{+C 6+,+,+,+,+,+,+,+,+,+;+M.l {+{+{+{+{+{+{+{+{+{+x+,+D.{+{+{+{+{+{+{+{+{+{+J $+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.6 {+{+{+{+{+{+{+{+j.,+Q.g {+{+{+{+{+{+K.y+[.Y.,+,+,+,+,+,+,+,+6+A+6 {+{+{+{+{+{+{+{+{+{+q+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+p.6 {+{+{+{+{+{+{+{+{+{+{+{+7 q )+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+o+,+s+{+{+{+{+{+{+{+{+{+{+t l+,+,+,+,+|+b.L v v v v R e.0.@.].{+{+{+{+{+{+{+{+A ,+,+,+,+,+n+f {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+].l+,+,+,+,+,+p.6 {+{+{+{+{+{+{+{+{+{+{+{+7 q )+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+o+,+,+,+,+,.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+3.,+,+,+,+,+,+,+,+,+,+,+-+;.$.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+v c.,+,+,+,+,+,+,+,+,+,+,+_.{+{+{+{+{+{+{+{+{+{+5 z./.4.w+V.V.6.'.*.n {+{+{+{+{+{+{+{+{+{+{+d+;+,+,+s+{+{+{+{+{+{+{+{+{+{+t l+,+,+,+,+|+b.L v v v v R e.0.@.].{+{+{+{+{+{+{+{+A ,+,+,+x.6 {+{+{+{+{+{+{+{+0 a+z.x+w+V.|.]+f.{+{+{+{+{+{+{+{+{+{+{+{+5+|+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+..{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+%.`.,+i.5 {+{+{+{+{+{+{+{+{+{+{+f+z+0+M e {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 k.,+,+,+,+,+,+m.$.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+8 N.,+,+,+,+,+,+,+..{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+%.`.,+,+,+,+,+2.8 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+9 _.,+,+,+,+,+,+,+,+,+,+,+,+,+,+@+3.K.{+{+{+{+{+{+{+{+{+{+{+{+8 [+0.>+,+,+,+,+,+,+,+,+,+,+,+,+,+>.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+C *+,+,+,+i.5 {+{+{+{+{+{+{+{+{+{+{+f+z+0+M e {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 k.,+,+,+,+/.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+T.S.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+|+:+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+9 l.,+,+,+]+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+W ,+,+,+,+,+,+,+,+r.t+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l d.,+,+,+,+,+,+,+,+|+:+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+9 l.,+,+,+,+,+,+,+j.f.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+p m.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+q.,.z.v d ].0 E m y.*.1.S.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+4.0 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+z.*+,+,+,+,+,+]+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+W ,+,+,+,+,+,+>.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+r X.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+P.j+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 <.,+,+,+,+@+o {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+p %+,+,+,+,+,+,+,+,+,+I.x {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+q r.,+,+,+,+,+,+,+,+,+,+P.j+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 <.,+,+,+,+,+,+,+,+,+#+.._+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+b x+*+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+b.f {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+6 -.;+,+,+,+,+,+,+@+o {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+p %+,+,+,+,+,+,+,+5.k {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+A =+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+++m {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0+,+,+,+,+,+,+p.u {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l h+,+,+,+,+,+,+,+,+,+,+,+|+..6 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 W #+,+,+,+,+,+,+,+,+,+,+,+,+++m {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0+,+,+,+,+,+,+,+,+,+,+,+,+P...)+{+{+{+{+{+{+{+{+{+{+{+{+j+x+=+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+X.L {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+s .+,+,+,+,+,+,+,+,+,+p.u {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l h+,+,+,+,+,+,+,+,+,+6+s {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+].x+;+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+I.T.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+7 :.,+,+,+,+,+,+,+,+c.9 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k 2+,+,+,+,+,+,+,+,+,+,+,+,+,+,+6+H {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+w j.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+I.T.{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+7 :.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+|+g.q+^+j+b 6 8 c K.&.y+l.>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+x.!+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+m !.|+,+,+,+,+,+,+,+,+,+,+,+c.9 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k 2+,+,+,+,+,+,+,+,+,+,+,+|+3.d {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+e+n+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+`.V {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+p+.+,+,+,+,+,+,+,+,+,+,+c.E {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l W.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+V.(+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+f+N.>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+`.V {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+p+.+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+d.M _+{+{+{+{+{+{+{+{+{+{+{+{+0 I _.-+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.E {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+l W.,+,+,+,+,+,+,+,+,+,+,+,+,+,+@+3.q {+{+{+{+{+{+{+{+{+{+{+{+b A+6+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+<.g {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 ]+=+,+,+,+,+,+,+,+,+,+,+,+,+p.o {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+K.s.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+k.#.e {+{+{+{+{+{+{+{+{+{+{+{+{+{+i o+w.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+<.g {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+5 ]+=+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+%+9.` ^+F c 8 6 b 8+z *.|.P.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+p.o {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+K.s.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+6+>.r+s $.9 0 u n A U.8.$+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+Z...b {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k+k.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+@+]+5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 {.#+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+;+p.B+Q H t+l 6 7 l p+y b+!.s.>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+Z...b {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+k+k.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+@+]+5 {+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+0 {.#+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+=+>.t {+{+{+{+{+{+{+{+{+{+{+{+{+d #.p.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+i.s+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+A m.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+=+>.t {+{+{+{+{+{+{+{+{+{+{+{+{+d #.p.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+i.s+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+{+A m.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+n+4.R H i E 0 ].d T.K }+W.-+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+D.O a {+{+{+{+{+{+{+{+{+{+{+{+E Q j.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+n+4.R H i E 0 ].d T.K }+W.-+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+D.O a {+{+{+{+{+{+{+{+{+{+{+{+E Q j.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+%+9.` k+j+l 6 7 l o k+ .0.#+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+%+9.` k+j+l 6 7 l o k+ .0.#+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+E.,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+b +.+.+.+.+.:+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+b +.+.+.+.+.a+{+g+,+,+,+,+,+,+,+{+b +.+.+.J {+:++.+.+.m {+E.,+{+m ,+,+,+,+,++.{+g+,+{+b +.+.+.+.+.+.+.w.,+{+m ,+,+{+b +.+.+.+.+.+.+.w.,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+b +.+.+.+.+.:+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+b +.+.+.+.+.a+{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+b +.+.+.+.+.:+{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+b +.+.+.+.+.:+{+g+,+{+b +.+.+.+.+.:+{+g+,+,+,+,+,+,+,+{+b +.+.+.+.+.+.+.w.,+{+m ,+,+,+,+,++.{+g+,+{+b +.+.+.+.+.+.+.w.,+{+b +.+.+.+.+.+.+.w.,+{+b +.+.+.+.+.:+{+g+,++.+.+.N {+r +.+.+.w.,+{+m ,+,+{+b +.+.+.+.+.:+{+g+,+{+b +.+.+.+.+.:+{+g+,+{+b +.+.+.+.+.+.+.w.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,+n.{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,+,+,+,+,+{+m ,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,+n.{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+m ,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+_+N N N N N r {+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+_+N N N N N U.g+7+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+m ,+,+,+,+,++.{+g+,+{+_+N N N N N N N g.,+{+m ,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+_+N N N N N r {+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+_+N N N N N U.g+7+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+_+N N N N N r {+g+,+{+_+N N N N N r {+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+_+N N N N N e.,+,+,+g+0+N N N N N +.g+7+,+{+m ,+,+,+,+,+,+,+,+,+{+_+N N N N N e.,+,+,+{+_+N N N N N r {+g+,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+_+N N N N N N N g.,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{++.,+,+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{++.,+,+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+{+{+{+{+{+{++.,+,+,+,+E.{+{+{+{+{++.,+,+,+{+m ,+,+,+,+,+,+,+,+,+{+{+{+{+{+{+{++.,+,+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.c.c.3.{+I c.c.c.X.,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+f c.c.c.c.c.+.:+V.,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+m ,+,+,+,+,++.{+g+,+c.c.c.c.c.c.c.N {+g+,+{+m ,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.c.c.c.c.c.c.N {+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+f c.c.c.c.c.+.:+V.,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+f c.c.c.c.c.N {+g+,+c.c.c.c.c.c.c.N {+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+f c.c.c.c.c.E.,+,+,+:+J c.c.c.c.c.+.:+V.,+{+m ,+,+,+,+,+,+,+,+,+{+f c.c.c.c.c.E.,+,+,+{+f c.c.c.c.c.c.c.X.,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+c.c.c.c.c.c.c.N {+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,++.{+g+,+{+m ,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+m ,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+{+m ,+,+,+,+,++.{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,++.{+g+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.{+N ,+,+,+,+,+{+6 m m m m m b {+g+,+{+6 m m m m m b {+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+6 m m m m m b {+g+,+m m m m m m m b {+g+,+{+m ,+,+{+6 m m m m m m m |.,+{+m ,+,+,+,+,+,+,+,+m m m m m m m b {+g+,+{+6 m m m m m b {+g+,+{+6 m m m m m b {+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+6 m m m $.{+b m m m 9 {+E.,+{+m ,+,+,+,+,++.{+g+,+m m m m m m m b {+g+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+6 m m m m m b {+g+,+,+,+,+,+,+,+{+6 m m m m m m m |.,+{+m ,+,+,+,+,++.{+g+,+{+6 m m m m m m m |.,+{+6 m m m m m m m |.,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+{+6 m m m m m b {+g+,+{+m ,+,+,+,+,++.{+g+,+m m m m m m m b {+g+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+c.{+N ,+,+,+,+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+m ,+,+,+g+{++.,+,+,+:+{+E.,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+{+{+{+{+E.,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+,+,+,+,+,+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+c.{+N ,+,+,+,+,+{+m ,+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,++.{+g+,+{+{+{+{+{+{+{+{+{+g+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+{+m ,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; { } 9 i t D S +.~.1.d.m.v.J.S.`.$+*+-+-+>+>+>+>+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+>+>+>+>+-+*+$+%+S.X.v.m.d.e.~.+.S D v i 9 } { > ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; ~ [ 9 i t D S +.!.|.c.m.u.J.R.Z.#+&+*+-+;+;+;+;+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+;+;+;+;+*+&+#+@+R.J.u.m.d.6.!.+.S D t i 9 [ { > ", -"; ~ [ 9 i s C Q ..'.}.b.k.s.H.P.=+`.%+#+$+*+*+*+*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*+*+*+*+#+%+`.=+P.H.s.k.b.}.)...R C s i 9 [ ~ ; ", -"; ~ [ 9 h s B P .'.}.a.j.r.G.O.S.Z.@+%+#+&+&+&+&+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+&+&+&+&+%+@+Z.S.O.G.r.j.a.}.'. .Q C s h 9 [ ~ ; ", -"; ~ [ 9 h s A O .,.[.9..+q.++J.Y.S.Z.`.@+#+#+#+#+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+#+#+#+#+`.Z.S.Y.J.++q.i.9.[.,. .Q A s h 9 [ ~ ; ", -"; ~ [ 8 g r A O Z B+:.8.h.p.n+H.P.Y.S.=+Z.@+@+@+@+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+@+@+@+@+=+S.Y.P.H.n+6+h.8.:.B+Z P A r g 8 [ ~ ; ", -"; ~ < 7 f K.z L.@.;.M.N.g.w.s.E.H.J.O.P.Q.R.R.R.R.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.R.R.R.R.P.O.J.H.E.s.w.g.N.M.;.@.L.z T.f 7 < ~ ; ", -"- ! : 6 j q s+z.A.-.C.e.a.h. +s.n+++G.H.H.J.J.J.J.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.J.J.J.J.H.G.++n+s. +h.a.e.C.-.A.z.s+q j 6 : ! - ", -"- ! _ 5 $.F x K n.o.).5.7.b.h.l.p.q.r.s.t.u.u.u.u.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.u.u.u.u.s.r.q.p.w.h.x.7.5.).o.#.K x F $.5 _ ! - ", -"= ) ( 4 l f.w J S Z ;.4.[.7.a.g.h.i.j.k.l.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.k.j.i.h.g.a.7.[.4.;.` S J w f.l 4 ( ) = ", -"= ' / 3 a m v B V {.=.3.4.5.6.7.8.9.0.a.b.c.c.c.c.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.c.c.c.c.a.0.9.8.7.e.5.4.3.=.{.V B v m a 3 / ' = ", -"* ] ^ 2 ].g q x U Q A.o.;.'.C.'+:.[.}.}.|.6.6.6.6.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.6.6.6.6.}.}.[.:.M.C.).;.o.A.Q U x q g ].2 ^ ] * ", -"& > { < 0 $.%.G z &.Q *.Z =.-.;.>.,.'.'.).!.!.!.!.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.!.!.!.!.'.'.,.>.;.-.=.Z {.Q K z G %.$.0 < { > & ", -"% - ) 1 3 k g F T z U V R W X Y Z ` . ...+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+. . .` Z @.X #.R V U z T F g k 3 1 ) - % ", -"$ = ] ^ [ 5 E i F G H I J K L M N O P P Q R R R R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R R R R P P O N M L K J I x G F i E 5 [ ^ ] = $ ", -"# & ; ! / 2 6 u g n p v w x y z A A B B C C C C C D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D C C C C C B A A z y x w v p n g u 6 2 / ! ; & # ", -"@ $ = , { ( 2 0 _+c g m %.F p K.r s s t t t t t t v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v t t t t t s s r K.p F %.m g c _+0 2 ( { , = $ @ ", -"+ # % - , ~ 1 [ 3 0 9 a l $.j e g g h h h i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i h h g g e j $.l a 9 0 3 [ 1 ~ ] - % # + ", -". + # & - , ! | 1 < 2 3 4 5 6 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 7 7 6 5 4 3 2 < 1 | ! , - & # + . ", -" . @ # % = ; ] ! { ^ / ( _ : : < < [ [ [ [ [ [ [ } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } [ [ [ [ [ [ < < < : _ ( / ^ { ! ] ; = % # @ . ", -" . @ # $ & = - > ] ' ) ! ! ~ ~ ~ ~ ~ ~ { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { ~ ~ ~ ~ ~ ! ! ) ' ] > - = & $ # @ . ", -" . + @ # $ % & * = = - - ; ; ; ; ; ; ; ; ; ; > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > ; ; ; ; ; ; ; ; ; - - = = * & % $ # @ + . "}; diff --git a/src/libaudgui/confirm.c b/src/libaudgui/confirm.c index 1b9c474..2330c07 100644 --- a/src/libaudgui/confirm.c +++ b/src/libaudgui/confirm.c @@ -1,6 +1,6 @@ /* * libaudgui/confirm.c - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * This file is part of Audacious. * @@ -19,7 +19,10 @@ * using our public API to be a derived work. */ +#include <gtk/gtk.h> + #include <audacious/audconfig.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/playlist.h> @@ -86,9 +89,7 @@ void audgui_confirm_playlist_delete (gint playlist) button = gtk_button_new_from_stock (GTK_STOCK_YES); gtk_box_pack_end ((GtkBox *) hbox, button, FALSE, FALSE, 0); -#if GTK_CHECK_VERSION (2, 18, 0) gtk_widget_set_can_default (button, TRUE); -#endif gtk_widget_grab_default (button); gtk_widget_grab_focus (button); g_signal_connect ((GObject *) button, "clicked", (GCallback) @@ -98,3 +99,32 @@ void audgui_confirm_playlist_delete (gint playlist) gtk_widget_show_all (window); } + +static void rename_cb (GtkDialog * dialog, gint resp, void * list) +{ + if (resp == GTK_RESPONSE_ACCEPT && GPOINTER_TO_INT (list) < + aud_playlist_count ()) + aud_playlist_set_title (GPOINTER_TO_INT (list), gtk_entry_get_text + ((GtkEntry *) g_object_get_data ((GObject *) dialog, "entry"))); + + gtk_widget_destroy ((GtkWidget *) dialog); +} + +void audgui_show_playlist_rename (gint playlist) +{ + GtkWidget * dialog = gtk_dialog_new_with_buttons (_("Rename Playlist"), + NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, NULL); + gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_ACCEPT); + + GtkWidget * entry = gtk_entry_new (); + gtk_entry_set_text ((GtkEntry *) entry, aud_playlist_get_title (playlist)); + gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE); + gtk_box_pack_start ((GtkBox *) gtk_dialog_get_content_area ((GtkDialog *) + dialog), entry, FALSE, FALSE, 0); + g_object_set_data ((GObject *) dialog, "entry", entry); + + g_signal_connect (dialog, "response", (GCallback) rename_cb, GINT_TO_POINTER + (playlist)); + gtk_widget_show_all (dialog); +} diff --git a/src/libaudgui/effects-menu.c b/src/libaudgui/effects-menu.c index 93fc18f..67cc118 100644 --- a/src/libaudgui/effects-menu.c +++ b/src/libaudgui/effects-menu.c @@ -27,13 +27,26 @@ #include "libaudgui-gtk.h" -static void enable_cb (GtkCheckMenuItem * item, PluginHandle * plugin) +static gboolean watch_cb (PluginHandle * plugin, GtkCheckMenuItem * item) { - aud_effect_plugin_enable (plugin, gtk_check_menu_item_get_active (item)); + gboolean enabled = aud_plugin_get_enabled (plugin); + gtk_check_menu_item_set_active (item, enabled); GtkWidget * settings = g_object_get_data ((GObject *) item, "settings"); if (settings != NULL) - gtk_widget_set_sensitive (settings, gtk_check_menu_item_get_active (item)); + gtk_widget_set_sensitive (settings, enabled); + + return TRUE; +} + +static void enable_cb (GtkCheckMenuItem * item, PluginHandle * plugin) +{ + aud_plugin_enable (plugin, gtk_check_menu_item_get_active (item)); +} + +static void destroy_cb (GtkCheckMenuItem * item, PluginHandle * plugin) +{ + aud_plugin_remove_watch (plugin, (PluginForEachFunc) watch_cb, item); } static void settings_cb (GtkMenuItem * settings, PluginHandle * plugin) @@ -53,7 +66,9 @@ static gboolean add_item_cb (PluginHandle * plugin, GtkWidget * menu) gtk_check_menu_item_set_active ((GtkCheckMenuItem *) item, aud_plugin_get_enabled (plugin)); gtk_menu_shell_append ((GtkMenuShell *) menu, item); + aud_plugin_add_watch (plugin, (PluginForEachFunc) watch_cb, item); g_signal_connect (item, "toggled", (GCallback) enable_cb, plugin); + g_signal_connect (item, "destroy", (GCallback) destroy_cb, plugin); gtk_widget_show (item); if (aud_plugin_has_configure (plugin)) diff --git a/src/libaudgui/icons-stock.c b/src/libaudgui/icons-stock.c index c96daf3..fd25c94 100644 --- a/src/libaudgui/icons-stock.c +++ b/src/libaudgui/icons-stock.c @@ -23,21 +23,25 @@ #include <gdk/gdk.h> #include <gtk/gtk.h> +#include <audacious/misc.h> -static void -load_stock_icon(gchar *id, gchar *filename, GtkIconFactory *iconfactory) +static void load_stock_icon (gchar * id, gchar * filename, + GtkIconFactory * iconfactory) { - GtkIconSet *iconset; - GdkPixbuf *pixbuf; + gchar * path = g_strdup_printf ("%s/images/%s", + aud_get_path (AUD_PATH_DATA_DIR), filename); - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file (path, NULL); if (pixbuf == NULL) - return; + goto ERR; - iconset = gtk_icon_set_new_from_pixbuf(pixbuf); + GtkIconSet * iconset = gtk_icon_set_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); gtk_icon_factory_add(iconfactory, id, iconset); + +ERR: + g_free (path); } void @@ -46,14 +50,13 @@ audgui_register_stock_icons(void) GtkIconFactory *iconfactory = gtk_icon_factory_new(); load_stock_icon(AUD_STOCK_PLAYLIST, - DATA_DIR "/images/menu_playlist.png", iconfactory); + "menu_playlist.png", iconfactory); load_stock_icon(AUD_STOCK_PLUGIN, - DATA_DIR "/images/menu_plugin.png", iconfactory); + "menu_plugin.png", iconfactory); load_stock_icon(AUD_STOCK_QUEUETOGGLE, - DATA_DIR "/images/menu_queue_toggle.png", iconfactory); + "menu_queue_toggle.png", iconfactory); load_stock_icon(AUD_STOCK_RANDOMIZEPL, - DATA_DIR "/images/menu_randomize_playlist.png", iconfactory); - + "menu_randomize_playlist.png", iconfactory); gtk_icon_factory_add_default( iconfactory ); g_object_unref( iconfactory ); diff --git a/src/libaudgui/iface-menu.c b/src/libaudgui/iface-menu.c index 3a9b76d..24b2834 100644 --- a/src/libaudgui/iface-menu.c +++ b/src/libaudgui/iface-menu.c @@ -28,13 +28,12 @@ static void switch_cb (GtkMenuItem * item, PluginHandle * plugin) { if (gtk_check_menu_item_get_active ((GtkCheckMenuItem *) item)) - aud_iface_plugin_set_active (plugin); + aud_plugin_enable (plugin, TRUE); } typedef struct { GtkWidget * menu; GSList * group; - PluginHandle * active_plugin; } IfaceMenuAddState; static gboolean add_item_cb (PluginHandle * plugin, IfaceMenuAddState * state) @@ -42,7 +41,7 @@ static gboolean add_item_cb (PluginHandle * plugin, IfaceMenuAddState * state) GtkWidget * item = gtk_radio_menu_item_new_with_label (state->group, aud_plugin_get_name (plugin)); state->group = gtk_radio_menu_item_get_group ((GtkRadioMenuItem *) item); - if (plugin == state->active_plugin) + if (aud_plugin_get_enabled (plugin)) gtk_check_menu_item_set_active ((GtkCheckMenuItem *) item, TRUE); gtk_menu_shell_append ((GtkMenuShell *) state->menu, item); g_signal_connect (item, "activate", (GCallback) switch_cb, plugin); @@ -52,8 +51,7 @@ static gboolean add_item_cb (PluginHandle * plugin, IfaceMenuAddState * state) GtkWidget * audgui_create_iface_menu (void) { - IfaceMenuAddState state = {gtk_menu_new (), NULL, - aud_iface_plugin_get_active ()}; + IfaceMenuAddState state = {gtk_menu_new (), NULL}; aud_plugin_for_each (PLUGIN_TYPE_IFACE, (PluginForEachFunc) add_item_cb, & state); return state.menu; diff --git a/src/libaudgui/infopopup.c b/src/libaudgui/infopopup.c index 55110af..f9c42eb 100644 --- a/src/libaudgui/infopopup.c +++ b/src/libaudgui/infopopup.c @@ -25,8 +25,8 @@ #include <string.h> #include <audacious/audconfig.h> -#include <audacious/compatibility.h> #include <audacious/drct.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/misc.h> #include <audacious/playlist.h> @@ -35,7 +35,16 @@ #include "libaudgui.h" #include "libaudgui-gtk.h" -#define DEFAULT_ARTWORK DATA_DIR "/images/audio.png" +static const gchar * get_default_artwork (void) +{ + static gchar * path = NULL; + if (! path) + path = g_strdup_printf ("%s/images/audio.png", + aud_get_path (AUD_PATH_DATA_DIR)); + return path; +} + +#define DEFAULT_ARTWORK (get_default_artwork ()) static GtkWidget * infopopup = NULL; @@ -246,8 +255,6 @@ static void infopopup_create (void) /* track progress */ infopopup_progress = gtk_progress_bar_new (); - gtk_progress_bar_set_orientation ((GtkProgressBar *) infopopup_progress, - GTK_PROGRESS_LEFT_TO_RIGHT); gtk_progress_bar_set_text ((GtkProgressBar *) infopopup_progress, ""); gtk_table_attach ((GtkTable *) infopopup_data_table, infopopup_progress, 0, 2, 7, 8, GTK_FILL, 0, 0, 0); diff --git a/src/libaudgui/infowin.c b/src/libaudgui/infowin.c index c103aba..13d1c47 100644 --- a/src/libaudgui/infowin.c +++ b/src/libaudgui/infowin.c @@ -26,6 +26,7 @@ #include <stdarg.h> #include <audacious/audconfig.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/misc.h> #include <audacious/playlist.h> @@ -36,7 +37,7 @@ #include "libaudgui.h" #include "libaudgui-gtk.h" -#define STATUS_TIMEOUT 3000 +#define AUDGUI_STATUS_TIMEOUT 3000 static GtkWidget * infowin = NULL; @@ -51,14 +52,11 @@ static GtkWidget * entry_genre; static GtkWidget * image_artwork; -static GtkWidget * image_fileicon; static GtkWidget * label_format_name; static GtkWidget * label_quality; static GtkWidget * label_bitrate; static GtkWidget * btn_apply; static GtkWidget * label_mini_status; -static GtkWidget * arrow_rawdata; -static GtkWidget * treeview_rawdata; enum { @@ -68,55 +66,53 @@ enum }; static gchar * current_file = NULL; -static InputPlugin * current_decoder = NULL; +static PluginHandle * current_decoder = NULL; static gboolean can_write = FALSE, something_changed = FALSE; -static const gchar * genre_table[] = -{ - N_("Blues"), N_("Classic Rock"), N_("Country"), N_("Dance"), - N_("Disco"), N_("Funk"), N_("Grunge"), N_("Hip-Hop"), - N_("Jazz"), N_("Metal"), N_("New Age"), N_("Oldies"), - N_("Other"), N_("Pop"), N_("R&B"), N_("Rap"), N_("Reggae"), - N_("Rock"), N_("Techno"), N_("Industrial"), N_("Alternative"), - N_("Ska"), N_("Death Metal"), N_("Pranks"), N_("Soundtrack"), - N_("Euro-Techno"), N_("Ambient"), N_("Trip-Hop"), N_("Vocal"), - N_("Jazz+Funk"), N_("Fusion"), N_("Trance"), N_("Classical"), - N_("Instrumental"), N_("Acid"), N_("House"), N_("Game"), - N_("Sound Clip"), N_("Gospel"), N_("Noise"), N_("AlternRock"), - N_("Bass"), N_("Soul"), N_("Punk"), N_("Space"), - N_("Meditative"), N_("Instrumental Pop"), - N_("Instrumental Rock"), N_("Ethnic"), N_("Gothic"), - N_("Darkwave"), N_("Techno-Industrial"), N_("Electronic"), - N_("Pop-Folk"), N_("Eurodance"), N_("Dream"), - N_("Southern Rock"), N_("Comedy"), N_("Cult"), - N_("Gangsta Rap"), N_("Top 40"), N_("Christian Rap"), - N_("Pop/Funk"), N_("Jungle"), N_("Native American"), - N_("Cabaret"), N_("New Wave"), N_("Psychedelic"), N_("Rave"), - N_("Showtunes"), N_("Trailer"), N_("Lo-Fi"), N_("Tribal"), - N_("Acid Punk"), N_("Acid Jazz"), N_("Polka"), N_("Retro"), - N_("Musical"), N_("Rock & Roll"), N_("Hard Rock"), N_("Folk"), - N_("Folk/Rock"), N_("National Folk"), N_("Swing"), - N_("Fast-Fusion"), N_("Bebob"), N_("Latin"), N_("Revival"), - N_("Celtic"), N_("Bluegrass"), N_("Avantgarde"), - N_("Gothic Rock"), N_("Progressive Rock"), - N_("Psychedelic Rock"), N_("Symphonic Rock"), N_("Slow Rock"), - N_("Big Band"), N_("Chorus"), N_("Easy Listening"), - N_("Acoustic"), N_("Humour"), N_("Speech"), N_("Chanson"), - N_("Opera"), N_("Chamber Music"), N_("Sonata"), N_("Symphony"), - N_("Booty Bass"), N_("Primus"), N_("Porn Groove"), - N_("Satire"), N_("Slow Jam"), N_("Club"), N_("Tango"), - N_("Samba"), N_("Folklore"), N_("Ballad"), N_("Power Ballad"), - N_("Rhythmic Soul"), N_("Freestyle"), N_("Duet"), - N_("Punk Rock"), N_("Drum Solo"), N_("A Cappella"), - N_("Euro-House"), N_("Dance Hall"), N_("Goa"), - N_("Drum & Bass"), N_("Club-House"), N_("Hardcore"), - N_("Terror"), N_("Indie"), N_("BritPop"), N_("Negerpunk"), - N_("Polsk Punk"), N_("Beat"), N_("Christian Gangsta Rap"), - N_("Heavy Metal"), N_("Black Metal"), N_("Crossover"), - N_("Contemporary Christian"), N_("Christian Rock"), - N_("Merengue"), N_("Salsa"), N_("Thrash Metal"), - N_("Anime"), N_("JPop"), N_("Synthpop") -}; +/* This is by no means intended to be a complete list. If it is not short, it + * is useless: scrolling through ten pages of dropdown list is more work than + * typing out the genre. */ + +static const gchar * genre_table[] = { + N_("Acid Jazz"), + N_("Acid Rock"), + N_("Ambient"), + N_("Bebop"), + N_("Bluegrass"), + N_("Blues"), + N_("Chamber Music"), + N_("Classical"), + N_("Country"), + N_("Death Metal"), + N_("Disco"), + N_("Easy Listening"), + N_("Folk"), + N_("Funk"), + N_("Gangsta Rap"), + N_("Gospel"), + N_("Grunge"), + N_("Hard Rock"), + N_("Heavy Metal"), + N_("Hip-hop"), + N_("House"), + N_("Jazz"), + N_("Jungle"), + N_("Metal"), + N_("New Age"), + N_("New Wave"), + N_("Noise"), + N_("Pop"), + N_("Punk Rock"), + N_("Rap"), + N_("Reggae"), + N_("Rock"), + N_("Rock and Roll"), + N_("Rhythm and Blues"), + N_("Ska"), + N_("Soul"), + N_("Swing"), + N_("Techno"), + N_("Trip-hop")}; static void set_entry_str_from_field (GtkWidget * widget, const Tuple * tuple, gint fieldn, gboolean editable) @@ -181,18 +177,14 @@ static void infowin_label_set_text (GtkWidget * widget, const gchar * text) gtk_label_set_use_markup ((GtkLabel *) widget, TRUE); } -static void infowin_entry_set_image (GtkWidget * widget, const char * text) +static void infowin_entry_set_image (GtkWidget * widget, gint list, gint entry) { - GdkPixbuf * pixbuf; - - pixbuf = gdk_pixbuf_new_from_file (text, NULL); - g_return_if_fail (pixbuf != NULL); - - if (strcmp (DATA_DIR "/images/audio.png", text)) - audgui_pixbuf_scale_within (& pixbuf, aud_cfg->filepopup_pixelsize); + GdkPixbuf * p = audgui_pixbuf_for_entry (list, entry); + g_return_if_fail (p); - gtk_image_set_from_pixbuf ((GtkImage *) widget, pixbuf); - g_object_unref (pixbuf); + audgui_pixbuf_scale_within (& p, aud_cfg->filepopup_pixelsize); + gtk_image_set_from_pixbuf ((GtkImage *) widget, p); + g_object_unref ((GObject *) p); } static void clear_infowin (void) @@ -221,8 +213,7 @@ static void clear_infowin (void) something_changed = FALSE; can_write = FALSE; gtk_widget_set_sensitive (btn_apply, FALSE); - - infowin_entry_set_image (image_artwork, DATA_DIR "/images/audio.png"); + gtk_image_clear ((GtkImage *) image_artwork); } static void entry_changed (GtkEditable * editable, void * unused) @@ -252,7 +243,7 @@ static void ministatus_display_message (const gchar * text) gtk_label_set_use_markup ((GtkLabel *) label_mini_status, TRUE); g_free (tmp); - g_timeout_add (STATUS_TIMEOUT, (GSourceFunc) ministatus_timeout_proc, + g_timeout_add (AUDGUI_STATUS_TIMEOUT, (GSourceFunc) ministatus_timeout_proc, label_mini_status); } @@ -281,80 +272,6 @@ static void infowin_update_tuple (void * unused) mowgli_object_unref (tuple); } -/** - * Looks up an icon from a NULL-terminated list of icon names. - * - * size: the requested size - * name: the default name - * ... : a NULL-terminated list of alternates - */ -static GdkPixbuf * themed_icon_lookup (gint size, const gchar * name, ...) -{ - GtkIconTheme * icon_theme; - GdkPixbuf * pixbuf; - const gchar * n; - va_list par; - - icon_theme = gtk_icon_theme_get_default (); - pixbuf = gtk_icon_theme_load_icon (icon_theme, name, size, 0, NULL); - - if (pixbuf != NULL) - return pixbuf; - - /* fallback */ - va_start (par, name); - - while ((n = va_arg (par, const gchar *)) != NULL) - { - pixbuf = gtk_icon_theme_load_icon (icon_theme, n, size, 0, NULL); - - if (pixbuf) - { - va_end (par); - return pixbuf; - } - } - - va_end (par); - return NULL; -} - -/** - * Intelligently looks up an icon for a mimetype. Supports - * HIDEOUSLY BROKEN gnome icon naming scheme too. - * - * size : the requested size - * mime_type: the mime type. - */ -static GdkPixbuf * mime_icon_lookup (gint size, const gchar * mime_type) -{ - gchar * mime_as_is; /* audio-x-mp3 */ - gchar * mime_gnome; /* gnome-mime-audio-x-mp3 */ - gchar * mime_generic; /* audio-x-generic */ - gchar * mime_gnome_generic; /* gnome-mime-audio */ - GdkPixbuf * icon = NULL; - gchar * * s = g_strsplit (mime_type, "/", 2); - - if (s[1] != NULL) - { - mime_as_is = g_strdup_printf ("%s-%s", s[0], s[1]); - mime_gnome = g_strdup_printf ("gnome-mime-%s-%s", s[0], s[1]); - mime_generic = g_strdup_printf ("%s-x-generic", s[0]); - mime_gnome_generic = g_strdup_printf ("gnome-mime-%s", s[0]); - - icon = themed_icon_lookup (size, mime_as_is, mime_gnome, mime_generic, - mime_gnome_generic, s[0], NULL); /* s[0] is category */ - - g_free (mime_gnome_generic); - g_free (mime_generic); - g_free (mime_gnome); - g_free (mime_as_is); - } - - g_strfreev (s); - return icon; -} - gboolean genre_fill (GtkWidget * combo) { GList * list = NULL; @@ -367,7 +284,7 @@ gboolean genre_fill (GtkWidget * combo) list = g_list_sort (list, (GCompareFunc) strcmp); for (node = list; node != NULL; node = node->next) - gtk_combo_box_append_text ((GtkComboBox *) combo, node->data); + gtk_combo_box_text_append_text ((GtkComboBoxText *) combo, node->data); g_list_free (list); return FALSE; @@ -378,9 +295,7 @@ void create_infowin (void) GtkWidget * hbox; GtkWidget * hbox_status_and_bbox; GtkWidget * vbox0; - GtkWidget * vbox1; GtkWidget * vbox2; - GtkWidget * vbox3; GtkWidget * label_title; GtkWidget * label_artist; GtkWidget * label_album; @@ -388,8 +303,6 @@ void create_infowin (void) GtkWidget * label_genre; GtkWidget * label_year; GtkWidget * label_track; - GtkWidget * label_location; - GtkWidget * label_general; GtkWidget * label_format; GtkWidget * label_quality_label; GtkWidget * label_bitrate_label; @@ -399,10 +312,6 @@ void create_infowin (void) GtkWidget * bbox_close; GtkWidget * btn_close; GtkWidget * alignment; - GtkWidget * separator; - GtkWidget * scrolledwindow; - GtkTreeViewColumn * column; - GtkCellRenderer * renderer; infowin = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width ((GtkContainer *) infowin, 6); @@ -416,47 +325,25 @@ void create_infowin (void) hbox = gtk_hbox_new (FALSE, 6); gtk_box_pack_start ((GtkBox *) vbox0, hbox, TRUE, TRUE, 0); - image_artwork = gtk_image_new (); - gtk_box_pack_start ((GtkBox *) hbox, image_artwork, FALSE, FALSE, 0); - gtk_misc_set_alignment ((GtkMisc *) image_artwork, 0.5, 0); - gtk_image_set_from_file ((GtkImage *) image_artwork, DATA_DIR - "/images/audio.png"); - separator = gtk_vseparator_new (); - gtk_box_pack_start ((GtkBox *) hbox, separator, FALSE, FALSE, 0); - - vbox1 = gtk_vbox_new (FALSE, 0); - gtk_box_pack_start ((GtkBox *) hbox, vbox1, TRUE, TRUE, 0); - - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox1, alignment, TRUE, TRUE, 0); + vbox2 = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start ((GtkBox *) hbox, vbox2, TRUE, TRUE, 0); - vbox2 = gtk_vbox_new (FALSE, 0); - gtk_container_add ((GtkContainer *) alignment, vbox2); - - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox1, alignment, TRUE, TRUE, 0); - - vbox3 = gtk_vbox_new(FALSE, 0); - gtk_container_add ((GtkContainer *) alignment, vbox3); - - label_general = gtk_label_new (_("<span size=\"small\">General</span>")); - gtk_box_pack_start ((GtkBox *) vbox2, label_general, FALSE, FALSE, 0); - gtk_label_set_use_markup ((GtkLabel *) label_general, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_general, 0, 0.5); + image_artwork = gtk_image_new (); + gtk_box_pack_start ((GtkBox *) vbox2, image_artwork, TRUE, TRUE, 0); - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 6, 0, 0); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); + location_text = gtk_label_new (""); + gtk_widget_set_size_request (location_text, 200, -1); + gtk_label_set_line_wrap ((GtkLabel *) location_text, TRUE); + gtk_label_set_line_wrap_mode ((GtkLabel *) location_text, + PANGO_WRAP_WORD_CHAR); + gtk_label_set_selectable ((GtkLabel *) location_text, TRUE); + gtk_box_pack_start ((GtkBox *) vbox2, location_text, FALSE, FALSE, 0); codec_hbox = gtk_hbox_new (FALSE, 6); - gtk_container_add ((GtkContainer *) alignment, codec_hbox); - - image_fileicon = gtk_image_new_from_stock (GTK_STOCK_MISSING_IMAGE, - GTK_ICON_SIZE_DIALOG); - gtk_box_pack_start ((GtkBox *) codec_hbox, image_fileicon, FALSE, FALSE, 0); + gtk_box_pack_start ((GtkBox *) vbox2, codec_hbox, FALSE, FALSE, 0); codec_table = gtk_table_new(3, 2, FALSE); - gtk_table_set_row_spacings ((GtkTable *) codec_table, 6); + gtk_table_set_row_spacings ((GtkTable *) codec_table, 3); gtk_table_set_col_spacings ((GtkTable *) codec_table, 12); gtk_box_pack_start ((GtkBox *) codec_hbox, codec_table, FALSE, FALSE, 0); @@ -494,6 +381,9 @@ void create_infowin (void) gtk_table_attach ((GtkTable *) codec_table, label_bitrate, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 0, 0); + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start ((GtkBox *) hbox, vbox2, TRUE, TRUE, 0); + label_title = gtk_label_new (_("<span size=\"small\">Title</span>")); gtk_box_pack_start ((GtkBox *) vbox2, label_title, FALSE, FALSE, 0); gtk_label_set_use_markup ((GtkLabel *) label_title, TRUE); @@ -550,8 +440,8 @@ void create_infowin (void) alignment = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0); - entry_genre = gtk_combo_box_entry_new_text (); + entry_genre = gtk_combo_box_text_new_with_entry (); gtk_container_add ((GtkContainer *) alignment, entry_genre); g_signal_connect (entry_genre, "changed", (GCallback) entry_changed, NULL); g_idle_add ((GSourceFunc) genre_fill, entry_genre); @@ -585,77 +475,6 @@ void create_infowin (void) GTK_FILL, 0, 0, 0); g_signal_connect (entry_track, "changed", (GCallback) entry_changed, NULL); - label_location = gtk_label_new (_("<span size=\"small\">Location</span>")); - gtk_box_pack_start ((GtkBox *) vbox2, label_location, FALSE, FALSE, 0); - gtk_label_set_use_markup ((GtkLabel *) label_location, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_location, 0, 0.5); - - alignment = gtk_alignment_new (0, 0, 0, 0); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 3, 6, 25, 0); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); - - location_text = gtk_label_new (""); - gtk_widget_set_size_request (location_text, 375, -1); - gtk_label_set_line_wrap ((GtkLabel *) location_text, TRUE); -#if GTK_CHECK_VERSION (2, 10, 0) - gtk_label_set_line_wrap_mode ((GtkLabel *) location_text, - PANGO_WRAP_WORD_CHAR); -#endif - gtk_label_set_selectable ((GtkLabel *) location_text, TRUE); - gtk_container_add ((GtkContainer *) alignment, location_text); - - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - hbox = gtk_hbox_new (FALSE, 0); - gtk_container_add ((GtkContainer *) alignment, hbox); - gtk_box_pack_start ((GtkBox *) vbox3, alignment, TRUE, TRUE, 0); - - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_alignment_set_padding ((GtkAlignment *) (alignment), 0, 6, 0, 0); - arrow_rawdata = gtk_expander_new - (_("<span size=\"small\">Raw Metadata</span>")); - gtk_expander_set_use_markup ((GtkExpander *) arrow_rawdata, TRUE); - gtk_container_add ((GtkContainer *) alignment, arrow_rawdata); - gtk_box_pack_start ((GtkBox *) hbox, alignment, TRUE, TRUE, 0); - - scrolledwindow = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy ((GtkScrolledWindow *) scrolledwindow, - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) scrolledwindow, - GTK_SHADOW_IN); - gtk_container_add ((GtkContainer *) arrow_rawdata, scrolledwindow); - - treeview_rawdata = gtk_tree_view_new (); - gtk_container_add ((GtkContainer *) scrolledwindow, treeview_rawdata); - gtk_tree_view_set_rules_hint ((GtkTreeView *) treeview_rawdata, TRUE); - gtk_tree_view_set_reorderable ((GtkTreeView *) treeview_rawdata, TRUE); - gtk_widget_set_size_request (treeview_rawdata, -1, 130); - - column = gtk_tree_view_column_new (); - gtk_tree_view_column_set_title (column, _("Key")); - gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_column_set_spacing (column, 4); - gtk_tree_view_column_set_resizable (column, FALSE); - gtk_tree_view_column_set_fixed_width (column, 50); - - renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (column, renderer, FALSE); - gtk_tree_view_column_set_attributes (column, renderer, "text", RAWDATA_KEY, - NULL); - gtk_tree_view_append_column ((GtkTreeView *) treeview_rawdata, column); - - column = gtk_tree_view_column_new (); - gtk_tree_view_column_set_title (column, _("Value")); - gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_column_set_spacing (column, 4); - gtk_tree_view_column_set_resizable (column, FALSE); - gtk_tree_view_column_set_fixed_width (column, 50); - - renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (column, renderer, FALSE); - gtk_tree_view_column_set_attributes (column, renderer, "text", - RAWDATA_VALUE, NULL); - gtk_tree_view_append_column ((GtkTreeView *) treeview_rawdata, column); - hbox_status_and_bbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start ((GtkBox *) vbox0, hbox_status_and_bbox, FALSE, FALSE, 0); @@ -686,73 +505,13 @@ void create_infowin (void) audgui_hide_on_escape (infowin); gtk_widget_show_all (vbox0); + gtk_widget_grab_focus (entry_title); } -/* Converts filenames (in place) for easy reading, thus: - * - * file:///home/me/Music/My song.ogg -> Music - * My song.ogg - * - * file:///media/disk/My song.ogg -> / - * media - * disk - * My song.ogg - */ -static gchar * easy_read_filename (gchar * file) -{ - const gchar * home; - gint len; - - if (strncmp (file, "file:///", 8)) - return file; - - home = getenv ("HOME"); - len = (home == NULL) ? 0 : strlen (home); - len = (len > 0 && home[len - 1] == '/') ? len - 1 : len; - - if (len > 0 && ! strncmp (file + 7, home, len) && file[len + 7] == '/') - { - string_replace_char (file + len + 8, '/', '\n'); - return file + len + 8; - } - - string_replace_char (file + 7, '/', '\n'); - return file + 6; -} - -static gboolean set_image_from_album_art (const gchar * filename, InputPlugin * - decoder) +static void infowin_show (gint list, gint entry, const gchar * filename, + const Tuple * tuple, PluginHandle * decoder, gboolean updating_enabled) { - GdkPixbuf * pixbuf = NULL; - void * data; - gint size; - - if (aud_file_read_image (filename, decoder, & data, & size)) - { - pixbuf = audgui_pixbuf_from_data (data, size); - g_free (data); - } - - if (pixbuf == NULL) - return FALSE; - - audgui_pixbuf_scale_within (& pixbuf, aud_cfg->filepopup_pixelsize); - gtk_image_set_from_pixbuf ((GtkImage *) image_artwork, pixbuf); - g_object_unref (pixbuf); - return TRUE; -} - -static void infowin_show (const gchar * filename, const Tuple * tuple, - InputPlugin * decoder, gboolean updating_enabled) -{ - const gchar * string; gchar * tmp; - GdkPixbuf * icon; - GtkTreeIter iter; - GtkListStore * store; - mowgli_dictionary_iteration_state_t state; - TupleValue * tvalue; - gint i; if (infowin == NULL) create_infowin (); @@ -772,18 +531,8 @@ static void infowin_show (const gchar * filename, const Tuple * tuple, set_entry_str_from_field (gtk_bin_get_child ((GtkBin *) entry_genre), tuple, FIELD_GENRE, updating_enabled); - tmp = g_strdup (filename); - string_decode_percent (tmp); - - /* Convert invalid UTF-8 URI's quietly. */ - if (! g_utf8_validate (tmp, -1, NULL)) - { - gchar * copy = str_to_utf8 (tmp); - g_free (tmp); - tmp = copy; - } - - gtk_label_set_text ((GtkLabel *) location_text, easy_read_filename (tmp)); + tmp = uri_to_display (filename); + gtk_label_set_text ((GtkLabel *) location_text, tmp); g_free (tmp); set_entry_int_from_field (entry_year, tuple, FIELD_YEAR, updating_enabled); @@ -805,69 +554,7 @@ static void infowin_show (const gchar * filename, const Tuple * tuple, else infowin_label_set_text (label_bitrate, NULL); - string = tuple_get_string (tuple, FIELD_MIMETYPE, NULL); - icon = mime_icon_lookup (48, string != NULL ? string : "audio/x-generic"); - - if (icon != NULL) - { - gtk_image_set_from_pixbuf ((GtkImage *) image_fileicon, icon); - g_object_unref (icon); - } - - if (! set_image_from_album_art (filename, decoder)) - { - tmp = aud_get_associated_image_file (filename); - - if (tmp != NULL) - { - infowin_entry_set_image (image_artwork, tmp); - g_free (tmp); - } - } - - store = gtk_list_store_new (RAWDATA_N_COLS, G_TYPE_STRING, G_TYPE_STRING); - - for (i = 0; i < FIELD_LAST; i ++) - { - gchar * value; - - if (tuple->values[i] == NULL) - continue; - - if (tuple->values[i]->type == TUPLE_INT) - value = g_strdup_printf ("%d", tuple->values[i]->value.integer); - else if (tuple->values[i]->value.string != NULL) - value = g_strdup (tuple->values[i]->value.string); - else - continue; - - gtk_list_store_append (store, & iter); - gtk_list_store_set (store, & iter, RAWDATA_KEY, tuple_fields[i].name, - RAWDATA_VALUE, value, -1); - g_free (value); - } - - /* non-standard values are stored in a dictionary. */ - MOWGLI_DICTIONARY_FOREACH (tvalue, & state, tuple->dict) - { - gchar * value; - - if (tvalue->type == TUPLE_INT) - value = g_strdup_printf ("%d", tvalue->value.integer); - else if (tvalue->value.string != NULL) - value = g_strdup (tvalue->value.string); - else - continue; - - gtk_list_store_append (store, & iter); - gtk_list_store_set (store, & iter, RAWDATA_KEY, state.cur->key, - RAWDATA_VALUE, value, -1); - g_free (value); - } - - gtk_tree_view_set_model ((GtkTreeView *) treeview_rawdata, (GtkTreeModel *) - store); - g_object_unref (store); + infowin_entry_set_image (image_artwork, list, entry); gtk_window_present ((GtkWindow *) infowin); } @@ -877,7 +564,8 @@ void audgui_infowin_show (gint playlist, gint entry) const gchar * filename = aud_playlist_entry_get_filename (playlist, entry); g_return_if_fail (filename != NULL); - InputPlugin * decoder = aud_file_find_decoder (filename, FALSE); + PluginHandle * decoder = aud_playlist_entry_get_decoder (playlist, entry, + FALSE); if (decoder == NULL) return; @@ -895,8 +583,8 @@ void audgui_infowin_show (gint playlist, gint entry) return; } - infowin_show (filename, tuple, decoder, aud_file_can_write_tuple (filename, - decoder)); + infowin_show (playlist, entry, filename, tuple, decoder, + aud_file_can_write_tuple (filename, decoder)); } void audgui_infowin_show_current (void) diff --git a/src/libaudgui/libaudgui-gtk.h b/src/libaudgui/libaudgui-gtk.h index f9316eb..f093851 100644 --- a/src/libaudgui/libaudgui-gtk.h +++ b/src/libaudgui/libaudgui-gtk.h @@ -34,17 +34,6 @@ void audgui_playlist_manager_update(void); void audgui_playlist_manager_ui_show(GtkWidget *mainwin); void audgui_playlist_manager_destroy(void); -/* library-store.c */ -enum -{ - AUDGUI_LIBRARY_STORE_TITLE, /* G_TYPE_STRING */ - AUDGUI_LIBRARY_STORE_FONT_WEIGHT, /* PANGO_TYPE_WEIGHT */ - AUDGUI_LIBRARY_STORE_ENTRY_COUNT, /* G_TYPE_INT */ - AUDGUI_LIBRARY_STORE_COLUMNS -}; - -GtkTreeModel * audgui_get_library_store (void); - /* util.c */ void audgui_hide_on_delete (GtkWidget * widget); void audgui_hide_on_escape (GtkWidget * widget); @@ -54,7 +43,8 @@ void audgui_simple_message (GtkWidget * * widget, GtkMessageType type, void audgui_connect_check_box (GtkWidget * box, gboolean * setting); GdkPixbuf * audgui_pixbuf_from_data (void * data, gint size); -GdkPixbuf * audgui_pixbuf_for_file (const gchar * filename); +GdkPixbuf * audgui_pixbuf_for_entry (gint playlist, gint entry); +GdkPixbuf * audgui_pixbuf_for_current (void); void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, gint size); #endif diff --git a/src/libaudgui/libaudgui.h b/src/libaudgui/libaudgui.h index 7fc4b1c..d96c933 100644 --- a/src/libaudgui/libaudgui.h +++ b/src/libaudgui/libaudgui.h @@ -45,6 +45,7 @@ void audgui_hide_about_window(void); /* confirm.c */ void audgui_confirm_playlist_delete (gint playlist); +void audgui_show_playlist_rename (gint playlist); /* equalizer.c */ void audgui_show_equalizer_window (void); diff --git a/src/libaudgui/library-store.c b/src/libaudgui/library-store.c deleted file mode 100644 index 4439e44..0000000 --- a/src/libaudgui/library-store.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * libaudgui/library-store.c - * Copyright 2010 John Lindgren - * - * This file is part of Audacious. - * - * Audacious 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 or version 3 of the License. - * - * Audacious 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 - * Audacious. If not, see <http://www.gnu.org/licenses/>. - * - * The Audacious team does not consider modular code linking to Audacious or - * using our public API to be a derived work. - */ - -#include <audacious/playlist.h> -#include <libaudcore/hook.h> - -#include "libaudgui-gtk.h" - -typedef GObjectClass LibraryStoreClass; - -typedef struct -{ - GObject parent; - gint rows, active; -} -LibraryStore; - -static void library_store_init (LibraryStore * store) -{ - store->rows = aud_playlist_count (); - store->active = aud_playlist_get_active (); -} - -static GtkTreeModelFlags library_store_get_flags (GtkTreeModel * model) -{ - return GTK_TREE_MODEL_LIST_ONLY; -} - -static gint library_store_get_n_columns (GtkTreeModel * model) -{ - return AUDGUI_LIBRARY_STORE_COLUMNS; -} - -static GType library_store_get_column_type (GtkTreeModel * model, gint column) -{ - switch (column) - { - case AUDGUI_LIBRARY_STORE_TITLE: - return G_TYPE_STRING; - case AUDGUI_LIBRARY_STORE_FONT_WEIGHT: - return PANGO_TYPE_WEIGHT; - case AUDGUI_LIBRARY_STORE_ENTRY_COUNT: - return G_TYPE_INT; - default: - return G_TYPE_INVALID; - } -} - -static gboolean library_store_get_iter (GtkTreeModel * model, GtkTreeIter * - iter, GtkTreePath * path) -{ - LibraryStore * store = (LibraryStore *) model; - gint playlist = gtk_tree_path_get_indices (path)[0]; - - if (playlist < 0 || playlist >= store->rows) - return FALSE; - - iter->user_data = GINT_TO_POINTER (playlist); - return TRUE; -} - -static GtkTreePath * library_store_get_path (GtkTreeModel * model, GtkTreeIter * - iter) -{ - return gtk_tree_path_new_from_indices (GPOINTER_TO_INT (iter->user_data), -1); -} - -static void library_store_get_value (GtkTreeModel * model, GtkTreeIter * iter, - gint column, GValue * value) -{ - LibraryStore * store = (LibraryStore *) model; - gint playlist = GPOINTER_TO_INT (iter->user_data); - - switch (column) - { - case AUDGUI_LIBRARY_STORE_TITLE: - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, aud_playlist_get_title (playlist)); - break; - case AUDGUI_LIBRARY_STORE_FONT_WEIGHT: - g_value_init (value, PANGO_TYPE_WEIGHT); - g_value_set_enum (value, (playlist == store->active) ? PANGO_WEIGHT_BOLD - : PANGO_WEIGHT_NORMAL); - break; - case AUDGUI_LIBRARY_STORE_ENTRY_COUNT: - g_value_init (value, G_TYPE_INT); - g_value_set_int (value, aud_playlist_entry_count (playlist)); - break; - } -} - -static gboolean library_store_iter_next (GtkTreeModel * model, GtkTreeIter * - iter) -{ - if (GPOINTER_TO_INT (iter->user_data) + 1 < aud_playlist_count ()) - { - iter->user_data = GINT_TO_POINTER (GPOINTER_TO_INT (iter->user_data) + 1); - return TRUE; - } - - return FALSE; -} - -static gboolean library_store_iter_children (GtkTreeModel * model, GtkTreeIter * - iter, GtkTreeIter * parent) -{ - if (parent == NULL) /* top level */ - { - /* there is always at least one playlist */ - iter->user_data = GINT_TO_POINTER (0); - return TRUE; - } - - return FALSE; -} - -static gboolean library_store_iter_has_child (GtkTreeModel * model, - GtkTreeIter * iter) -{ - return FALSE; -} - -static gint library_store_iter_n_children (GtkTreeModel * model, GtkTreeIter * - iter) -{ - LibraryStore * store = (LibraryStore *) model; - - if (iter == NULL) /* top level */ - return store->rows; - - return 0; -} - -static gboolean library_store_iter_nth_child (GtkTreeModel * model, - GtkTreeIter * iter, GtkTreeIter * parent, gint n) -{ - LibraryStore * store = (LibraryStore *) model; - - if (parent != NULL) /* not top level */ - return FALSE; - - if (n < 0 || n >= store->rows) - return FALSE; - - iter->user_data = GINT_TO_POINTER (n); - return TRUE; -} - -static gboolean library_store_iter_parent (GtkTreeModel * model, GtkTreeIter * - iter, GtkTreeIter * child) -{ - return FALSE; -} - -static void interface_init (GtkTreeModelIface * interface) -{ - interface->get_flags = library_store_get_flags; - interface->get_n_columns = library_store_get_n_columns; - interface->get_column_type = library_store_get_column_type; - interface->get_iter = library_store_get_iter; - interface->get_path = library_store_get_path; - interface->get_value = library_store_get_value; - interface->iter_next = library_store_iter_next; - interface->iter_children = library_store_iter_children; - interface->iter_has_child = library_store_iter_has_child; - interface->iter_n_children = library_store_iter_n_children; - interface->iter_nth_child = library_store_iter_nth_child; - interface->iter_parent = library_store_iter_parent; -} - -static const GInterfaceInfo interface_info = -{ - .interface_init = (GInterfaceInitFunc) interface_init, - .interface_finalize = NULL, - .interface_data = NULL, -}; - -static gboolean library_store_drag_data_get (GtkTreeDragSource * source, - GtkTreePath * path, GtkSelectionData * data) -{ - return gtk_tree_set_row_drag_data (data, (GtkTreeModel *) source, path); -} - -static gboolean library_store_drag_data_delete (GtkTreeDragSource * source, - GtkTreePath * path) -{ - return TRUE; -} - -static void source_init (GtkTreeDragSourceIface * interface) -{ - interface->drag_data_get = library_store_drag_data_get; - interface->drag_data_delete = library_store_drag_data_delete; -} - -static const GInterfaceInfo source_info = -{ - .interface_init = (GInterfaceInitFunc) source_init, - .interface_finalize = NULL, - .interface_data = NULL, -}; - -static gboolean library_store_drag_data_received (GtkTreeDragDest * dest, - GtkTreePath * dest_path, GtkSelectionData * data) -{ - LibraryStore * store = (LibraryStore *) dest; - GtkTreeModel * model; - GtkTreePath * source_path, * top; - gint from, to, count; - gint order[store->rows]; - - if (! gtk_tree_get_row_drag_data (data, & model, & source_path)) - return FALSE; - - from = gtk_tree_path_get_indices (source_path)[0]; - to = gtk_tree_path_get_indices (dest_path)[0]; - - /* GTK gives us the number of the row before which we are to put the row. - * We want the number of the row where the row will end up. */ - if (to > from) - to --; - - if (from < 0 || from >= store->rows || to < 0 || to >= store->rows) - return FALSE; - - aud_playlist_reorder (from, to, 1); - - for (count = 0; count < from; count ++) - order[count] = count; - - if (from < to) - { - for (count = from; count < to; count ++) - order[count] = count + 1; - } - else - { - for (count = to; count < from; count ++) - order[count + 1] = count; - } - - order[to] = from; - - top = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (model, top, NULL, order); - gtk_tree_path_free (top); - - return TRUE; -} - -gboolean library_store_row_drop_possible (GtkTreeDragDest * dest, - GtkTreePath * path, GtkSelectionData * selection_data) -{ - LibraryStore * store = (LibraryStore *) dest; - gint before = gtk_tree_path_get_indices (path)[0]; - - return (before >= 0 && before <= store->rows); -} - -static void dest_init (GtkTreeDragDestIface * interface) -{ - interface->drag_data_received = library_store_drag_data_received; - interface->row_drop_possible = library_store_row_drop_possible; -} - -static const GInterfaceInfo dest_info = -{ - .interface_init = (GInterfaceInitFunc) dest_init, - .interface_finalize = NULL, - .interface_data = NULL, -}; - -static GType library_store_get_type (void) -{ - static GType type = 0; - - if (! type) - { - type = g_type_register_static_simple (G_TYPE_OBJECT, "LibraryStore", - sizeof (LibraryStoreClass), NULL, sizeof (LibraryStore), - (GInstanceInitFunc) library_store_init, 0); - g_type_add_interface_static (type, GTK_TYPE_TREE_MODEL, & interface_info); - g_type_add_interface_static (type, GTK_TYPE_TREE_DRAG_SOURCE, - & source_info); - g_type_add_interface_static (type, GTK_TYPE_TREE_DRAG_DEST, & dest_info); - } - - return type; -} - -static void library_store_update (GtkTreeModel * model) -{ - LibraryStore * store = (LibraryStore *) model; - gint old_rows = store->rows; - GtkTreePath * path; - GtkTreeIter iter; - gint row; - - store->rows = aud_playlist_count (); - store->active = aud_playlist_get_active (); - - if (store->rows < old_rows) - { - path = gtk_tree_path_new_from_indices (store->rows, -1); - - for (row = store->rows; row < old_rows; row ++) - gtk_tree_model_row_deleted (model, path); - - gtk_tree_path_free (path); - old_rows = store->rows; - } - - path = gtk_tree_path_new_first (); - - for (row = 0; row < old_rows; row ++) - { - iter.user_data = GINT_TO_POINTER (row); - gtk_tree_model_row_changed (model, path, & iter); - gtk_tree_path_next (path); - } - - for (; row < store->rows; row ++) - { - iter.user_data = GINT_TO_POINTER (row); - gtk_tree_model_row_inserted (model, path, & iter); - gtk_tree_path_next (path); - } - - gtk_tree_path_free (path); -} - -static void update_cb (void * data, void * user_data) -{ - if (GPOINTER_TO_INT (data) >= PLAYLIST_UPDATE_STRUCTURE) - library_store_update ((GtkTreeModel *) user_data); -} - -GtkTreeModel * audgui_get_library_store (void) -{ - static GtkTreeModel * store = NULL; - - if (store == NULL) - { - store = (GtkTreeModel *) g_object_new (library_store_get_type (), NULL); - hook_associate ("playlist update", update_cb, store); - } - - return store; -} diff --git a/src/libaudgui/list.c b/src/libaudgui/list.c new file mode 100755 index 0000000..7a39cde --- /dev/null +++ b/src/libaudgui/list.c @@ -0,0 +1,758 @@ +/* + * list.c + * Copyright 2011 John Lindgren + * + * This file is part of Audacious. + * + * Audacious 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 or version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include "list.h" + +#include <audacious/gtk-compat.h> + +enum {HIGHLIGHT_COLUMN, RESERVED_COLUMNS}; + +#define PATH_IS_SELECTED(w, p) (gtk_tree_selection_path_is_selected \ + (gtk_tree_view_get_selection ((GtkTreeView *) (w)), (p))) + +typedef struct { + GObject parent; + const AudguiListCallbacks * cbs; + void * user; + gint rows, highlight; + gint columns; + GList * column_types; + gboolean frozen, blocked; + gboolean dragging; + gboolean clicked_row, receive_row; + gint scroll_source, scroll_speed; +} ListModel; + +/* ==== MODEL ==== */ + +static GtkTreeModelFlags list_model_get_flags (GtkTreeModel * model) +{ + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint list_model_get_n_columns (GtkTreeModel * model) +{ + return ((ListModel *) model)->columns; +} + +static GType list_model_get_column_type (GtkTreeModel * _model, gint column) +{ + ListModel * model = (ListModel *) _model; + g_return_val_if_fail (column >= 0 && column < model->columns, G_TYPE_INVALID); + + if (column == HIGHLIGHT_COLUMN) + return PANGO_TYPE_WEIGHT; + + return GPOINTER_TO_INT (g_list_nth_data (model->column_types, column - + RESERVED_COLUMNS)); +} + +static gboolean list_model_get_iter (GtkTreeModel * model, GtkTreeIter * iter, + GtkTreePath * path) +{ + gint row = gtk_tree_path_get_indices (path)[0]; + if (row < 0 || row >= ((ListModel *) model)->rows) + return FALSE; + iter->user_data = GINT_TO_POINTER (row); + return TRUE; +} + +static GtkTreePath * list_model_get_path (GtkTreeModel * model, + GtkTreeIter * iter) +{ + gint row = GPOINTER_TO_INT (iter->user_data); + g_return_val_if_fail (row >= 0 && row < ((ListModel *) model)->rows, NULL); + return gtk_tree_path_new_from_indices (row, -1); +} + +static void list_model_get_value (GtkTreeModel * _model, GtkTreeIter * iter, + gint column, GValue * value) +{ + ListModel * model = (ListModel *) _model; + gint row = GPOINTER_TO_INT (iter->user_data); + g_return_if_fail (column >= 0 && column < model->columns); + g_return_if_fail (row >= 0 && row < model->rows); + + if (column == HIGHLIGHT_COLUMN) + { + g_value_init (value, PANGO_TYPE_WEIGHT); + g_value_set_enum (value, row == model->highlight ? PANGO_WEIGHT_BOLD : + PANGO_WEIGHT_NORMAL); + return; + } + + g_value_init (value, GPOINTER_TO_INT (g_list_nth_data (model->column_types, + column - RESERVED_COLUMNS))); + model->cbs->get_value (model->user, row, column - RESERVED_COLUMNS, value); +} + +static gboolean list_model_iter_next (GtkTreeModel * _model, GtkTreeIter * iter) +{ + ListModel * model = (ListModel *) _model; + gint row = GPOINTER_TO_INT (iter->user_data); + g_return_val_if_fail (row >= 0 && row < model->rows, FALSE); + if (row + 1 >= model->rows) + return FALSE; + iter->user_data = GINT_TO_POINTER (row + 1); + return TRUE; +} + +static gboolean list_model_iter_children (GtkTreeModel * model, + GtkTreeIter * iter, GtkTreeIter * parent) +{ + if (parent || ((ListModel *) model)->rows < 1) + return FALSE; + iter->user_data = GINT_TO_POINTER (0); + return TRUE; +} + +static gboolean list_model_iter_has_child (GtkTreeModel * model, + GtkTreeIter * iter) +{ + return FALSE; +} + +static gint list_model_iter_n_children (GtkTreeModel * model, GtkTreeIter * iter) +{ + return iter ? 0 : ((ListModel *) model)->rows; +} + +static gboolean list_model_iter_nth_child (GtkTreeModel * model, + GtkTreeIter * iter, GtkTreeIter * parent, gint n) +{ + if (parent || n < 0 || n >= ((ListModel *) model)->rows) + return FALSE; + iter->user_data = GINT_TO_POINTER (n); + return TRUE; +} + +static gboolean list_model_iter_parent (GtkTreeModel * model, + GtkTreeIter * iter, GtkTreeIter * child) +{ + return FALSE; +} + +static void iface_init (GtkTreeModelIface * iface) +{ + iface->get_flags = list_model_get_flags; + iface->get_n_columns = list_model_get_n_columns; + iface->get_column_type = list_model_get_column_type; + iface->get_iter = list_model_get_iter; + iface->get_path = list_model_get_path; + iface->get_value = list_model_get_value; + iface->iter_next = list_model_iter_next; + iface->iter_children = list_model_iter_children; + iface->iter_has_child = list_model_iter_has_child; + iface->iter_n_children = list_model_iter_n_children; + iface->iter_nth_child = list_model_iter_nth_child; + iface->iter_parent = list_model_iter_parent; +} + +static const GInterfaceInfo iface_info = +{ + .interface_init = (GInterfaceInitFunc) iface_init, + .interface_finalize = NULL, + .interface_data = NULL, +}; + +static GType list_model_get_type (void) +{ + static GType type = G_TYPE_INVALID; + if (type == G_TYPE_INVALID) + { + type = g_type_register_static_simple (G_TYPE_OBJECT, "AudguiListModel", + sizeof (GObjectClass), NULL, sizeof (ListModel), NULL, 0); + g_type_add_interface_static (type, GTK_TYPE_TREE_MODEL, & iface_info); + } + return type; +} + +/* ==== CALLBACKS ==== */ + +static gboolean select_allow_cb (GtkTreeSelection * sel, GtkTreeModel * model, + GtkTreePath * path, gboolean was, void * user) +{ + return ! ((ListModel *) model)->frozen; +} + +static void select_row_cb (GtkTreeModel * _model, GtkTreePath * path, + GtkTreeIter * iter, void * user) +{ + ListModel * model = (ListModel *) _model; + gint row = gtk_tree_path_get_indices (path)[0]; + g_return_if_fail (row >= 0 && row < model->rows); + model->cbs->set_selected (model->user, row, TRUE); +} + +static void select_cb (GtkTreeSelection * sel, ListModel * model) +{ + if (model->blocked) + return; + model->cbs->select_all (model->user, FALSE); + gtk_tree_selection_selected_foreach (sel, select_row_cb, NULL); +} + +static void activate_cb (GtkTreeView * tree, GtkTreePath * path, + GtkTreeViewColumn * col, ListModel * model) +{ + gint row = gtk_tree_path_get_indices (path)[0]; + g_return_if_fail (row >= 0 && row < model->rows); + model->cbs->activate_row (model->user, row); +} + +static gboolean button_press_cb (GtkWidget * widget, GdkEventButton * event, + ListModel * model) +{ + GtkTreePath * path = NULL; + gtk_tree_view_get_path_at_pos ((GtkTreeView *) widget, event->x, event->y, + & path, NULL, NULL, NULL); + + if (event->type == GDK_BUTTON_PRESS && event->button == 3 && + model->cbs->right_click) + { + /* Only allow GTK to select this row if it is not already selected. We + * don't want to clear a multiple selection. */ + if (path) + { + if (PATH_IS_SELECTED (widget, path)) + model->frozen = TRUE; + gtk_tree_view_set_cursor ((GtkTreeView *) widget, path, NULL, FALSE); + model->frozen = FALSE; + } + + model->cbs->right_click (model->user, event); + + if (path) + gtk_tree_path_free (path); + return TRUE; + } + + /* Only allow GTK to select this row if it is not already selected. If we + * are going to be dragging, we don't want to clear a multiple selection. + * If this is just a simple click, we will clear the multiple selection in + * button_release_cb. */ + if (event->type == GDK_BUTTON_PRESS && event->button == 1 && ! (event->state + & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) && path && PATH_IS_SELECTED (widget, + path)) + model->frozen = TRUE; + + if (path) + model->clicked_row = gtk_tree_path_get_indices (path)[0]; + else + model->clicked_row = -1; + + if (path) + gtk_tree_path_free (path); + return FALSE; +} + +static gboolean button_release_cb (GtkWidget * widget, GdkEventButton * event, + ListModel * model) +{ + /* If button_press_cb set "frozen", and we were not dragging, we need to + * clear a multiple selection. */ + if (model->frozen && model->clicked_row >= 0 && model->clicked_row < + model->rows) + { + model->frozen = FALSE; + GtkTreePath * path = gtk_tree_path_new_from_indices (model->clicked_row, + -1); + gtk_tree_view_set_cursor ((GtkTreeView *) widget, path, NULL, FALSE); + gtk_tree_path_free (path); + } + + return FALSE; +} + +/* ==== DRAG AND DROP ==== */ + +static void drag_begin (GtkWidget * widget, GdkDragContext * context, + ListModel * model) +{ + g_signal_stop_emission_by_name (widget, "drag-begin"); + + model->dragging = TRUE; +} + +static void drag_end (GtkWidget * widget, GdkDragContext * context, + ListModel * model) +{ + g_signal_stop_emission_by_name (widget, "drag-end"); + + model->dragging = FALSE; + model->clicked_row = -1; +} + +static void drag_data_get (GtkWidget * widget, GdkDragContext * context, + GtkSelectionData * sel, guint info, guint time, ListModel * model) +{ + g_signal_stop_emission_by_name (widget, "drag-data-get"); + + void * data = NULL; + gint length = 0; + model->cbs->get_data (model->user, & data, & length); + gtk_selection_data_set (sel, gdk_atom_intern (model->cbs->data_type, FALSE), + 8, data, length); + g_free (data); +} + +static gint calc_drop_row (ListModel * model, GtkWidget * widget, gint x, gint y) +{ + gint row = audgui_list_row_at_point (widget, x, y); + if (row < 0) + row = model->rows; + return row; +} + +static void stop_autoscroll (ListModel * model) +{ + if (! model->scroll_source) + return; + + g_source_remove (model->scroll_source); + model->scroll_source = 0; + model->scroll_speed = 0; +} + +static gboolean autoscroll (GtkWidget * widget) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) widget); + + GtkAdjustment * adj = gtk_tree_view_get_vadjustment ((GtkTreeView *) widget); + if (! adj) + return FALSE; + + gint new = gtk_adjustment_get_value (adj) + model->scroll_speed; + gint clamped = CLAMP (new, 0, gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); + gtk_adjustment_set_value (adj, clamped); + + if (clamped != new) /* reached top or bottom? */ + return FALSE; + + if (model->scroll_speed > 0) + model->scroll_speed = MIN (model->scroll_speed + 2, 100); + else + model->scroll_speed = MAX (model->scroll_speed - 2, -100); + + return TRUE; +} + +static void start_autoscroll (ListModel * model, GtkWidget * widget, gint speed) +{ + if (model->scroll_source) + return; + + model->scroll_source = g_timeout_add (50, (GSourceFunc) autoscroll, widget); + model->scroll_speed = speed; +} + +static gboolean drag_motion (GtkWidget * widget, GdkDragContext * context, + gint x, gint y, guint time, ListModel * model) +{ + g_signal_stop_emission_by_name (widget, "drag-motion"); + + /* If button_press_cb preserved a multiple selection, tell button_release_cb + * not to clear it. */ + model->frozen = FALSE; + + if (model->dragging && model->cbs->shift_rows) /* dragging within same list */ + gdk_drag_status (context, GDK_ACTION_MOVE, time); + else if (model->cbs->data_type) /* cross-widget dragging */ + gdk_drag_status (context, GDK_ACTION_COPY, time); + else + return FALSE; + + if (model->rows > 0) + { + gint row = calc_drop_row (model, widget, x, y); + if (row == model->rows) + { + GtkTreePath * path = gtk_tree_path_new_from_indices (row - 1, -1); + gtk_tree_view_set_drag_dest_row ((GtkTreeView *) widget, path, + GTK_TREE_VIEW_DROP_AFTER); + gtk_tree_path_free (path); + } + else + { + GtkTreePath * path = gtk_tree_path_new_from_indices (row, -1); + gtk_tree_view_set_drag_dest_row ((GtkTreeView *) widget, path, + GTK_TREE_VIEW_DROP_BEFORE); + gtk_tree_path_free (path); + } + } + + gint height; + gdk_window_get_geometry (gtk_tree_view_get_bin_window ((GtkTreeView *) + widget), NULL, NULL, NULL, & height); + gtk_tree_view_convert_widget_to_bin_window_coords ((GtkTreeView *) widget, + x, y, & x, & y); + + if (y >= 0 && y < 48) + start_autoscroll (model, widget, -2); + else if (y >= height - 48 && y < height) + start_autoscroll (model, widget, 2); + else + stop_autoscroll (model); + + return TRUE; +} + +static void drag_leave (GtkWidget * widget, GdkDragContext * context, + guint time, ListModel * model) +{ + g_signal_stop_emission_by_name (widget, "drag-leave"); + + gtk_tree_view_set_drag_dest_row ((GtkTreeView *) widget, NULL, 0); + stop_autoscroll (model); +} + +static gboolean drag_drop (GtkWidget * widget, GdkDragContext * context, gint x, + gint y, guint time, ListModel * model) +{ + g_signal_stop_emission_by_name (widget, "drag-drop"); + + gboolean success = TRUE; + gint row = calc_drop_row (model, widget, x, y); + + if (model->dragging && model->cbs->shift_rows) /* dragging within same list */ + { + if (model->clicked_row >= 0 && model->clicked_row < model->rows) + model->cbs->shift_rows (model->user, model->clicked_row, row); + else + success = FALSE; + } + else if (model->cbs->data_type) /* cross-widget dragging */ + { + model->receive_row = row; + gtk_drag_get_data (widget, context, gdk_atom_intern + (model->cbs->data_type, FALSE), time); + } + else + success = FALSE; + + gtk_drag_finish (context, success, FALSE, time); + gtk_tree_view_set_drag_dest_row ((GtkTreeView *) widget, NULL, 0); + stop_autoscroll (model); + return TRUE; +} + +static void drag_data_received (GtkWidget * widget, GdkDragContext * context, gint x, + gint y, GtkSelectionData * sel, guint info, guint time, ListModel * model) +{ + g_signal_stop_emission_by_name (widget, "drag-data-received"); + + g_return_if_fail (model->receive_row >= 0 && model->receive_row <= + model->rows); + + const guchar * data = gtk_selection_data_get_data (sel); + gint length = gtk_selection_data_get_length (sel); + + if (data && length) + model->cbs->receive_data (model->user, model->receive_row, data, length); + + model->receive_row = -1; +} + +/* ==== PUBLIC FUNCS ==== */ + +static void destroy_cb (ListModel * model) +{ + stop_autoscroll (model); + g_object_unref (model); +} + +static void update_selection (GtkWidget * list, ListModel * model, gint at, + gint rows) +{ + model->blocked = TRUE; + GtkTreeSelection * sel = gtk_tree_view_get_selection ((GtkTreeView *) list); + + for (gint i = at; i < at + rows; i ++) + { + GtkTreeIter iter = {.user_data = GINT_TO_POINTER (i)}; + if (model->cbs->get_selected (model->user, i)) + gtk_tree_selection_select_iter (sel, & iter); + else + gtk_tree_selection_unselect_iter (sel, & iter); + } + + model->blocked = FALSE; +} + +GtkWidget * audgui_list_new (const AudguiListCallbacks * cbs, void * user, + gint rows) +{ + g_return_val_if_fail (cbs->get_value, NULL); + if (cbs->get_selected) + g_return_val_if_fail (cbs->set_selected && cbs->select_all, NULL); + if (cbs->data_type) + g_return_val_if_fail (cbs->get_data && cbs->receive_data, NULL); + + ListModel * model = (ListModel *) g_object_new (list_model_get_type (), NULL); + model->cbs = cbs; + model->user = user; + model->rows = rows; + model->highlight = -1; + model->columns = RESERVED_COLUMNS; + model->column_types = NULL; + model->frozen = FALSE; + model->blocked = FALSE; + model->dragging = FALSE; + model->clicked_row = -1; + model->receive_row = -1; + model->scroll_source = 0; + model->scroll_speed = 0; + + GtkWidget * list = gtk_tree_view_new_with_model ((GtkTreeModel *) model); + g_signal_connect_swapped (list, "destroy", (GCallback) destroy_cb, model); + + if (cbs->get_selected) + { + GtkTreeSelection * sel = gtk_tree_view_get_selection + ((GtkTreeView *) list); + gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE); + gtk_tree_selection_set_select_function (sel, select_allow_cb, NULL, NULL); + g_signal_connect (sel, "changed", (GCallback) select_cb, model); + + update_selection (list, model, 0, rows); + } + + if (cbs->activate_row) + g_signal_connect (list, "row-activated", (GCallback) activate_cb, model); + + g_signal_connect (list, "button-press-event", (GCallback) button_press_cb, + model); + g_signal_connect (list, "button-release-event", (GCallback) + button_release_cb, model); + + if (cbs->data_type) + { + const GtkTargetEntry target = {(gchar *) cbs->data_type, 0, 0}; + + gtk_drag_source_set (list, GDK_BUTTON1_MASK, & target, 1, + GDK_ACTION_COPY); + gtk_drag_dest_set (list, 0, & target, 1, GDK_ACTION_COPY); + + g_signal_connect (list, "drag-data-get", (GCallback) drag_data_get, + model); + g_signal_connect (list, "drag-data-received", (GCallback) + drag_data_received, model); + } + else if (cbs->shift_rows) + { + gtk_drag_source_set (list, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY); + gtk_drag_dest_set (list, 0, NULL, 0, GDK_ACTION_COPY); + } + + if (cbs->data_type || cbs->shift_rows) + { + g_signal_connect (list, "drag-begin", (GCallback) drag_begin, model); + g_signal_connect (list, "drag-end", (GCallback) drag_end, model); + g_signal_connect (list, "drag-motion", (GCallback) drag_motion, model); + g_signal_connect (list, "drag-leave", (GCallback) drag_leave, model); + g_signal_connect (list, "drag-drop", (GCallback) drag_drop, model); + } + + return list; +} + +void * audgui_list_get_user (GtkWidget * list) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + return model->user; +} + +void audgui_list_add_column (GtkWidget * list, const gchar * title, + gint column, GType type, gboolean expand) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + g_return_if_fail (RESERVED_COLUMNS + column == model->columns); + + model->columns ++; + model->column_types = g_list_append (model->column_types, GINT_TO_POINTER + (type)); + + GtkCellRenderer * renderer = gtk_cell_renderer_text_new (); + GtkTreeViewColumn * tree_column = gtk_tree_view_column_new_with_attributes + (title, renderer, "text", RESERVED_COLUMNS + column, "weight", + HIGHLIGHT_COLUMN, NULL); + + if (expand) + { + gtk_tree_view_column_set_resizable (tree_column, TRUE); + gtk_tree_view_column_set_expand (tree_column, TRUE); + g_object_set ((GObject *) renderer, "ellipsize-set", TRUE, "ellipsize", + PANGO_ELLIPSIZE_END, NULL); + } + else + { + gtk_tree_view_column_set_sizing (tree_column, + GTK_TREE_VIEW_COLUMN_GROW_ONLY); + g_object_set ((GObject *) renderer, "xalign", (gfloat) 1, NULL); + } + + gtk_tree_view_append_column ((GtkTreeView *) list, tree_column); +} + +gint audgui_list_row_count (GtkWidget * list) +{ + return ((ListModel *) gtk_tree_view_get_model ((GtkTreeView *) list))->rows; +} + +void audgui_list_insert_rows (GtkWidget * list, gint at, gint rows) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + g_return_if_fail (at >= 0 && at <= model->rows && rows >= 0); + + model->rows += rows; + if (model->highlight >= at) + model->highlight += rows; + + GtkTreeIter iter = {.user_data = GINT_TO_POINTER (at)}; + GtkTreePath * path = gtk_tree_path_new_from_indices (at, -1); + + for (gint i = rows; i --; ) + gtk_tree_model_row_inserted ((GtkTreeModel *) model, path, & iter); + + gtk_tree_path_free (path); + + update_selection (list, model, at, rows); +} + +void audgui_list_update_rows (GtkWidget * list, gint at, gint rows) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + g_return_if_fail (at >= 0 && rows >= 0 && at + rows <= model->rows); + + GtkTreeIter iter = {.user_data = GINT_TO_POINTER (at)}; + GtkTreePath * path = gtk_tree_path_new_from_indices (at, -1); + + while (rows --) + { + gtk_tree_model_row_changed ((GtkTreeModel *) model, path, & iter); + iter.user_data = GINT_TO_POINTER (GPOINTER_TO_INT (iter.user_data) + 1); + gtk_tree_path_next (path); + } + + gtk_tree_path_free (path); +} + +void audgui_list_delete_rows (GtkWidget * list, gint at, gint rows) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + g_return_if_fail (at >= 0 && rows >= 0 && at + rows <= model->rows); + + model->rows -= rows; + if (model->highlight >= at + rows) + model->highlight -= rows; + else if (model->highlight >= at) + model->highlight = -1; + + model->blocked = TRUE; + GtkTreePath * path = gtk_tree_path_new_from_indices (at, -1); + + while (rows --) + gtk_tree_model_row_deleted ((GtkTreeModel *) model, path); + + gtk_tree_path_free (path); + model->blocked = FALSE; +} + +void audgui_list_update_selection (GtkWidget * list, gint at, gint rows) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + g_return_if_fail (model->cbs->get_selected); + g_return_if_fail (at >= 0 && rows >= 0 && at + rows <= model->rows); + update_selection (list, model, at, rows); +} + +void audgui_list_set_highlight (GtkWidget * list, gint row) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + g_return_if_fail (row >= -1 && row < model->rows); + + gint old = model->highlight; + if (row == old) + return; + model->highlight = row; + + if (old >= 0) + audgui_list_update_rows (list, old, 1); + if (row >= 0) + audgui_list_update_rows (list, row, 1); +} + +void audgui_list_set_focus (GtkWidget * list, gint row) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model + ((GtkTreeView *) list); + g_return_if_fail (row >= -1 && row < model->rows); + + if (row < 0) + { + if (model->rows < 1) + return; + row = 0; + } + + model->frozen = TRUE; + GtkTreePath * path = gtk_tree_path_new_from_indices (row, -1); + gtk_tree_view_set_cursor ((GtkTreeView *) list, path, NULL, FALSE); + gtk_tree_view_scroll_to_cell ((GtkTreeView *) list, path, NULL, FALSE, 0, 0); + gtk_tree_path_free (path); + model->frozen = FALSE; +} + +gint audgui_list_row_at_point (GtkWidget * list, gint x, gint y) +{ + ListModel * model = (ListModel *) gtk_tree_view_get_model ((GtkTreeView *) + list); + + GtkTreePath * path = NULL; + gtk_tree_view_convert_widget_to_bin_window_coords ((GtkTreeView *) list, x, + y, & x, & y); + gtk_tree_view_get_path_at_pos ((GtkTreeView *) list, x, y, & path, NULL, + NULL, NULL); + + if (! path) + return -1; + + gint row = gtk_tree_path_get_indices (path)[0]; + g_return_val_if_fail (row >= 0 && row < model->rows, -1); + + GdkRectangle rect; + gtk_tree_view_get_background_area ((GtkTreeView *) list, path, NULL, + & rect); + if (y > rect.y + rect.height / 2) + row ++; + + gtk_tree_path_free (path); + return row; +} diff --git a/src/libaudgui/list.h b/src/libaudgui/list.h new file mode 100644 index 0000000..0eb1cef --- /dev/null +++ b/src/libaudgui/list.h @@ -0,0 +1,61 @@ +/* + * list.h + * Copyright 2011 John Lindgren + * + * This file is part of Audacious. + * + * Audacious 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 or version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef AUDGUI_LIST_H +#define AUDGUI_LIST_H + +#include <gtk/gtk.h> + +typedef struct { + void (* get_value) (void * user, gint row, gint column, GValue * value); + + /* selection (optional) */ + gboolean (* get_selected) (void * user, gint row); + void (* set_selected) (void * user, gint row, gboolean selected); + void (* select_all) (void * user, gboolean selected); + + void (* activate_row) (void * user, gint row); /* optional */ + void (* right_click) (void * user, GdkEventButton * event); /* optional */ + void (* shift_rows) (void * user, gint row, gint before); /* optional */ + + /* cross-widget drag and drop (optional) */ + const gchar * data_type; + void (* get_data) (void * user, void * * data, gint * length); /* data will + be freed */ + void (* receive_data) (void * user, gint row, const void * data, gint length); +} AudguiListCallbacks; + +GtkWidget * audgui_list_new (const AudguiListCallbacks * cbs, void * user, + gint rows); +void * audgui_list_get_user (GtkWidget * list); +void audgui_list_add_column (GtkWidget * list, const gchar * title, + gint column, GType type, gboolean expand); + +gint audgui_list_row_count (GtkWidget * list); +void audgui_list_insert_rows (GtkWidget * list, gint at, gint rows); +void audgui_list_update_rows (GtkWidget * list, gint at, gint rows); +void audgui_list_delete_rows (GtkWidget * list, gint at, gint rows); +void audgui_list_update_selection (GtkWidget * list, gint at, gint rows); +void audgui_list_set_highlight (GtkWidget * list, gint row); +void audgui_list_set_focus (GtkWidget * list, gint row); +gint audgui_list_row_at_point (GtkWidget * list, gint x, gint y); + +#endif diff --git a/src/libaudgui/ui_about.c b/src/libaudgui/ui_about.c index 29a2a3d..caeac03 100644 --- a/src/libaudgui/ui_about.c +++ b/src/libaudgui/ui_about.c @@ -24,6 +24,8 @@ * Audacious or using our public API to be a derived work. */ +#include <limits.h> + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -34,79 +36,22 @@ #include <gdk/gdk.h> #include <gdk/gdkkeysyms.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/misc.h> -#include "audacious/compatibility.h" +#include "libaudgui-gtk.h" -#include "ui_credits.h" +/* ui_credits.c */ +GtkWidget * audgui_get_credits_widget (void); static GtkWidget *about_window = NULL; -static GdkPixbuf *about_pixbuf = NULL; -static GdkPixmap *mask_pixmap_window1 = NULL, - *mask_pixmap_window2 = NULL; -static GdkBitmap *mask_bitmap_window1 = NULL, - *mask_bitmap_window2 = NULL; - -static gboolean -on_about_window_expose(GtkWidget *widget, GdkEventExpose *expose, gpointer data) -{ - GdkWindow *window; - - g_return_val_if_fail(widget != NULL, FALSE); - g_return_val_if_fail(GTK_IS_WIDGET (widget), FALSE); - - window = gtk_widget_get_window(widget); - gdk_window_set_back_pixmap(window, mask_pixmap_window2, 0); - gdk_window_clear(window); - - return FALSE; -} - -static gboolean -on_about_window_key_press (GtkWidget *widget, GdkEventKey *event, gpointer data) -{ - g_return_val_if_fail(GTK_IS_WIDGET (widget), FALSE); - - if (event->keyval == GDK_Escape) - { - gtk_widget_hide(widget); - } - - return FALSE; -} - -static gboolean -on_close_button_clicked (GtkWidget *widget, gpointer data) -{ - g_return_val_if_fail(GTK_IS_WIDGET (widget), FALSE); - - gtk_widget_hide(about_window); - - return FALSE; -} - -static gboolean -on_credits_button_clicked (GtkWidget *widget, gpointer data) -{ - g_return_val_if_fail(GTK_IS_WIDGET (widget), FALSE); - - audgui_show_credits_window(); - - return FALSE; -} void audgui_show_about_window(void) { - GtkWidget *about_fixedbox; - GtkWidget *close_button; - GtkWidget *credits_button , *credits_button_hbox, *credits_button_image, *credits_button_label; GtkWidget *brief_label; - gchar *filename = DATA_DIR G_DIR_SEPARATOR_S "images" G_DIR_SEPARATOR_S "about-logo.png"; gchar *text; - PangoAttrList *brief_label_attrs; - PangoAttribute *brief_label_foreground; static const gchar *audacious_brief; if (about_window != NULL) @@ -118,99 +63,38 @@ audgui_show_about_window(void) aud_get_audacious_credits(&audacious_brief, NULL, NULL); about_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width ((GtkContainer *) about_window, 3); g_signal_connect(about_window, "destroy", G_CALLBACK(gtk_widget_destroyed), &about_window); - gtk_widget_realize(about_window); - - about_pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - - gtk_widget_set_size_request(GTK_WIDGET (about_window), - gdk_pixbuf_get_width (about_pixbuf), - gdk_pixbuf_get_height (about_pixbuf)); - - gtk_widget_set_app_paintable(about_window, TRUE); gtk_window_set_title(GTK_WINDOW(about_window), _("About Audacious")); - gtk_window_set_position(GTK_WINDOW(about_window), GTK_WIN_POS_CENTER); gtk_window_set_resizable(GTK_WINDOW(about_window), FALSE); - gtk_window_set_decorated(GTK_WINDOW(about_window), FALSE); - - gdk_pixbuf_render_pixmap_and_mask(about_pixbuf, - &mask_pixmap_window1, - &mask_bitmap_window1, - 0); - - gdk_pixbuf_render_pixmap_and_mask(about_pixbuf, - &mask_pixmap_window2, - &mask_bitmap_window2, - 128); - - gtk_widget_add_events(about_window, GDK_ALL_EVENTS_MASK); - - g_signal_connect(about_window, "expose-event", - G_CALLBACK(on_about_window_expose), &about_window); - - g_signal_connect(about_window, "key-press-event", - G_CALLBACK(on_about_window_key_press), &about_window); - - gtk_widget_shape_combine_mask(GTK_WIDGET(about_window), mask_bitmap_window2, 0, 0); - - /* GtkFixed hasn't got its GdkWindow, this means that it can be used to - display widgets while the logo below will be displayed anyway; - however fixed positions are not that great, cause the button sizes may (will) - vary depending on the gtk style used, so it's not possible to center - them unless a fixed width and heigth is forced (and this may bring to cutted - text if someone, i.e., uses a big font for gtk widgets); - other types of container most likely have their GdkWindow, this simply - means that the logo must be drawn on the container widget, instead of the - window; otherwise, it won't be displayed correctly */ - about_fixedbox = gtk_fixed_new(); - gtk_container_add( GTK_CONTAINER(about_window) , about_fixedbox ); - - close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - - g_signal_connect(close_button, "clicked", - G_CALLBACK(on_close_button_clicked), NULL); - - gtk_fixed_put( GTK_FIXED(about_fixedbox) , close_button , 375 , 220 ); - gtk_widget_set_size_request( close_button , 100 , -1 ); - - credits_button = gtk_button_new(); - credits_button_hbox = gtk_hbox_new( FALSE , 0 ); - credits_button_image = gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO , GTK_ICON_SIZE_BUTTON ); - gtk_misc_set_alignment( GTK_MISC(credits_button_image) , 1 , 0.5 ); - credits_button_label = gtk_label_new( _("Credits") ); - gtk_misc_set_alignment( GTK_MISC(credits_button_label) , 0 , 0.5 ); - gtk_box_pack_start( GTK_BOX(credits_button_hbox) , credits_button_image , - TRUE , TRUE , 2 ); - gtk_box_pack_start( GTK_BOX(credits_button_hbox) , credits_button_label , - TRUE , TRUE , 2 ); - gtk_container_add( GTK_CONTAINER(credits_button) , credits_button_hbox ); - - g_signal_connect(credits_button, "clicked", - G_CALLBACK(on_credits_button_clicked), NULL); - - gtk_fixed_put( GTK_FIXED(about_fixedbox) , credits_button , 25 , 220 ); - gtk_widget_set_size_request( credits_button , 100 , -1 ); + audgui_destroy_on_escape (about_window); + + GtkWidget * vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add ((GtkContainer *) about_window, vbox); + + gchar name[PATH_MAX]; + snprintf (name, sizeof name, "%s/images/about-logo.png", aud_get_path + (AUD_PATH_DATA_DIR)); + GtkWidget * image = gtk_image_new_from_file (name); + gtk_box_pack_start ((GtkBox *) vbox, image, FALSE, FALSE, 0); brief_label = gtk_label_new(NULL); text = g_strdup_printf(_(audacious_brief), VERSION); - brief_label_foreground = pango_attr_foreground_new(0, 0, 0); - brief_label_attrs = pango_attr_list_new(); - pango_attr_list_insert(brief_label_attrs, brief_label_foreground); - gtk_label_set_markup(GTK_LABEL(brief_label), text); gtk_label_set_justify(GTK_LABEL(brief_label), GTK_JUSTIFY_CENTER); - gtk_label_set_attributes(GTK_LABEL(brief_label), brief_label_attrs); g_free(text); + + gtk_box_pack_start ((GtkBox *) vbox, brief_label, FALSE, FALSE, 0); - gtk_fixed_put(GTK_FIXED(about_fixedbox), brief_label, 20, 145); - gtk_widget_set_size_request( brief_label , 460 , -1 ); + GtkWidget * exp = gtk_expander_new (_("Credits")); + gtk_container_add ((GtkContainer *) exp, audgui_get_credits_widget ()); + gtk_box_pack_start ((GtkBox *) vbox, exp, TRUE, TRUE, 0); gtk_widget_show_all(about_window); - gtk_window_present(GTK_WINDOW(about_window)); } void diff --git a/src/libaudgui/ui_credits.c b/src/libaudgui/ui_credits.c index 4f3732f..ec7f0f6 100644 --- a/src/libaudgui/ui_credits.c +++ b/src/libaudgui/ui_credits.c @@ -30,14 +30,10 @@ #include <glib.h> #include <gtk/gtk.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/misc.h> -#include "audacious/compatibility.h" - -#include "ui_credits.h" -#include "audacious_logo.xpm" - enum { COL_LEFT, COL_RIGHT, @@ -104,81 +100,22 @@ generate_credit_list(const gchar * text[], gboolean sec_space) GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(scrollwin), treeview); - gtk_container_set_border_width(GTK_CONTAINER(scrollwin), 10); - - gtk_widget_show_all(scrollwin); + gtk_container_set_border_width ((GtkContainer *) scrollwin, 4); return scrollwin; } -void -audgui_show_credits_window(void) +GtkWidget * audgui_get_credits_widget (void) { - static GtkWidget *about_window = NULL; - - GdkPixbuf *logo_pixbuf; - GtkWidget *about_vbox; - GtkWidget *about_credits_logo_box, *about_credits_logo_frame; - GtkWidget *about_credits_logo; GtkWidget *about_notebook; GtkWidget *list; - GtkWidget *bbox, *close_btn; - GtkWidget *label; - gchar *text; - static const gchar *audacious_brief; - static const gchar **credit_text; - static const gchar **translators; - - if (about_window) - return; - - aud_get_audacious_credits(&audacious_brief, &credit_text, &translators); - - about_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_type_hint(GTK_WINDOW(about_window), - GDK_WINDOW_TYPE_HINT_DIALOG); - - gtk_window_set_default_size(GTK_WINDOW(about_window), -1, 512); - gtk_window_set_title(GTK_WINDOW(about_window), _("About Audacious")); - gtk_window_set_position(GTK_WINDOW(about_window), GTK_WIN_POS_CENTER); - gtk_window_set_resizable(GTK_WINDOW(about_window), TRUE); - gtk_container_set_border_width(GTK_CONTAINER(about_window), 10); - - g_signal_connect(about_window, "destroy", - G_CALLBACK(gtk_widget_destroyed), &about_window); - - gtk_widget_realize(about_window); + const gchar **credit_text; + const gchar **translators; - about_vbox = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(about_window), about_vbox); - - logo_pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)audacious_logo_xpm); - - about_credits_logo_box = gtk_hbox_new(TRUE, 0); - gtk_box_pack_start(GTK_BOX(about_vbox), about_credits_logo_box, - FALSE, FALSE, 0); - - about_credits_logo_frame = gtk_frame_new(NULL); - gtk_frame_set_shadow_type(GTK_FRAME(about_credits_logo_frame), - GTK_SHADOW_ETCHED_OUT); - gtk_box_pack_start(GTK_BOX(about_credits_logo_box), - about_credits_logo_frame, FALSE, FALSE, 0); - - about_credits_logo = gtk_image_new_from_pixbuf(logo_pixbuf); - gtk_container_add(GTK_CONTAINER(about_credits_logo_frame), - about_credits_logo); - g_object_unref(logo_pixbuf); - - label = gtk_label_new(NULL); - text = g_strdup_printf(_(audacious_brief), VERSION); - gtk_label_set_markup(GTK_LABEL(label), text); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); - g_free(text); - - gtk_box_pack_start(GTK_BOX(about_vbox), label, FALSE, FALSE, 0); + aud_get_audacious_credits (NULL, &credit_text, &translators); about_notebook = gtk_notebook_new(); - gtk_box_pack_start(GTK_BOX(about_vbox), about_notebook, TRUE, TRUE, 0); + gtk_widget_set_size_request (about_notebook, -1, 250); list = generate_credit_list(credit_text, TRUE); gtk_notebook_append_page(GTK_NOTEBOOK(about_notebook), list, @@ -188,18 +125,5 @@ audgui_show_credits_window(void) gtk_notebook_append_page(GTK_NOTEBOOK(about_notebook), list, gtk_label_new(_("Translators"))); - bbox = gtk_hbutton_box_new(); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_set_spacing(GTK_BOX(bbox), 5); - gtk_box_pack_start(GTK_BOX(about_vbox), bbox, FALSE, FALSE, 0); - - close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - g_signal_connect_swapped(close_btn, "clicked", - G_CALLBACK(gtk_widget_destroy), about_window); - - gtk_widget_set_can_default(close_btn, TRUE); - gtk_box_pack_start(GTK_BOX(bbox), close_btn, TRUE, TRUE, 0); - gtk_widget_grab_default(close_btn); - - gtk_widget_show_all(about_window); + return about_notebook; } diff --git a/src/libaudgui/ui_credits.h b/src/libaudgui/ui_credits.h deleted file mode 100644 index fe30775..0000000 --- a/src/libaudgui/ui_credits.h +++ /dev/null @@ -1,9 +0,0 @@ - -#ifndef AUDACIOUS_UI_CREDITS_H -#define AUDACIOUS_UI_CREDITS_H - -void audgui_show_about_window(void); -void audgui_hide_about_window(void); -void audgui_show_credits_window(void); - -#endif /* AUDACIOUS_UI_CREDITS_H */ diff --git a/src/libaudgui/ui_fileopener.c b/src/libaudgui/ui_fileopener.c index a0eb83b..b7d6794 100644..100755 --- a/src/libaudgui/ui_fileopener.c +++ b/src/libaudgui/ui_fileopener.c @@ -22,6 +22,7 @@ #include <audacious/audconfig.h> #include <audacious/i18n.h> #include <audacious/drct.h> +#include <audacious/gtk-compat.h> #include "config.h" #include "libaudgui.h" @@ -144,6 +145,9 @@ run_filebrowser_gtk2style(gboolean play_button, gboolean show) gtk_container_add(GTK_CONTAINER(bbox), close_button); gtk_container_add(GTK_CONTAINER(bbox), action_button); + gtk_widget_set_can_default (action_button, TRUE); + gtk_widget_grab_default (action_button); + /* this storage object holds several other objects which are used in the * callback functions */ diff --git a/src/libaudgui/ui_gtk.c b/src/libaudgui/ui_gtk.c index fa8d4e8..24d2fe5 100644 --- a/src/libaudgui/ui_gtk.c +++ b/src/libaudgui/ui_gtk.c @@ -19,8 +19,14 @@ #include <gtk/gtk.h> +#include <audacious/misc.h> + void audgui_set_default_icon (void) { +#ifdef _WIN32 + gtk_window_set_default_icon_from_file (aud_get_path (AUD_PATH_ICON_FILE), + NULL); +#else gtk_window_set_default_icon_name ("audacious"); +#endif } - diff --git a/src/libaudgui/ui_jumptotrack.c b/src/libaudgui/ui_jumptotrack.c index a97d30e..52f3f24 100644 --- a/src/libaudgui/ui_jumptotrack.c +++ b/src/libaudgui/ui_jumptotrack.c @@ -27,38 +27,25 @@ # include "config.h" #endif - -#include <glib.h> -#include <glib/gprintf.h> -#include <gtk/gtk.h> - -#include <gdk/gdk.h> -#include <gdk/gdkkeysyms.h> - #include <math.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> -#if defined(USE_REGEX_ONIGURUMA) - #include <onigposix.h> -#elif defined(USE_REGEX_PCRE) - #include <pcreposix.h> -#else - #include <regex.h> -#endif +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> #include <audacious/audconfig.h> #include <audacious/drct.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/playlist.h> #include <libaudcore/hook.h> -#include "audacious/compatibility.h" - #include "icons-stock.h" #include "ui_jumptotrack_cache.h" +#include "ui_regex.h"
static void watchdog (void * hook_data, void * user_data); diff --git a/src/libaudgui/ui_jumptotrack_cache.c b/src/libaudgui/ui_jumptotrack_cache.c index 9d4fd8a..97202d0 100644 --- a/src/libaudgui/ui_jumptotrack_cache.c +++ b/src/libaudgui/ui_jumptotrack_cache.c @@ -28,18 +28,11 @@ # include "sys/types.h" #endif -#if defined(USE_REGEX_ONIGURUMA) - #include <onigposix.h> -#elif defined(USE_REGEX_PCRE) - #include <pcreposix.h> -#else - #include <regex.h> -#endif - #include <audacious/debug.h> #include <audacious/playlist.h> #include "ui_jumptotrack_cache.h" +#include "ui_regex.h"
// Struct to keep information about matches from searches. typedef struct diff --git a/src/libaudgui/ui_playlist_manager.c b/src/libaudgui/ui_playlist_manager.c index e554812..09ec102 100644 --- a/src/libaudgui/ui_playlist_manager.c +++ b/src/libaudgui/ui_playlist_manager.c @@ -1,5 +1,5 @@ /* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2010 Audacious development team. + * Copyright (C) 2005-2011 Audacious 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 @@ -17,7 +17,10 @@ * Audacious or using our public API to be a derived work. */ +#include <gtk/gtk.h> + #include <audacious/audconfig.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/playlist.h> #include <libaudcore/hook.h> @@ -25,38 +28,9 @@ #include "config.h" #include "libaudgui.h" #include "libaudgui-gtk.h" +#include "list.h" -static gint iter_to_row (GtkTreeModel * model, GtkTreeIter * iter) -{ - GtkTreePath * path = gtk_tree_model_get_path (model, iter); - gint row = gtk_tree_path_get_indices (path)[0]; - - gtk_tree_path_free (path); - return row; -} - -static gint get_selected_row (GtkWidget * list) -{ - GtkTreeSelection * selection = gtk_tree_view_get_selection ((GtkTreeView *) - list); - GtkTreeModel * model; - GtkTreeIter iter; - - if (! gtk_tree_selection_get_selected (selection, & model, & iter)) - return -1; - - return iter_to_row (model, & iter); -} - -static void set_selected_row (GtkWidget * list, gint row) -{ - GtkTreeSelection * selection = gtk_tree_view_get_selection ((GtkTreeView *) - list); - GtkTreePath * path = gtk_tree_path_new_from_indices (row, -1); - - gtk_tree_selection_select_path (selection, path); - gtk_tree_path_free (path); -} +static GtkWidget * playman_win = NULL; static void save_position (GtkWidget * window) { @@ -73,13 +47,9 @@ static gboolean hide_cb (GtkWidget * window) return TRUE; } -static void activate_cb (GtkTreeView * list, GtkTreePath * path, - GtkTreeViewColumn * column, GtkWidget * window) +static void rename_cb (void) { - aud_playlist_set_active (gtk_tree_path_get_indices (path)[0]); - - if (aud_cfg->playlist_manager_close_on_activate) - hide_cb (window); + audgui_show_playlist_rename (aud_playlist_get_active ()); } static void new_cb (GtkButton * button, void * unused) @@ -89,65 +59,115 @@ static void new_cb (GtkButton * button, void * unused) static void delete_cb (GtkButton * button, GtkWidget * list) { - gint playlist = get_selected_row (list); - - if (playlist != -1) - audgui_confirm_playlist_delete (playlist); + audgui_confirm_playlist_delete (aud_playlist_get_active ()); } -static void rename_cb (GtkButton * button, GtkWidget * lv) +static void save_config_cb (void * hook_data, void * user_data) { - GtkTreeSelection *listsel = gtk_tree_view_get_selection( GTK_TREE_VIEW(lv) ); - GtkTreeModel *store; - GtkTreeIter iter; + if (gtk_widget_get_visible ((GtkWidget *) user_data)) + save_position ((GtkWidget *) user_data); +} - if ( gtk_tree_selection_get_selected( listsel , &store , &iter ) == TRUE ) +static void get_value (void * user, gint row, gint column, GValue * value) +{ + switch (column) { - GtkTreePath *path = gtk_tree_model_get_path( GTK_TREE_MODEL(store) , &iter ); - GtkCellRenderer *rndrname = g_object_get_data( G_OBJECT(lv) , "rn" ); - /* set the name renderer to editable and start editing */ - g_object_set( G_OBJECT(rndrname) , "editable" , TRUE , NULL ); - gtk_tree_view_set_cursor_on_cell ((GtkTreeView *) lv, path, - gtk_tree_view_get_column ((GtkTreeView *) lv, - AUDGUI_LIBRARY_STORE_TITLE), rndrname, TRUE); - gtk_tree_path_free( path ); + case 0: + g_value_set_string (value, aud_playlist_get_title (row)); + break; + case 1: + g_value_set_int (value, aud_playlist_entry_count (row)); + break; } } -static void -playlist_manager_cb_lv_name_edited ( GtkCellRendererText *cell , gchar *path_string , - gchar *new_text , gpointer listview ) +static gboolean get_selected (void * user, gint row) +{ + return (row == aud_playlist_get_active ()); +} + +static void set_selected (void * user, gint row, gboolean selected) { - /* this is currently used to change playlist names */ - GtkTreeModel *store = gtk_tree_view_get_model( GTK_TREE_VIEW(listview) ); - GtkTreeIter iter; + if (selected) + aud_playlist_set_active (row); +} - if ( gtk_tree_model_get_iter_from_string( store , &iter , path_string ) == TRUE ) - aud_playlist_set_title (iter_to_row (store, & iter), new_text); +static void select_all (void * user, gboolean selected) +{ +} - /* set the renderer uneditable again */ - g_object_set( G_OBJECT(cell) , "editable" , FALSE , NULL ); +static void activate_row (void * user, gint row) +{ + aud_playlist_set_active (row); + + if (aud_cfg->playlist_manager_close_on_activate) + hide_cb (playman_win); } -static void save_config_cb (void * hook_data, void * user_data) +static void shift_rows (void * user, gint row, gint before) { -#if GTK_CHECK_VERSION (2, 18, 0) - if (gtk_widget_get_visible ((GtkWidget *) user_data)) -#else - if (GTK_WIDGET_VISIBLE ((GtkWidget *) user_data)) -#endif - save_position ((GtkWidget *) user_data); + if (before < row) + aud_playlist_reorder (row, before, 1); + else if (before - 1 > row) + aud_playlist_reorder (row, before - 1, 1); } -static GtkWidget * playman_win = NULL; +static const AudguiListCallbacks callbacks = { + .get_value = get_value, + .get_selected = get_selected, + .set_selected = set_selected, + .select_all = select_all, + .activate_row = activate_row, + .right_click = NULL, + .shift_rows = shift_rows, + .data_type = NULL, + .get_data = NULL, + .receive_data = NULL}; + +static gboolean position_changed = FALSE; + +static void update_hook (void * data, void * list) +{ + if (GPOINTER_TO_INT (data) >= PLAYLIST_UPDATE_STRUCTURE) + { + gint old_rows = audgui_list_row_count (list); + gint rows = aud_playlist_count (); + + if (rows < old_rows) + { + audgui_list_delete_rows (list, rows, old_rows - rows); + old_rows = rows; + } + + audgui_list_update_rows (list, 0, old_rows); + audgui_list_update_selection (list, 0, old_rows); + + if (rows > old_rows) + audgui_list_insert_rows (list, old_rows, rows - old_rows); + + audgui_list_set_focus (list, aud_playlist_get_active ()); + } + + if (GPOINTER_TO_INT (data) >= PLAYLIST_UPDATE_STRUCTURE || position_changed) + { + audgui_list_set_highlight (list, aud_playlist_get_playing ()); + position_changed = FALSE; + } +} + +static void position_hook (void * data, void * list) +{ + if (aud_playlist_update_pending ()) + position_changed = TRUE; + else + audgui_list_set_highlight (list, aud_playlist_get_playing ()); +} void audgui_playlist_manager_ui_show (GtkWidget *mainwin) { GtkWidget *playman_vbox; GtkWidget * playman_pl_lv, * playman_pl_lv_sw; - GtkCellRenderer *playman_pl_lv_textrndr_name, *playman_pl_lv_textrndr_entriesnum; - GtkTreeViewColumn *playman_pl_lv_col_name, *playman_pl_lv_col_entriesnum; GtkWidget *playman_bbar_hbbox; GtkWidget * rename_button, * new_button, * delete_button; GtkWidget * hbox, * button; @@ -179,34 +199,17 @@ audgui_playlist_manager_ui_show (GtkWidget *mainwin) g_signal_connect ((GObject *) playman_win, "delete-event", (GCallback) hide_cb, NULL); + audgui_hide_on_escape (playman_win); playman_vbox = gtk_vbox_new (FALSE, 6); gtk_container_add( GTK_CONTAINER(playman_win) , playman_vbox ); - playman_pl_lv = gtk_tree_view_new_with_model (audgui_get_library_store ()); - gtk_tree_view_set_reorderable ((GtkTreeView *) playman_pl_lv, TRUE); - - playman_pl_lv_textrndr_entriesnum = gtk_cell_renderer_text_new(); /* uneditable */ - playman_pl_lv_textrndr_name = gtk_cell_renderer_text_new(); /* can become editable */ - g_object_set( G_OBJECT(playman_pl_lv_textrndr_entriesnum) , "weight-set" , TRUE , NULL ); - g_object_set( G_OBJECT(playman_pl_lv_textrndr_name) , "weight-set" , TRUE , NULL ); - g_signal_connect( G_OBJECT(playman_pl_lv_textrndr_name) , "edited" , - G_CALLBACK(playlist_manager_cb_lv_name_edited) , playman_pl_lv ); - g_object_set_data( G_OBJECT(playman_pl_lv) , "rn" , playman_pl_lv_textrndr_name ); - - playman_pl_lv_col_name = gtk_tree_view_column_new_with_attributes - (_("Playlist"), playman_pl_lv_textrndr_name, "text", - AUDGUI_LIBRARY_STORE_TITLE, "weight", AUDGUI_LIBRARY_STORE_FONT_WEIGHT, - NULL); - gtk_tree_view_column_set_expand( GTK_TREE_VIEW_COLUMN(playman_pl_lv_col_name) , TRUE ); - gtk_tree_view_append_column( GTK_TREE_VIEW(playman_pl_lv), playman_pl_lv_col_name ); - - playman_pl_lv_col_entriesnum = gtk_tree_view_column_new_with_attributes - (_("Entries"), playman_pl_lv_textrndr_entriesnum, "text", - AUDGUI_LIBRARY_STORE_ENTRY_COUNT, "weight", - AUDGUI_LIBRARY_STORE_FONT_WEIGHT, NULL); - gtk_tree_view_column_set_expand( GTK_TREE_VIEW_COLUMN(playman_pl_lv_col_entriesnum) , FALSE ); - gtk_tree_view_append_column( GTK_TREE_VIEW(playman_pl_lv), playman_pl_lv_col_entriesnum ); + playman_pl_lv = audgui_list_new (& callbacks, NULL, aud_playlist_count ()); + audgui_list_add_column (playman_pl_lv, _("Title"), 0, G_TYPE_STRING, TRUE); + audgui_list_add_column (playman_pl_lv, _("Entries"), 1, G_TYPE_INT, FALSE); + audgui_list_set_highlight (playman_pl_lv, aud_playlist_get_playing ()); + hook_associate ("playlist update", update_hook, playman_pl_lv); + hook_associate ("playlist position", position_hook, playman_pl_lv); playman_pl_lv_sw = gtk_scrolled_window_new( NULL , NULL ); gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) playman_pl_lv_sw, @@ -235,8 +238,6 @@ audgui_playlist_manager_ui_show (GtkWidget *mainwin) gtk_box_pack_start( GTK_BOX(playman_vbox) , playman_bbar_hbbox , FALSE , FALSE , 0 ); - g_signal_connect ((GObject *) playman_pl_lv, "row-activated", (GCallback) - activate_cb, playman_win); g_signal_connect ((GObject *) rename_button, "clicked", (GCallback) rename_cb, playman_pl_lv); g_signal_connect ((GObject *) new_button, "clicked", (GCallback) new_cb, @@ -244,8 +245,6 @@ audgui_playlist_manager_ui_show (GtkWidget *mainwin) g_signal_connect ((GObject *) delete_button, "clicked", (GCallback) delete_cb, playman_pl_lv); - set_selected_row (playman_pl_lv, aud_playlist_get_active ()); - hbox = gtk_hbox_new (FALSE, 6); gtk_box_pack_start ((GtkBox *) playman_vbox, hbox, FALSE, FALSE, 0); button = gtk_check_button_new_with_mnemonic diff --git a/src/audacious/chardet.h b/src/libaudgui/ui_regex.h index 7311037..587f8d3 100644 --- a/src/audacious/chardet.h +++ b/src/libaudgui/ui_regex.h @@ -1,31 +1,39 @@ -/* Audacious - Cross-platform multimedia player - * Copyright (C) 2005-2009 Audacious 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 <http://www.gnu.org/licenses>. - * - * The Audacious team does not consider modular code linking to - * Audacious or using our public API to be a derived work. - */ - -#ifndef AUDACIOUS_CHARDET_H -#define AUDACIOUS_CHARDET_H - -#include <glib.h> - -gchar * cd_str_to_utf8(const gchar *str); -gchar * cd_chardet_to_utf8(const gchar *str, gssize len, - gsize *arg_bytes_read, gsize *arg_bytes_write, - GError **arg_error); -void chardet_init(void); - -#endif /* AUDACIOUS_CHARDET_H */ +/* Audacious - Cross-platform multimedia player
+ * Copyright (C) 2005-2006 Audacious 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 <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef __UI_REGEX_H__
+#define __UI_REGEX_H__
+
+#if defined USE_REGEX_ONIGURUMA
+ #include <onigposix.h>
+
+#elif defined USE_REGEX_PCRE
+ #include <pcreposix.h>
+
+#elif defined HAVE_REGEX_H
+ #include <regex.h>
+
+#elif defined HAVE_RXPOSIX_H
+ #include <rxposix.h>
+
+#elif defined HAVE_RX_RXPOSIX_H
+ #include <rx/rxposix.h>
+#endif
+
+#endif
diff --git a/src/libaudgui/ui_urlopener.c b/src/libaudgui/ui_urlopener.c index c6f9ca5..45e39c0 100644 --- a/src/libaudgui/ui_urlopener.c +++ b/src/libaudgui/ui_urlopener.c @@ -31,6 +31,7 @@ #include <gtk/gtk.h> #include <audacious/audconfig.h> +#include <audacious/gtk-compat.h> #include <audacious/i18n.h> #include <audacious/drct.h> #include <audacious/misc.h> @@ -63,7 +64,7 @@ GtkWidget * urlopener_add_url_dialog_new (GCallback func, gboolean open) vbox = gtk_vbox_new(FALSE, 10); gtk_container_add(GTK_CONTAINER(win), vbox); - combo = gtk_combo_box_entry_new_text(); + combo = gtk_combo_box_text_new_with_entry (); gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0); entry = gtk_bin_get_child(GTK_BIN(combo)); @@ -71,8 +72,8 @@ GtkWidget * urlopener_add_url_dialog_new (GCallback func, gboolean open) gtk_entry_set_text(GTK_ENTRY(entry), ""); for (url = aud_cfg->url_history; url; url = g_list_next(url)) - gtk_combo_box_append_text(GTK_COMBO_BOX(combo), - (const gchar *) url->data); + gtk_combo_box_text_append_text ((GtkComboBoxText *) combo, + (const gchar *) url->data); g_signal_connect(entry, "activate", G_CALLBACK(urlopener_add_url_callback), diff --git a/src/libaudgui/util.c b/src/libaudgui/util.c index fe2f141..ac3667a 100644 --- a/src/libaudgui/util.c +++ b/src/libaudgui/util.c @@ -24,8 +24,12 @@ #include <gtk/gtk.h> #include <audacious/debug.h> +#include <audacious/gtk-compat.h> +#include <audacious/playlist.h> #include <audacious/plugin.h> #include <audacious/misc.h> +#include <libaudcore/audstrings.h> +#include <libaudcore/hook.h> #include "libaudgui.h" #include "libaudgui-gtk.h" @@ -74,19 +78,34 @@ void audgui_connect_check_box (GtkWidget * box, gboolean * setting) void audgui_simple_message (GtkWidget * * widget, GtkMessageType type, const gchar * title, const gchar * text) { - if (* widget == NULL) + AUDDBG ("%s\n", text); + + if (* widget != NULL) { - * widget = gtk_message_dialog_new (NULL, 0, type, GTK_BUTTONS_OK, "%s", - text); - gtk_window_set_title ((GtkWindow *) * widget, title); - - g_signal_connect (* widget, "response", (GCallback) gtk_widget_destroy, - NULL); - audgui_destroy_on_escape (* widget); - g_signal_connect (* widget, "destroy", (GCallback) gtk_widget_destroyed, - widget); +#if GTK_CHECK_VERSION (2, 10, 0) + const gchar * old = NULL; + g_object_get ((GObject *) * widget, "text", & old, NULL); + g_return_if_fail (old); + + if (! strcmp (old, text)) + goto CREATED; + + gchar both[strlen (old) + strlen (text) + 2]; + snprintf (both, sizeof both, "%s\n%s", old, text); + g_object_set ((GObject *) * widget, "text", both, NULL); +#endif + goto CREATED; } + * widget = gtk_message_dialog_new (NULL, 0, type, GTK_BUTTONS_OK, "%s", text); + gtk_window_set_title ((GtkWindow *) * widget, title); + + g_signal_connect (* widget, "response", (GCallback) gtk_widget_destroy, NULL); + audgui_destroy_on_escape (* widget); + g_signal_connect (* widget, "destroy", (GCallback) gtk_widget_destroyed, + widget); + +CREATED: gtk_window_present ((GtkWindow *) * widget); } @@ -105,16 +124,20 @@ GdkPixbuf * audgui_pixbuf_from_data (void * data, gint size) return pixbuf; } -GdkPixbuf * audgui_pixbuf_for_file (const gchar * name) +GdkPixbuf * audgui_pixbuf_for_entry (gint list, gint entry) { - /* MMS is slow. Skip it. */ - if (! strncmp (name, "mms://", 6)) - return NULL; + const gchar * name = aud_playlist_entry_get_filename (list, entry); + g_return_val_if_fail (name, NULL); - InputPlugin * decoder = aud_file_find_decoder (name, FALSE); + /* Don't get album art for network files -- too slow. */ + if (! strncmp (name, "http://", 7) || ! strncmp (name, "https://", 8) || + ! strncmp (name, "mms://", 6)) + goto FALLBACK; + AUDDBG ("Trying to load pixbuf for %s.\n", name); + PluginHandle * decoder = aud_playlist_entry_get_decoder (list, entry, FALSE); if (! decoder) - return NULL; + goto FALLBACK; void * data; gint size; @@ -123,17 +146,67 @@ GdkPixbuf * audgui_pixbuf_for_file (const gchar * name) { GdkPixbuf * p = audgui_pixbuf_from_data (data, size); g_free (data); - return p; + if (p) + return p; } gchar * assoc = aud_get_associated_image_file (name); - if (! assoc) - return NULL; + if (assoc) + { + GdkPixbuf * p = gdk_pixbuf_new_from_file (assoc, NULL); + g_free (assoc); + if (p) + return p; + } + +FALLBACK:; + AUDDBG ("Using fallback pixbuf.\n"); + static GdkPixbuf * fallback = NULL; + if (! fallback) + { + gchar * path = g_strdup_printf ("%s/images/album.png", + aud_get_path (AUD_PATH_DATA_DIR)); + fallback = gdk_pixbuf_new_from_file (path, NULL); + g_free (path); + } + if (fallback) + g_object_ref ((GObject *) fallback); + return fallback; +} + + +static void clear_cached_pixbuf (void * list, GdkPixbuf * * pixbuf) +{ + if (GPOINTER_TO_INT (list) != aud_playlist_get_playing () || ! * pixbuf) + return; - GdkPixbuf * p = gdk_pixbuf_new_from_file (assoc, NULL); - g_free (assoc); - return p; + AUDDBG ("Clearing cached pixbuf.\n"); + g_object_unref ((GObject *) * pixbuf); + * pixbuf = NULL; +} + +GdkPixbuf * audgui_pixbuf_for_current (void) +{ + static GdkPixbuf * pixbuf = NULL; + static gboolean hooked = FALSE; + + if (! hooked) + { + hook_associate ("playlist position", (HookFunction) clear_cached_pixbuf, + & pixbuf); + hooked = TRUE; + } + + if (! pixbuf) + { + gint list = aud_playlist_get_playing (); + pixbuf = audgui_pixbuf_for_entry (list, aud_playlist_get_position (list)); + } + + if (pixbuf) + g_object_ref ((GObject *) pixbuf); + return pixbuf; } void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, gint size) diff --git a/src/libaudtag/Makefile b/src/libaudtag/Makefile index a440424..db21388 100644 --- a/src/libaudtag/Makefile +++ b/src/libaudtag/Makefile @@ -16,6 +16,6 @@ INCLUDES = audtag.h include ../../buildsys.mk include ../../extra.mk -CPPFLAGS += ${LIB_CPPFLAGS} ${GLIB_CFLAGS} ${MOWGLI_CFLAGS} -D_AUDACIOUS_CORE -I.. -I../.. +CPPFLAGS += ${GLIB_CFLAGS} ${MOWGLI_CFLAGS} -D_AUDACIOUS_CORE -I.. -I../.. CFLAGS += ${LIB_CFLAGS} LIBS += ${GLIB_LIBS} ${MOWGLI_LIBS} -L../libaudcore -laudcore diff --git a/src/libaudtag/aac/aac.c b/src/libaudtag/aac/aac.c index 0053cd5..8751d8a 100644 --- a/src/libaudtag/aac/aac.c +++ b/src/libaudtag/aac/aac.c @@ -79,8 +79,8 @@ void writeAtom(VFSFile * fd, Atom * atom) void printAtom(Atom * atom) { - AUDDBG("size = %x\n", atom->size); - AUDDBG("name = %s\n", atom->name); + TAGDBG("size = %x\n", atom->size); + TAGDBG("name = %s\n", atom->name); } StrDataAtom *readStrDataAtom(VFSFile * fd) @@ -119,7 +119,7 @@ Atom *findAtom(VFSFile * fd, gchar * name) if (vfs_feof(fd)) { g_free(atom); - AUDDBG("The atom %s could not be found\n", name); + TAGDBG("The atom %s could not be found\n", name); return NULL; } return atom; @@ -140,7 +140,7 @@ Atom *getilstAtom(VFSFile * fd) vfs_fseek(fd, -(meta->size - 11), SEEK_CUR); Atom *ilst = findAtom(fd, ILST); - AUDDBG("zzz = %d\n", vfs_ftell(fd)); + TAGDBG("zzz = %d\n", vfs_ftell(fd)); ilstFileOffset = vfs_ftell(fd) - ilst->size; vfs_fseek(fd, -(ilst->size - 7), SEEK_CUR); @@ -191,7 +191,7 @@ void writeAtomListToFile(VFSFile * from, VFSFile * to, int offset, mowgli_list_t g_free(atom); if (vfs_feof(from)) { - AUDDBG("No free atoms\n"); + TAGDBG("No free atoms\n"); g_free(atom); atom = NULL; } diff --git a/src/libaudtag/ape/ape.c b/src/libaudtag/ape/ape.c index 36c55a3..f1f5d8e 100755 --- a/src/libaudtag/ape/ape.c +++ b/src/libaudtag/ape/ape.c @@ -84,7 +84,7 @@ static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * if (ape_read_header (handle, header)) { - AUDDBG ("Found header at 0, length = %d, version = %d.\n", (gint) + TAGDBG ("Found header at 0, length = %d, version = %d.\n", (gint) header->length, (gint) header->version); * start = 0; * length = header->length; @@ -94,7 +94,7 @@ static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * if (! (header->flags & APE_FLAG_HAS_HEADER) || ! (header->flags & APE_FLAG_IS_HEADER)) { - AUDDBG ("Invalid header flags (%u).\n", (guint) header->flags); + TAGDBG ("Invalid header flags (%u).\n", (guint) header->flags); return FALSE; } @@ -105,7 +105,7 @@ static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * if (! ape_read_header (handle, & secondary)) { - AUDDBG ("Expected footer, but found none.\n"); + TAGDBG ("Expected footer, but found none.\n"); return FALSE; } @@ -120,7 +120,7 @@ static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * if (ape_read_header (handle, header)) { - AUDDBG ("Found footer at %d, length = %d, version = %d.\n", (gint) + TAGDBG ("Found footer at %d, length = %d, version = %d.\n", (gint) vfs_ftell (handle) - (gint) sizeof (APEHeader), (gint) header->length, (gint) header->version); * start = vfs_ftell (handle) - header->length; @@ -131,7 +131,7 @@ static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * if ((header->flags & APE_FLAG_HAS_NO_FOOTER) || (header->flags & APE_FLAG_IS_HEADER)) { - AUDDBG ("Invalid footer flags (%u).\n", (guint) header->flags); + TAGDBG ("Invalid footer flags (%u).\n", (guint) header->flags); return FALSE; } @@ -143,7 +143,7 @@ static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * if (! ape_read_header (handle, & secondary)) { - AUDDBG ("Expected header, but found none.\n"); + TAGDBG ("Expected header, but found none.\n"); return FALSE; } @@ -154,7 +154,7 @@ static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * return TRUE; } - AUDDBG ("No header found.\n"); + TAGDBG ("No header found.\n"); return FALSE; } @@ -175,7 +175,7 @@ static ValuePair * ape_read_item (void * * data, gint length) if (length < 8) { - AUDDBG ("Expected item, but only %d bytes remain in tag.\n", length); + TAGDBG ("Expected item, but only %d bytes remain in tag.\n", length); return NULL; } @@ -183,7 +183,7 @@ static ValuePair * ape_read_item (void * * data, gint length) if (value == NULL) { - AUDDBG ("Unterminated item key (max length = %d).\n", length - 8); + TAGDBG ("Unterminated item key (max length = %d).\n", length - 8); return NULL; } @@ -191,7 +191,7 @@ static ValuePair * ape_read_item (void * * data, gint length) if (header[0] > (gchar *) (* data) + length - value) { - AUDDBG ("Item value of length %d, but only %d bytes remain in tag.\n", + TAGDBG ("Item value of length %d, but only %d bytes remain in tag.\n", (gint) header[0], (gint) ((gchar *) (* data) + length - value)); return NULL; } @@ -227,7 +227,7 @@ static GList * ape_read_items (VFSFile * handle) return NULL; } - AUDDBG ("Reading %d items:\n", header.items); + TAGDBG ("Reading %d items:\n", header.items); item = data; while (header.items --) @@ -238,7 +238,7 @@ static GList * ape_read_items (VFSFile * handle) if (pair == NULL) break; - AUDDBG ("Read: %s = %s.\n", pair->key, pair->value); + TAGDBG ("Read: %s = %s.\n", pair->key, pair->value); list = g_list_prepend (list, pair); } @@ -354,7 +354,7 @@ static gboolean ape_write_item (VFSFile * handle, const gchar * key, gint value_len = strlen (value); guint32 header[2]; - AUDDBG ("Write: %s = %s.\n", key, value); + TAGDBG ("Write: %s = %s.\n", key, value); header[0] = GUINT32_TO_LE (value_len); header[1] = 0; @@ -433,24 +433,24 @@ static gboolean ape_write_tag (const Tuple * tuple, VFSFile * handle) { if (start + length != vfs_fsize (handle)) { - AUDDBG ("Writing tags is only supported at end of file.\n"); - goto ERROR; + TAGDBG ("Writing tags is only supported at end of file.\n"); + goto ERR; } if (vfs_ftruncate (handle, start)) - goto ERROR; + goto ERR; } else { start = vfs_fsize (handle); if (start < 0) - goto ERROR; + goto ERR; } if (vfs_fseek (handle, start, SEEK_SET) || ! write_header (0, 0, TRUE, handle)) - goto ERROR; + goto ERR; length = 0; items = 0; @@ -463,7 +463,7 @@ static gboolean ape_write_tag (const Tuple * tuple, VFSFile * handle) FIELD_GENRE, handle, "Genre", & length, & items) || ! write_integer_item (tuple, FIELD_TRACK_NUMBER, handle, "Track", & length, & items) || ! write_integer_item (tuple, FIELD_YEAR, handle, "Year", & length, & items)) - goto ERROR; + goto ERR; for (node = list; node != NULL; node = node->next) { @@ -476,21 +476,21 @@ static gboolean ape_write_tag (const Tuple * tuple, VFSFile * handle) continue; if (! ape_write_item (handle, key, value, & length)) - goto ERROR; + goto ERR; items ++; } - AUDDBG ("Wrote %d items, %d bytes.\n", items, length); + TAGDBG ("Wrote %d items, %d bytes.\n", items, length); if (! write_header (length, items, FALSE, handle) || vfs_fseek (handle, start, SEEK_SET) || ! write_header (length, items, TRUE, handle)) - goto ERROR; + goto ERR; free_tag_list (list); return TRUE; -ERROR: +ERR: free_tag_list (list); return FALSE; } diff --git a/src/libaudtag/audtag.c b/src/libaudtag/audtag.c index 31c8b73..6db71d9 100644 --- a/src/libaudtag/audtag.c +++ b/src/libaudtag/audtag.c @@ -23,11 +23,18 @@ #include "tag_module.h" #include "util.h" +gboolean tag_verbose = FALSE; + void tag_init(void) { init_tag_modules(); } +void tag_set_verbose (gboolean verbose) +{ + tag_verbose = verbose; +} + /* The tuple's file-related attributes are already set */ gboolean tag_tuple_read (Tuple * tuple, VFSFile * handle) diff --git a/src/libaudtag/audtag.h b/src/libaudtag/audtag.h index df20961..725cf5c 100644 --- a/src/libaudtag/audtag.h +++ b/src/libaudtag/audtag.h @@ -37,8 +37,8 @@ enum TAG_TYPE_APE, }; -void tag_init(void); -void tag_terminate(void); +void tag_init (void); +void tag_set_verbose (gboolean verbose); gboolean tag_tuple_read (Tuple * tuple, VFSFile *fd); gboolean tag_image_read (VFSFile * handle, void * * data, gint * size); diff --git a/src/libaudtag/id3/id3-common.c b/src/libaudtag/id3/id3-common.c index 9a07ba6..ea7c587 100644 --- a/src/libaudtag/id3/id3-common.c +++ b/src/libaudtag/id3/id3-common.c @@ -34,7 +34,7 @@ gchar * convert_text (const gchar * text, gint length, gint encoding, gboolean gchar * buffer = NULL; gsize converted = 0; - AUDDBG ("length = %d, encoding = %d, nulled = %d\n", length, encoding, + TAGDBG ("length = %d, encoding = %d, nulled = %d\n", length, encoding, nulled); if (nulled) @@ -50,7 +50,7 @@ gchar * convert_text (const gchar * text, gint length, gint encoding, gboolean return NULL; length = null - text; - AUDDBG ("length before null = %d\n", length); + TAGDBG ("length before null = %d\n", length); if (after != NULL) * after = null + 1; @@ -62,7 +62,7 @@ gchar * convert_text (const gchar * text, gint length, gint encoding, gboolean return NULL; length = null - text; - AUDDBG ("length before null = %d\n", length); + TAGDBG ("length before null = %d\n", length); if (after != NULL) * after = null + 2; @@ -75,7 +75,7 @@ gchar * convert_text (const gchar * text, gint length, gint encoding, gboolean { case 0: case 3: - buffer = chardet_to_utf8 (text, length, NULL, & converted, NULL); + buffer = str_to_utf8_full (text, length, NULL, & converted, NULL); break; case 1: if (text[0] == (gchar) 0xff) @@ -92,8 +92,8 @@ gchar * convert_text (const gchar * text, gint length, gint encoding, gboolean break; } - AUDDBG ("length converted: %d\n", (gint) converted); - AUDDBG ("string: %s\n", buffer); + TAGDBG ("length converted: %d\n", (gint) converted); + TAGDBG ("string: %s\n", buffer); if (_converted != NULL) * _converted = converted; diff --git a/src/libaudtag/id3/id3v1.c b/src/libaudtag/id3/id3v1.c index 6897d3e..f6426ef 100644 --- a/src/libaudtag/id3/id3v1.c +++ b/src/libaudtag/id3/id3v1.c @@ -123,9 +123,8 @@ gboolean id3v1_read_tag (Tuple * tuple, VFSFile * f) { tuple_associate_string(tuple, FIELD_GENRE, NULL, ext_genre); genre_set = TRUE; + g_free(ext_genre); } - - g_free(ext_genre); } tuple_associate_string(tuple, FIELD_TITLE, NULL, title); @@ -159,6 +158,7 @@ ERR: static gboolean id3v1_write_tag (const Tuple * tuple, VFSFile * handle) { + fprintf (stderr, "Writing ID3v1 tags is not implemented yet, sorry.\n"); return FALSE; } diff --git a/src/libaudtag/id3/id3v22.c b/src/libaudtag/id3/id3v22.c index 331b366..65e6796 100644 --- a/src/libaudtag/id3/id3v22.c +++ b/src/libaudtag/id3/id3v22.c @@ -97,12 +97,12 @@ static gboolean validate_header (ID3v2Header * header) header->size = unsyncsafe32(GUINT32_FROM_BE(header->size)); - AUDDBG ("Found ID3v2 header:\n"); - AUDDBG (" magic = %.3s\n", header->magic); - AUDDBG (" version = %d\n", (gint) header->version); - AUDDBG (" revision = %d\n", (gint) header->revision); - AUDDBG (" flags = %x\n", (gint) header->flags); - AUDDBG (" size = %d\n", (gint) header->size); + TAGDBG ("Found ID3v2 header:\n"); + TAGDBG (" magic = %.3s\n", header->magic); + TAGDBG (" version = %d\n", (gint) header->version); + TAGDBG (" revision = %d\n", (gint) header->revision); + TAGDBG (" flags = %x\n", (gint) header->flags); + TAGDBG (" size = %d\n", (gint) header->size); return TRUE; } @@ -128,7 +128,7 @@ static gboolean read_header (VFSFile * handle, gint * version, gboolean * * syncsafe = (header.flags & ID3_HEADER_SYNCSAFE) ? TRUE : FALSE; - AUDDBG ("Offset = %d, header size = %d, data size = %d\n", + TAGDBG ("Offset = %d, header size = %d, data size = %d\n", (gint) * offset, * header_size, * data_size); return TRUE; @@ -154,16 +154,16 @@ static gboolean read_frame (VFSFile * handle, gint max_size, gint version, for (i = 0; i < 3; i++) { hdrsz |= (guint32) header.size[i] << ((2 - i) * 8); - AUDDBG("header.size[%d] = %d hdrsz %d slot %d\n", i, header.size[i], hdrsz, 2 - i); + TAGDBG("header.size[%d] = %d hdrsz %d slot %d\n", i, header.size[i], hdrsz, 2 - i); } // hdrsz = GUINT32_TO_BE(hdrsz); if (hdrsz > max_size || hdrsz == 0) return FALSE; - AUDDBG ("Found frame:\n"); - AUDDBG (" key = %.3s\n", header.key); - AUDDBG (" size = %d\n", (gint) hdrsz); + TAGDBG ("Found frame:\n"); + TAGDBG (" key = %.3s\n", header.key); + TAGDBG (" size = %d\n", (gint) hdrsz); * frame_size = sizeof (ID3v2FrameHeader) + hdrsz; sprintf (key, "%.3s", header.key); @@ -174,7 +174,7 @@ static gboolean read_frame (VFSFile * handle, gint max_size, gint version, if (vfs_fread (* data, 1, * size, handle) != * size) return FALSE; - AUDDBG ("Data size = %d.\n", * size); + TAGDBG ("Data size = %d.\n", * size); return TRUE; } @@ -226,9 +226,9 @@ static void associate_string (Tuple * tuple, gint field, const gchar * return; if (customfield != NULL) - AUDDBG ("Custom field %s = %s.\n", customfield, text); + TAGDBG ("Custom field %s = %s.\n", customfield, text); else - AUDDBG ("Field %i = %s.\n", field, text); + TAGDBG ("Field %i = %s.\n", field, text); tuple_associate_string (tuple, field, customfield, text); g_free (text); @@ -243,9 +243,9 @@ static void associate_int (Tuple * tuple, gint field, const gchar * return; if (customfield != NULL) - AUDDBG ("Custom field %s = %s.\n", customfield, text); + TAGDBG ("Custom field %s = %s.\n", customfield, text); else - AUDDBG ("Field %i = %s.\n", field, text); + TAGDBG ("Field %i = %s.\n", field, text); tuple_associate_int (tuple, field, customfield, atoi (text)); g_free (text); @@ -258,7 +258,7 @@ static void decode_comment (Tuple * tuple, const guchar * data, gint size) if (! decode_comment_frame (data, size, & lang, & type, & value)) return; - AUDDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value); + TAGDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value); if (! type[0]) /* blank type == actual comment */ tuple_associate_string (tuple, FIELD_COMMENT, NULL, value); @@ -281,7 +281,7 @@ static void decode_txx (Tuple * tuple, const guchar * data, gint size) return; gchar * value = separator + 1; - AUDDBG ("TXX: %s = %s.\n", text, value); + TAGDBG ("TXX: %s = %s.\n", text, value); tuple_associate_string (tuple, -1, text, value); g_free (text); @@ -307,7 +307,7 @@ static gboolean decode_rva_block (const guchar * * _data, gint * _size, gint * data += 4; size -= 4; - AUDDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n", + TAGDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n", * channel, * adjustment, * adjustment_unit, peak_bits); if (peak_bits > 0 && peak_bits < sizeof (gint) * 8) @@ -327,7 +327,7 @@ static gboolean decode_rva_block (const guchar * * _data, gint * _size, gint * data += bytes; size -= count; - AUDDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit); + TAGDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit); } else { @@ -350,7 +350,7 @@ static void decode_rva (Tuple * tuple, const guchar * data, gint size) domain = (const gchar *) data; - AUDDBG ("RVA domain: %s\n", domain); + TAGDBG ("RVA domain: %s\n", domain); size -= strlen (domain) + 1; data += strlen (domain) + 1; @@ -444,7 +444,7 @@ gboolean id3v22_read_tag (Tuple * tuple, VFSFile * handle) & data_size)) return FALSE; - AUDDBG("Reading tags from %i bytes of ID3 data in %s\n", data_size, handle->uri); + TAGDBG("Reading tags from %i bytes of ID3 data in %s\n", data_size, handle->uri); for (pos = 0; pos < data_size; ) { @@ -455,7 +455,7 @@ gboolean id3v22_read_tag (Tuple * tuple, VFSFile * handle) if (! read_frame (handle, data_size - pos, version, syncsafe, & frame_size, key, & data, & size)) { - AUDDBG("read_frame failed at pos %i\n", pos); + TAGDBG("read_frame failed at pos %i\n", pos); break; } @@ -507,7 +507,7 @@ gboolean id3v22_read_tag (Tuple * tuple, VFSFile * handle) decode_rva (tuple, data, size); break; default: - AUDDBG ("Ignoring unsupported ID3 frame %s.\n", key); + TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key); break; } @@ -533,7 +533,7 @@ static gboolean parse_pic (const guchar * data, gint size, gchar * * mime, * image_data = g_memdup (after, data + size - after); * image_size = data + size - after; - AUDDBG ("PIC: mime = %s, type = %d, size = %d.\n", * mime, + TAGDBG ("PIC: mime = %s, type = %d, size = %d.\n", * mime, * type, * image_size); return TRUE; } @@ -589,6 +589,7 @@ static gboolean id3v22_read_image (VFSFile * handle, void * * image_data, gint * static gboolean id3v22_write_tag (const Tuple * tuple, VFSFile * f) { + fprintf (stderr, "Writing ID3v2.2 tags is not implemented yet, sorry.\n"); return FALSE; } diff --git a/src/libaudtag/id3/id3v24.c b/src/libaudtag/id3/id3v24.c index da1de0a..d655ce8 100644 --- a/src/libaudtag/id3/id3v24.c +++ b/src/libaudtag/id3/id3v24.c @@ -118,7 +118,7 @@ static gboolean skip_extended_header_3 (VFSFile * handle, gint * _size) size = GUINT32_FROM_BE (size); - AUDDBG ("Found v2.3 extended header, size = %d.\n", (gint) size); + TAGDBG ("Found v2.3 extended header, size = %d.\n", (gint) size); if (vfs_fseek (handle, size, SEEK_CUR)) return FALSE; @@ -136,7 +136,7 @@ static gboolean skip_extended_header_4 (VFSFile * handle, gint * _size) size = unsyncsafe32 (GUINT32_FROM_BE (size)); - AUDDBG ("Found v2.4 extended header, size = %d.\n", (gint) size); + TAGDBG ("Found v2.4 extended header, size = %d.\n", (gint) size); if (vfs_fseek (handle, size - 4, SEEK_CUR)) return FALSE; @@ -155,12 +155,12 @@ static gboolean validate_header (ID3v2Header * header, gboolean is_footer) header->size = unsyncsafe32 (GUINT32_FROM_BE (header->size)); - AUDDBG ("Found ID3v2 %s:\n", is_footer ? "footer" : "header"); - AUDDBG (" magic = %.3s\n", header->magic); - AUDDBG (" version = %d\n", (gint) header->version); - AUDDBG (" revision = %d\n", (gint) header->revision); - AUDDBG (" flags = %x\n", (gint) header->flags); - AUDDBG (" size = %d\n", (gint) header->size); + TAGDBG ("Found ID3v2 %s:\n", is_footer ? "footer" : "header"); + TAGDBG (" magic = %.3s\n", header->magic); + TAGDBG (" version = %d\n", (gint) header->version); + TAGDBG (" revision = %d\n", (gint) header->revision); + TAGDBG (" flags = %x\n", (gint) header->flags); + TAGDBG (" size = %d\n", (gint) header->size); return TRUE; } @@ -256,7 +256,7 @@ static gboolean read_header (VFSFile * handle, gint * version, gboolean * * data_size -= extended_size; } - AUDDBG ("Offset = %d, header size = %d, data size = %d, footer size = " + TAGDBG ("Offset = %d, header size = %d, data size = %d, footer size = " "%d.\n", (gint) * offset, * header_size, * data_size, * footer_size); return TRUE; @@ -303,17 +303,17 @@ static gboolean read_frame (VFSFile * handle, gint max_size, gint version, if (header.size > max_size || header.size == 0) return FALSE; - AUDDBG ("Found frame:\n"); - AUDDBG (" key = %.4s\n", header.key); - AUDDBG (" size = %d\n", (gint) header.size); - AUDDBG (" flags = %x\n", (gint) header.flags); + TAGDBG ("Found frame:\n"); + TAGDBG (" key = %.4s\n", header.key); + TAGDBG (" size = %d\n", (gint) header.size); + TAGDBG (" flags = %x\n", (gint) header.flags); * frame_size = sizeof (ID3v2FrameHeader) + header.size; sprintf (key, "%.4s", header.key); if (header.flags & (ID3_FRAME_COMPRESSED | ID3_FRAME_ENCRYPTED)) { - AUDDBG ("Hit compressed/encrypted frame %s.\n", key); + TAGDBG ("Hit compressed/encrypted frame %s.\n", key); return FALSE; } @@ -334,7 +334,7 @@ static gboolean read_frame (VFSFile * handle, gint max_size, gint version, if (syncsafe || (header.flags & ID3_FRAME_SYNCSAFE)) * size = unsyncsafe (* data, * size); - AUDDBG ("Data size = %d.\n", * size); + TAGDBG ("Data size = %d.\n", * size); return TRUE; } @@ -390,7 +390,7 @@ static void read_all_frames (VFSFile * handle, gint version, gboolean syncsafe, if (mowgli_dictionary_retrieve (dict, key) != NULL) { - AUDDBG ("Discarding duplicate frame %s.\n", key); + TAGDBG ("Discarding duplicate frame %s.\n", key); g_free (data); continue; } @@ -407,7 +407,7 @@ static void read_all_frames (VFSFile * handle, gint version, gboolean syncsafe, static gboolean write_frame (VFSFile * handle, GenericFrame * frame, gint * frame_size) { - AUDDBG ("Writing frame %s, size %d\n", frame->key, frame->size); + TAGDBG ("Writing frame %s, size %d\n", frame->key, frame->size); ID3v2FrameHeader header; @@ -447,7 +447,7 @@ static gint writeAllFramesToFile (VFSFile * fd, mowgli_dictionary_t * dict) WriteState state = {fd, 0}; mowgli_dictionary_foreach (dict, write_frame_cb, & state); - AUDDBG ("Total frame bytes written = %d.\n", state.written_size); + TAGDBG ("Total frame bytes written = %d.\n", state.written_size); return state.written_size; } @@ -485,12 +485,15 @@ static void associate_string (Tuple * tuple, gint field, const gchar * gchar * text = decode_text_frame (data, size); if (text == NULL || ! text[0]) + { + g_free (text); return; + } if (customfield != NULL) - AUDDBG ("Custom field %s = %s.\n", customfield, text); + TAGDBG ("Custom field %s = %s.\n", customfield, text); else - AUDDBG ("Field %i = %s.\n", field, text); + TAGDBG ("Field %i = %s.\n", field, text); tuple_associate_string (tuple, field, customfield, text); g_free (text); @@ -505,9 +508,9 @@ static void associate_int (Tuple * tuple, gint field, const gchar * return; if (customfield != NULL) - AUDDBG ("Custom field %s = %s.\n", customfield, text); + TAGDBG ("Custom field %s = %s.\n", customfield, text); else - AUDDBG ("Field %i = %s.\n", field, text); + TAGDBG ("Field %i = %s.\n", field, text); tuple_associate_int (tuple, field, customfield, atoi (text)); g_free (text); @@ -545,10 +548,10 @@ static void decode_private_info (Tuple * tuple, const guchar * data, gint size) if (!memcmp(value, SECONDARY_CLASS_GAMES_SONG, 16)) tuple_associate_string (tuple, -1, "media-class", "Game Soundtrack"); } else { - AUDDBG("Unrecognised tag %s (Windows Media) ignored\n", text); + TAGDBG("Unrecognised tag %s (Windows Media) ignored\n", text); } } else { - AUDDBG("Unable to decode private data, skipping: %s\n", text); + TAGDBG("Unable to decode private data, skipping: %s\n", text); } DONE: @@ -562,7 +565,7 @@ static void decode_comment (Tuple * tuple, const guchar * data, gint size) if (! decode_comment_frame (data, size, & lang, & type, & value)) return; - AUDDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value); + TAGDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value); if (! type[0]) /* blank type == actual comment */ tuple_associate_string (tuple, FIELD_COMMENT, NULL, value); @@ -585,7 +588,7 @@ static void decode_txxx (Tuple * tuple, const guchar * data, gint size) return; gchar * value = separator + 1; - AUDDBG ("TXXX: %s = %s.\n", text, value); + TAGDBG ("TXXX: %s = %s.\n", text, value); tuple_associate_string (tuple, -1, text, value); g_free (text); @@ -611,7 +614,7 @@ static gboolean decode_rva2_block (const guchar * * _data, gint * _size, gint * data += 4; size -= 4; - AUDDBG ("RVA2 block: channel = %d, adjustment = %d/%d, peak bits = %d\n", + TAGDBG ("RVA2 block: channel = %d, adjustment = %d/%d, peak bits = %d\n", * channel, * adjustment, * adjustment_unit, peak_bits); if (peak_bits > 0 && peak_bits < sizeof (gint) * 8) @@ -631,7 +634,7 @@ static gboolean decode_rva2_block (const guchar * * _data, gint * _size, gint * data += bytes; size -= count; - AUDDBG ("RVA2 block: peak = %d/%d\n", * peak, * peak_unit); + TAGDBG ("RVA2 block: peak = %d/%d\n", * peak, * peak_unit); } else { @@ -654,7 +657,7 @@ static void decode_rva2 (Tuple * tuple, const guchar * data, gint size) domain = (const gchar *) data; - AUDDBG ("RVA2 domain: %s\n", domain); + TAGDBG ("RVA2 domain: %s\n", domain); size -= strlen (domain) + 1; data += strlen (domain) + 1; @@ -718,11 +721,11 @@ static void decode_genre (Tuple * tuple, const guchar * data, gint size) numericgenre = atoi (text); if (numericgenre > 0) - { - tuple_associate_string(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(numericgenre)); - return; - } - tuple_associate_string(tuple, FIELD_GENRE, NULL, text); + tuple_associate_string (tuple, FIELD_GENRE, NULL, + convert_numericgenre_to_text (numericgenre)); + else + tuple_associate_string (tuple, FIELD_GENRE, NULL, text); + g_free (text); return; } @@ -752,7 +755,7 @@ static void remove_frame (gint id, mowgli_dictionary_t * dict) if (frame == NULL) return; - AUDDBG ("Deleting frame %s.\n", id3_frames[id]); + TAGDBG ("Deleting frame %s.\n", id3_frames[id]); mowgli_dictionary_delete (dict, id3_frames[id]); free_generic_frame (frame); } @@ -766,7 +769,7 @@ static void add_text_frame (gint id, const gchar * text, mowgli_dictionary_t * return; } - AUDDBG ("Adding text frame %s = %s.\n", id3_frames[id], text); + TAGDBG ("Adding text frame %s = %s.\n", id3_frames[id], text); gint length = strlen (text); GenericFrame * frame = add_generic_frame (id, length + 1, dict); @@ -782,7 +785,7 @@ static void add_comment_frame (const gchar * text, mowgli_dictionary_t * dict) return; } - AUDDBG ("Adding comment frame = %s.\n", text); + TAGDBG ("Adding comment frame = %s.\n", text); gint length = strlen (text); GenericFrame * frame = add_generic_frame (ID3_COMMENT, length + 5, dict); @@ -896,7 +899,7 @@ static gboolean id3v24_read_tag (Tuple * tuple, VFSFile * handle) decode_rva2 (tuple, data, size); break; default: - AUDDBG ("Ignoring unsupported ID3 frame %s.\n", key); + TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key); break; } @@ -925,7 +928,7 @@ static gboolean parse_apic (const guchar * _data, gint size, gchar * * mime, * image_data = g_memdup (after, data + size - after); * image_size = data + size - after; - AUDDBG ("APIC: mime = %s, type = %d, desc = %s, size = %d.\n", * mime, + TAGDBG ("APIC: mime = %s, type = %d, desc = %s, size = %d.\n", * mime, * type, * desc, * image_size); return TRUE; } @@ -1008,32 +1011,32 @@ static gboolean id3v24_write_tag (const Tuple * tuple, VFSFile * f) if (! offset) { if (! cut_beginning_tag (f, header_size + data_size + footer_size)) - goto ERROR; + goto ERR; } else { if (offset + header_size + data_size + footer_size != vfs_fsize (f)) - goto ERROR; + goto ERR; if (vfs_ftruncate (f, offset)) - goto ERROR; + goto ERR; } offset = vfs_fsize (f); if (offset < 0 || vfs_fseek (f, offset, SEEK_SET) || ! write_header (f, 0, FALSE)) - goto ERROR; + goto ERR; data_size = writeAllFramesToFile (f, dict); if (! write_header (f, data_size, TRUE) || vfs_fseek (f, offset, SEEK_SET) || ! write_header (f, data_size, FALSE)) - goto ERROR; + goto ERR; mowgli_dictionary_destroy (dict, free_frame_cb, NULL); return TRUE; -ERROR: +ERR: mowgli_dictionary_destroy (dict, free_frame_cb, NULL); return FALSE; } diff --git a/src/libaudtag/tag_module.c b/src/libaudtag/tag_module.c index bba9f64..0b2f743 100644 --- a/src/libaudtag/tag_module.c +++ b/src/libaudtag/tag_module.c @@ -47,13 +47,13 @@ tag_module_t * find_tag_module (VFSFile * fd, gint new_type) { if (vfs_fseek(fd, 0, SEEK_SET)) { - AUDDBG("not a seekable file\n"); + TAGDBG("not a seekable file\n"); return NULL; } if (((tag_module_t *) mod->data)->can_handle_file (fd)) { - AUDDBG ("Module %s accepted file.\n", ((tag_module_t *) + TAGDBG ("Module %s accepted file.\n", ((tag_module_t *) mod->data)->name); return mod->data; } @@ -69,6 +69,6 @@ tag_module_t * find_tag_module (VFSFile * fd, gint new_type) } } - AUDDBG("no module found\n"); + TAGDBG("no module found\n"); return NULL; } diff --git a/src/libaudtag/util.c b/src/libaudtag/util.c index 73b33c1..5d75777 100644 --- a/src/libaudtag/util.c +++ b/src/libaudtag/util.c @@ -61,66 +61,66 @@ const gchar *get_complete_filepath(Tuple * tuple) dir = tuple_get_string(tuple, FIELD_FILE_PATH, NULL); file = tuple_get_string(tuple, FIELD_FILE_NAME, NULL); filepath = g_strdup_printf("%s/%s", dir, file); - AUDDBG("file path = %s\n", filepath); + TAGDBG("file path = %s\n", filepath); return filepath; } void print_tuple(Tuple * tuple) { #if WMA_DEBUG - AUDDBG("--------------TUPLE PRINT --------------------\n"); + TAGDBG("--------------TUPLE PRINT --------------------\n"); const gchar *title = tuple_get_string(tuple, FIELD_TITLE, NULL); - AUDDBG("title = %s\n", title); + TAGDBG("title = %s\n", title); /* artist */ const gchar *artist = tuple_get_string(tuple, FIELD_ARTIST, NULL); - AUDDBG("artist = %s\n", artist); + TAGDBG("artist = %s\n", artist); /* copyright */ const gchar *copyright = tuple_get_string(tuple, FIELD_COPYRIGHT, NULL); - AUDDBG("copyright = %s\n", copyright); + TAGDBG("copyright = %s\n", copyright); /* comment / description */ const gchar *comment = tuple_get_string(tuple, FIELD_COMMENT, NULL); - AUDDBG("comment = %s\n", comment); + TAGDBG("comment = %s\n", comment); /* codec name */ const gchar *codec_name = tuple_get_string(tuple, FIELD_CODEC, NULL); - AUDDBG("codec = %s\n", codec_name); + TAGDBG("codec = %s\n", codec_name); /* album */ const gchar *album = tuple_get_string(tuple, FIELD_ALBUM, NULL); - AUDDBG("Album = %s\n", album); + TAGDBG("Album = %s\n", album); /*track number */ gint track_nr = tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL); - AUDDBG("Track nr = %d\n", track_nr); + TAGDBG("Track nr = %d\n", track_nr); /* genre */ const gchar *genre = tuple_get_string(tuple, FIELD_GENRE, NULL); - AUDDBG("Genre = %s \n", genre); + TAGDBG("Genre = %s \n", genre); /* length */ gint length = tuple_get_int(tuple, FIELD_LENGTH, NULL); - AUDDBG("Length = %d\n", length); + TAGDBG("Length = %d\n", length); /* year */ gint year = tuple_get_int(tuple, FIELD_YEAR, NULL); - AUDDBG("Year = %d\n", year); + TAGDBG("Year = %d\n", year); /* quality */ const gchar *quality = tuple_get_string(tuple, FIELD_QUALITY, NULL); - AUDDBG("quality = %s\n", quality); + TAGDBG("quality = %s\n", quality); /* path */ const gchar *path = tuple_get_string(tuple, FIELD_FILE_PATH, NULL); - AUDDBG("path = %s\n", path); + TAGDBG("path = %s\n", path); /* filename */ const gchar *filename = tuple_get_string(tuple, FIELD_FILE_NAME, NULL); - AUDDBG("filename = %s\n", filename); + TAGDBG("filename = %s\n", filename); - AUDDBG("-----------------END---------------------\n"); + TAGDBG("-----------------END---------------------\n"); #endif } @@ -156,7 +156,7 @@ gunichar2 *fread_utf16(VFSFile * f, guint64 size) p = NULL; } gchar *s = utf8(p); - AUDDBG("Converted to UTF8: '%s'\n", s); + TAGDBG("Converted to UTF8: '%s'\n", s); g_free(s); return p; } diff --git a/src/libaudtag/util.h b/src/libaudtag/util.h index 7d9acc0..4692cfd 100644 --- a/src/libaudtag/util.h +++ b/src/libaudtag/util.h @@ -23,7 +23,7 @@ #define TAGUTIL_H #include <glib.h> -#include <audacious/debug.h> + #include "libaudcore/tuple.h" #include "libaudcore/vfs.h" @@ -157,6 +157,10 @@ enum { GENRE_EURO_HOUSE }; +extern gboolean tag_verbose; + +#define TAGDBG(...) do {if (tag_verbose) {printf ("%s:%d [%s]: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__);}} while (0) + time_t unix_time(guint64 win_time); guint16 get_year(guint64 win_time); diff --git a/src/libaudtag/wma/guid.c b/src/libaudtag/wma/guid.c index 473c138..f464335 100644 --- a/src/libaudtag/wma/guid.c +++ b/src/libaudtag/wma/guid.c @@ -27,11 +27,11 @@ #include "guid.h" #include "wma_fmt.h" -GUID *guid_read_from_file(VFSFile * f) +GUID_t *guid_read_from_file(VFSFile * f) { - GUID temp; + GUID_t temp; - if ((f == NULL) || (vfs_fread(&temp, sizeof(GUID), 1, f) != 1)) + if ((f == NULL) || (vfs_fread(&temp, sizeof(GUID_t), 1, f) != 1)) return NULL; temp.le32 = GUINT32_FROM_LE(temp.le32); @@ -39,56 +39,56 @@ GUID *guid_read_from_file(VFSFile * f) temp.le16_2 = GUINT16_FROM_LE(temp.le16_2); temp.be64 = GUINT64_FROM_BE(temp.be64); - return g_memdup(&temp, sizeof(GUID)); + return g_memdup(&temp, sizeof(GUID_t)); } gboolean guid_write_to_file(VFSFile * f, int guid_type) { g_return_val_if_fail(f != NULL, FALSE); - GUID *g = guid_convert_from_string(wma_guid_map(guid_type)); + GUID_t *g = guid_convert_from_string(wma_guid_map(guid_type)); gboolean ret = write_LEuint32(f, g->le32) && write_LEuint16(f, g->le16_1) && write_LEuint16(f, g->le16_1) && write_LEuint64(f, g->be64); g_free(g); return ret; } -GUID *guid_convert_from_string(const gchar * string) +GUID_t *guid_convert_from_string(const gchar * string) { - GUID temp; + GUID_t temp; if (sscanf(string, "%" SCNx32 "-%" SCNx16 "-%" SCNx16 "-%" SCNx64, &temp.le32, &temp.le16_1, &temp.le16_2, &temp.be64) != 4) return NULL; - return g_memdup(&temp, sizeof(GUID)); + return g_memdup(&temp, sizeof(GUID_t)); } -gchar *guid_convert_to_string(const GUID * g) +gchar *guid_convert_to_string(const GUID_t * g) { return g_strdup_printf("%8x-%hx-%hx-%" PRIx64 "\n", GUINT32_TO_LE(g->le32), GUINT16_TO_LE(g->le16_1), GUINT16_TO_LE(g->le16_2), GUINT64_TO_BE(g->be64)); } -gboolean guid_equal(GUID * g1, GUID * g2) +gboolean guid_equal(GUID_t * g1, GUID_t * g2) { /* - AUDDBG("GUID 1 = %8x-%hx-%hx-%"PRIx64"\n", g1->le32, g1->le16_1, g1->le16_2, g1->be64); - AUDDBG("GUID 2 = %8x-%hx-%hx-%"PRIx64"\n", g2->le32, g2->le16_1, g2->le16_2, g2->be64); + TAGDBG("GUID 1 = %8x-%hx-%hx-%"PRIx64"\n", g1->le32, g1->le16_1, g1->le16_2, g1->be64); + TAGDBG("GUID 2 = %8x-%hx-%hx-%"PRIx64"\n", g2->le32, g2->le16_1, g2->le16_2, g2->be64); */ g_return_val_if_fail((g1 != NULL) && (g2 != NULL), FALSE); if (!memcmp(g1, g2, 16)) { - // AUDDBG("equal\n"); + // TAGDBG("equal\n"); return TRUE; } - /* AUDDBG("not equal\n"); */ + /* TAGDBG("not equal\n"); */ return FALSE; } -int get_guid_type(GUID * g) +int get_guid_type(GUID_t * g) { - GUID *g1; + GUID_t *g1; int i; for (i = 0; i < ASF_OBJECT_LAST - 1; i++) { diff --git a/src/libaudtag/wma/guid.h b/src/libaudtag/wma/guid.h index 1bb193a..8d68d49 100644 --- a/src/libaudtag/wma/guid.h +++ b/src/libaudtag/wma/guid.h @@ -30,16 +30,16 @@ typedef struct _guid { guint16 le16_1; guint16 le16_2; guint64 be64; -}GUID; +}GUID_t; -GUID *guid_read_from_file(VFSFile *); +GUID_t *guid_read_from_file(VFSFile *); gboolean guid_write_to_file(VFSFile *, int); -GUID *guid_convert_from_string(const gchar *); -gchar *guid_convert_to_string(const GUID*); -gboolean guid_equal(GUID *, GUID *); -int get_guid_type(GUID *); +GUID_t *guid_convert_from_string(const gchar *); +gchar *guid_convert_to_string(const GUID_t*); +gboolean guid_equal(GUID_t *, GUID_t *); +int get_guid_type(GUID_t *); const gchar *wma_guid_map(int); #endif /* _GUID_H */ diff --git a/src/libaudtag/wma/wma.c b/src/libaudtag/wma/wma.c index c307a7c..94e181e 100644 --- a/src/libaudtag/wma/wma.c +++ b/src/libaudtag/wma/wma.c @@ -31,7 +31,7 @@ /* static functions */ static GenericHeader *read_generic_header(VFSFile * f, gboolean read_data) { - AUDDBG("read top-level header object\n"); + TAGDBG("read top-level header object\n"); g_return_val_if_fail((f != NULL), NULL); GenericHeader *header = g_new0(GenericHeader, 1); header->guid = guid_read_from_file(f); @@ -42,7 +42,7 @@ static GenericHeader *read_generic_header(VFSFile * f, gboolean read_data) header->data = NULL; gchar *s = guid_convert_to_string(header->guid); - AUDDBG("read GUID: %s\n", s); + TAGDBG("read GUID: %s\n", s); g_free(s); return header; @@ -50,7 +50,7 @@ static GenericHeader *read_generic_header(VFSFile * f, gboolean read_data) static HeaderObj *read_top_header_object(VFSFile * f) { - AUDDBG("read top-level header object\n"); + TAGDBG("read top-level header object\n"); g_return_val_if_fail((f != NULL), NULL); HeaderObj *header = g_new0(HeaderObj, 1); @@ -59,7 +59,7 @@ static HeaderObj *read_top_header_object(VFSFile * f) header->size = read_LEuint64(f); header->objectsNr = read_LEuint32(f); - AUDDBG("Number of child objects: %d\n", header->objectsNr); + TAGDBG("Number of child objects: %d\n", header->objectsNr); header->res1 = read_uint8(f); header->res2 = read_uint8(f); @@ -102,14 +102,14 @@ static ExtContentDescrObj *read_ext_content_descr_obj(VFSFile * f, Tuple * t, gb static guint find_descriptor_id(gchar * s) { - AUDDBG("finding descriptor id for '%s'\n", s); + TAGDBG("finding descriptor id for '%s'\n", s); g_return_val_if_fail(s != NULL, -1); gchar *l[DESC_LAST] = { DESC_ALBUM_STR, DESC_YEAR_STR, DESC_GENRE_STR, DESC_TRACK_STR }; guint i; for (i = 0; i < DESC_LAST; i++) if (!strcmp(l[i], s)) { - AUDDBG("found descriptor %s\n", s); + TAGDBG("found descriptor %s\n", s); return i; } return -1; @@ -121,20 +121,20 @@ static ContentDescriptor *read_descriptor(VFSFile * f, Tuple * t, gboolean popul gchar *val = NULL, *name = NULL; guint32 intval = -1; gint dtype; - AUDDBG("reading name_len\n"); + TAGDBG("reading name_len\n"); cd->name_len = read_LEuint16(f); - AUDDBG("reading name\n"); + TAGDBG("reading name\n"); cd->name = fread_utf16(f, cd->name_len); - AUDDBG("reading val_type\n"); + TAGDBG("reading val_type\n"); cd->val_type = read_LEuint16(f); - AUDDBG("reading val_len\n"); + TAGDBG("reading val_len\n"); cd->val_len = read_LEuint16(f); name = utf8(cd->name); dtype = find_descriptor_id(name); g_free(name); - AUDDBG("reading val\n"); + TAGDBG("reading val\n"); if (populate_tuple) { @@ -143,7 +143,7 @@ static ContentDescriptor *read_descriptor(VFSFile * f, Tuple * t, gboolean popul { /*UTF16 */ cd->val = read_char_data(f, cd->val_len); val = utf8((gunichar2 *) cd->val); - AUDDBG("val: '%s' dtype: %d\n", val, dtype); + TAGDBG("val: '%s' dtype: %d\n", val, dtype); if (dtype == DESC_ALBUM) tuple_associate_string(t, FIELD_ALBUM, NULL, val); if (dtype == DESC_GENRE) @@ -158,7 +158,7 @@ static ContentDescriptor *read_descriptor(VFSFile * f, Tuple * t, gboolean popul if (cd->val_type == 3) { intval = read_LEuint32(f); - AUDDBG("intval: %d, dtype: %d\n", intval, dtype); + TAGDBG("intval: %d, dtype: %d\n", intval, dtype); if (dtype == DESC_TRACK) tuple_associate_int(t, FIELD_TRACK_NUMBER, NULL, intval); } @@ -168,8 +168,8 @@ static ContentDescriptor *read_descriptor(VFSFile * f, Tuple * t, gboolean popul } else cd->val = read_char_data(f, cd->val_len); - AUDDBG("read str_val: '%s', intval: %d\n", val, intval); - AUDDBG("exiting read_descriptor \n\n"); + TAGDBG("read str_val: '%s', intval: %d\n", val, intval); + TAGDBG("exiting read_descriptor \n\n"); return cd; } @@ -212,9 +212,9 @@ void free_ext_content_descr_obj(ExtContentDescrObj * ecdo) } /* returns the offset of the object in the file */ -static long ftell_object_by_guid(VFSFile * f, GUID * g) +static long ftell_object_by_guid(VFSFile * f, GUID_t * g) { - AUDDBG("seeking object %s, with ID %d \n", guid_convert_to_string(g), get_guid_type(g)); + TAGDBG("seeking object %s, with ID %d \n", guid_convert_to_string(g), get_guid_type(g)); HeaderObj *h = read_top_header_object(f); g_return_val_if_fail((f != NULL) && (g != NULL) && (h != NULL), -1); @@ -222,19 +222,19 @@ static long ftell_object_by_guid(VFSFile * f, GUID * g) while (i < h->objectsNr) { GenericHeader *gen_hdr = read_generic_header(f, FALSE); - AUDDBG("encountered GUID %s, with ID %d\n", guid_convert_to_string(gen_hdr->guid), get_guid_type(gen_hdr->guid)); + TAGDBG("encountered GUID %s, with ID %d\n", guid_convert_to_string(gen_hdr->guid), get_guid_type(gen_hdr->guid)); if (guid_equal(gen_hdr->guid, g)) { g_free(h); g_free(gen_hdr); guint64 ret = vfs_ftell(f) - 24; - AUDDBG("at offset %" PRIx64 "\n", ret); + TAGDBG("at offset %" PRIx64 "\n", ret); return ret; } vfs_fseek(f, gen_hdr->size - 24, SEEK_CUR); //most headers have a size as their second field" i++; } - AUDDBG("The object was not found\n"); + TAGDBG("The object was not found\n"); g_free(h); return -1; @@ -250,7 +250,7 @@ VFSFile *make_temp_file() long ftell_object_by_str(VFSFile * f, gchar * s) { - GUID *g = guid_convert_from_string(s); + GUID_t *g = guid_convert_from_string(s); long res = ftell_object_by_guid(f, g); g_free(g); return res; @@ -299,7 +299,7 @@ static void write_ext_content_descr_obj_from_tuple(VFSFile * f, ExtContentDescrO static gboolean write_generic_header(VFSFile * f, GenericHeader * gh) { - AUDDBG("Writing generic header\n"); + TAGDBG("Writing generic header\n"); guid_write_to_file(f, get_guid_type(gh->guid)); return write_char_data(f, gh->data, gh->size); } @@ -313,7 +313,7 @@ static void free_generic_header(GenericHeader * gh) static gboolean write_top_header_object(VFSFile * f, HeaderObj * header) { - AUDDBG("write header object\n"); + TAGDBG("write header object\n"); vfs_fseek(f, 0, SEEK_SET); return (guid_write_to_file(f, ASF_HEADER_OBJECT) && write_LEuint64(f, header->size) && write_LEuint32(f, header->objectsNr) && write_uint8(f, header->res1) && /* the reserved fields */ write_uint8(f, header->res2)); @@ -322,8 +322,8 @@ static gboolean write_top_header_object(VFSFile * f, HeaderObj * header) /* interface functions */ gboolean wma_can_handle_file(VFSFile * f) { - GUID *guid1 = guid_read_from_file(f); - GUID *guid2 = guid_convert_from_string(ASF_HEADER_OBJECT_GUID); + GUID_t *guid1 = guid_read_from_file(f); + GUID_t *guid2 = guid_convert_from_string(ASF_HEADER_OBJECT_GUID); gboolean equal = ((guid1 != NULL) && guid_equal(guid1, guid2)); g_free(guid1); @@ -366,10 +366,10 @@ gboolean wma_write_tag (Tuple * tuple, VFSFile * f) HeaderObj *top_ho = read_top_header_object(f); VFSFile *tmpfile = make_temp_file(); gint i, cdo_pos, ecdo_pos; - GUID *g; + GUID_t *g; /*read all the headers and write them to the new file */ /*the headers that contain tuple data will be overwritten */ - AUDDBG("Header Object size: %" PRId64 "\n", top_ho->size); + TAGDBG("Header Object size: %" PRId64 "\n", top_ho->size); //vfs_fseek(tmpfile, ) for (i = 0; i < top_ho->objectsNr; i++) { @@ -414,11 +414,11 @@ gboolean wma_write_tag (Tuple * tuple, VFSFile * f) /* if (g_rename(f1, f2) == 0) { - AUDDBG("the tag was updated successfully\n"); + TAGDBG("the tag was updated successfully\n"); } else { - AUDDBG("an error has occured\n"); + TAGDBG("an error has occured\n"); } */ g_free(f1); diff --git a/src/libaudtag/wma/wma.h b/src/libaudtag/wma/wma.h index 756cee2..2743a7b 100644 --- a/src/libaudtag/wma/wma.h +++ b/src/libaudtag/wma/wma.h @@ -26,26 +26,6 @@ #include "libaudcore/vfs.h" #include "wma_fmt.h" -#ifndef TAG_WMA_MODULE_H -/* static functions */ - -/* reads the whole structure, containing the data */ -static GenericHeader *read_generic_header(VFSFile *f, gboolean read_data); - -static HeaderObj *read_top_header_object(VFSFile *f); - -static gboolean write_top_header_object(VFSFile *f, HeaderObj *h); - -static ContentDescriptor **read_descriptors(VFSFile *f, guint count, Tuple*t, gboolean populate_tuple); - -static ContentDescrObj *read_content_descr_obj(VFSFile *f); - -static ExtContentDescrObj *read_ext_content_descr_obj(VFSFile * f, Tuple *t, gboolean populate_tuple); - -static long ftell_object_by_guid(VFSFile *f, GUID *g); -static long ftell_object_by_str(VFSFile *f, gchar *s); -#endif - /* TAG plugin API */ gboolean wma_can_handle_file(VFSFile *f); gboolean wma_read_tag (Tuple * tuple, VFSFile * handle); diff --git a/src/libaudtag/wma/wma_fmt.h b/src/libaudtag/wma/wma_fmt.h index c96517c..17d859f 100644 --- a/src/libaudtag/wma/wma_fmt.h +++ b/src/libaudtag/wma/wma_fmt.h @@ -81,13 +81,13 @@ typedef enum { * but the size is needed so that we can skip it */ typedef struct _generic_header { - GUID *guid; + GUID_t *guid; guint64 size; gchar *data; } GenericHeader; typedef struct _header_object { - GUID *guid; + GUID_t *guid; guint64 size; guint32 objectsNr; guint8 res1; @@ -98,7 +98,7 @@ typedef struct _header_object { * this is special, its size does not include the size of the ext_data */ typedef struct _header_extension_object { - GUID *guid; + GUID_t *guid; guint64 size; guint32 objects_count; guint8 res1; @@ -108,7 +108,7 @@ typedef struct _header_extension_object { } HeaderExtensionObject; typedef struct _file_properties_header { - GUID *guid; + GUID_t *guid; guint64 size; gchar dontcare1[16]; guint64 duration; //expressed as the count of 100ns intervals @@ -116,7 +116,7 @@ typedef struct _file_properties_header { } FilePropertiesHeader; typedef struct _content_description_object { - GUID *guid; + GUID_t *guid; guint64 size; guint16 title_length; guint16 author_length; @@ -148,7 +148,7 @@ typedef struct _content_descriptor { } ContentDescriptor; typedef struct _extended_content_description_object { - GUID *guid; + GUID_t *guid; guint64 size; guint16 content_desc_count; ContentDescriptor **descriptors; diff --git a/src/libeggsmclient/eggdesktopfile.c b/src/libeggsmclient/eggdesktopfile.c index 4d48cda..9b12af5 100644 --- a/src/libeggsmclient/eggdesktopfile.c +++ b/src/libeggsmclient/eggdesktopfile.c @@ -31,7 +31,7 @@ #include <unistd.h> #include <glib/gi18n.h> -#include <gdk/gdk.h> +#include <gdk/gdkx.h> #include <gtk/gtk.h> struct EggDesktopFile { @@ -55,7 +55,6 @@ struct EggDesktopFile { EggDesktopFile * egg_desktop_file_new (const char *desktop_file_path, GError **error) { - EggDesktopFile *desktop_file; GKeyFile *key_file; key_file = g_key_file_new (); @@ -65,13 +64,8 @@ egg_desktop_file_new (const char *desktop_file_path, GError **error) return NULL; } - desktop_file = egg_desktop_file_new_from_key_file (key_file, - desktop_file_path, - error); - if (!desktop_file) - g_key_file_free (key_file); - - return desktop_file; + return egg_desktop_file_new_from_key_file (key_file, desktop_file_path, + error); } /** @@ -105,9 +99,42 @@ egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, full_path, error); g_free (full_path); - if (!desktop_file) - g_key_file_free (key_file); + return desktop_file; +} + +/** + * egg_desktop_file_new_from_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @search_dirs: NULL-terminated array of directories to search + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_dirs (const char *desktop_file_path, + const char **search_dirs, + GError **error) +{ + EggDesktopFile *desktop_file; + GKeyFile *key_file; + char *full_path; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs, + &full_path, 0, error)) + { + g_key_file_free (key_file); + return NULL; + } + desktop_file = egg_desktop_file_new_from_key_file (key_file, + full_path, + error); + g_free (full_path); return desktop_file; } @@ -118,8 +145,8 @@ egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, * @error: error pointer * * Creates a new #EggDesktopFile for @key_file. Assumes ownership of - * @key_file on success (meaning it will be freed when the desktop_file - * is freed). + * @key_file (on success or failure); you should consider @key_file to + * be freed after calling this function. * * Return value: the new #EggDesktopFile, or %NULL on error. **/ @@ -136,6 +163,7 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file, g_set_error (error, EGG_DESKTOP_FILE_ERROR, EGG_DESKTOP_FILE_ERROR_INVALID, _("File is not a valid .desktop file")); + g_key_file_free (key_file); return NULL; } @@ -159,13 +187,14 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file, EGG_DESKTOP_FILE_ERROR_INVALID, _("Unrecognized desktop file Version '%s'"), version); g_free (version); + g_key_file_free (key_file); return NULL; } - else g_free (version); } desktop_file = g_new0 (EggDesktopFile, 1); + desktop_file->key_file = key_file; if (g_path_is_absolute (source)) desktop_file->source = g_filename_to_uri (source, NULL, NULL); @@ -201,7 +230,7 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file, if (!exec) { egg_desktop_file_free (desktop_file); - g_free(type); + g_free (type); return NULL; } @@ -234,7 +263,7 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file, if (!url) { egg_desktop_file_free (desktop_file); - g_free(type); + g_free (type); return NULL; } g_free (url); @@ -244,7 +273,7 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file, else desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED; - g_free(type); + g_free (type); /* Check the Icon key */ desktop_file->icon = g_key_file_get_string (key_file, @@ -269,7 +298,6 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file, } } - desktop_file->key_file = key_file; return desktop_file; } @@ -290,22 +318,6 @@ egg_desktop_file_free (EggDesktopFile *desktop_file) } /** - * egg_desktop_file_get_key_file: - * @desktop_file: an #EggDesktopFile - * - * Gets the #GKeyFile associated with @desktop_file. You must not free - * this value, and changes made to it will not be reflected by - * @desktop_file. - * - * Return value: the #GKeyFile associated with @desktop_file. - **/ -GKeyFile * -egg_desktop_file_get_key_file (EggDesktopFile *desktop_file) -{ - return desktop_file->key_file; -} - -/** * egg_desktop_file_get_source: * @desktop_file: an #EggDesktopFile * @@ -368,6 +380,91 @@ egg_desktop_file_get_icon (EggDesktopFile *desktop_file) return desktop_file->icon; } +gboolean +egg_desktop_file_has_key (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_has_key (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char * +egg_desktop_file_get_string (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char * +egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + GError **error) +{ + return g_key_file_get_locale_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, locale, + error); +} + +gboolean +egg_desktop_file_get_boolean (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_boolean (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +double +egg_desktop_file_get_numeric (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_double (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +int +egg_desktop_file_get_integer (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_integer (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char ** +egg_desktop_file_get_string_list (EggDesktopFile *desktop_file, + const char *key, + gsize *length, + GError **error) +{ + return g_key_file_get_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, length, + error); +} + +char ** +egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + gsize *length, + GError **error) +{ + return g_key_file_get_locale_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + locale, length, + error); +} + /** * egg_desktop_file_can_launch: * @desktop_file: an #EggDesktopFile @@ -822,7 +919,7 @@ parse_link (EggDesktopFile *desktop_file, return TRUE; } -#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE +#if GTK_CHECK_VERSION (2, 12, 0) static char * start_startup_notification (GdkDisplay *display, EggDesktopFile *desktop_file, @@ -900,7 +997,7 @@ end_startup_notification (GdkDisplay *display, NULL); } -#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */ * 1000) +#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */) typedef struct { GdkDisplay *display; @@ -930,24 +1027,32 @@ set_startup_notification_timeout (GdkDisplay *display, sn_data->display = g_object_ref (display); sn_data->startup_id = g_strdup (startup_id); - g_timeout_add (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH, - startup_notification_timeout, sn_data); + g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH, + startup_notification_timeout, sn_data); } -#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */ - -extern char **environ; +#endif /* GTK 2.12 */ static GPtrArray * array_putenv (GPtrArray *env, char *variable) { - int i, keylen; + guint i, keylen; if (!env) { + char **envp; + env = g_ptr_array_new (); - for (i = 0; environ[i]; i++) - g_ptr_array_add (env, g_strdup (environ[i])); + envp = g_listenv (); + for (i = 0; envp[i]; i++) + { + const char *value; + + value = g_getenv (envp[i]); + g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i], + value ? value : "")); + } + g_strfreev (envp); } keylen = strcspn (variable, "="); @@ -977,7 +1082,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file, GError **error) { EggDesktopFileLaunchOption option; - GSList *translated_documents, *docs; + GSList *translated_documents = NULL, *docs = NULL; char *command, **argv; int argc, i, screen_num; gboolean success, current_success; @@ -993,8 +1098,6 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file, GSpawnFlags flags = G_SPAWN_SEARCH_PATH; GSpawnChildSetupFunc setup_func = NULL; gpointer setup_data = NULL; - docs = NULL; - translated_documents = NULL; GPid *ret_pid = NULL; int *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL; @@ -1114,7 +1217,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file, } g_free (command); -#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE +#if GTK_CHECK_VERSION (2, 12, 0) startup_id = start_startup_notification (display, desktop_file, argv[0], screen_num, workspace, launch_time); @@ -1127,7 +1230,10 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file, } #else startup_id = NULL; -#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */ +#endif /* GTK 2.12 */ + + if (env != NULL) + g_ptr_array_add (env, NULL); current_success = g_spawn_async_with_pipes (directory, @@ -1142,7 +1248,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file, if (startup_id) { -#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE +#if GTK_CHECK_VERSION (2, 12, 0) if (current_success) { set_startup_notification_timeout (display, startup_id); @@ -1153,7 +1259,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file, g_free (startup_id); } else -#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */ +#endif /* GTK 2.12 */ g_free (startup_id); } else if (ret_startup_id) @@ -1178,8 +1284,8 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file, out: if (env) { - g_strfreev ((char **)env->pdata); - g_ptr_array_free (env, FALSE); + g_ptr_array_foreach (env, (GFunc)g_free, NULL); + g_ptr_array_free (env, TRUE); } free_document_list (translated_documents); @@ -1290,6 +1396,8 @@ egg_desktop_file_launch (EggDesktopFile *desktop_file, free_document_list (documents); break; + case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED: + case EGG_DESKTOP_FILE_TYPE_DIRECTORY: default: g_set_error (error, EGG_DESKTOP_FILE_ERROR, EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, @@ -1312,6 +1420,40 @@ egg_desktop_file_error_quark (void) G_LOCK_DEFINE_STATIC (egg_desktop_file); static EggDesktopFile *egg_desktop_file; +static void +egg_set_desktop_file_internal (const char *desktop_file_path, + gboolean set_defaults) +{ + GError *error = NULL; + + G_LOCK (egg_desktop_file); + if (egg_desktop_file) + egg_desktop_file_free (egg_desktop_file); + + egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error); + if (error) + { + g_warning ("Could not load desktop file '%s': %s", + desktop_file_path, error->message); + g_error_free (error); + } + + if (set_defaults && egg_desktop_file != NULL) { + /* Set localized application name and default window icon */ + if (egg_desktop_file->name) + g_set_application_name (egg_desktop_file->name); + if (egg_desktop_file->icon) + { + if (g_path_is_absolute (egg_desktop_file->icon)) + gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL); + else + gtk_window_set_default_icon_name (egg_desktop_file->icon); + } + } + + G_UNLOCK (egg_desktop_file); +} + /** * egg_set_desktop_file: * @desktop_file_path: path to the application's desktop file @@ -1323,42 +1465,38 @@ static EggDesktopFile *egg_desktop_file; * gtk_window_set_default_icon_from_file() with the application's * icon. Other code may use additional information from the desktop * file. + * See egg_set_desktop_file_without_defaults() for a variant of this + * function that does not set the application name and default window + * icon. * * Note that for thread safety reasons, this function can only - * be called once. + * be called once, and is mutually exclusive with calling + * egg_set_desktop_file_without_defaults(). **/ void egg_set_desktop_file (const char *desktop_file_path) { - GError *error = NULL; - - G_LOCK (egg_desktop_file); - if (egg_desktop_file) - egg_desktop_file_free (egg_desktop_file); - - egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error); - if (error) - { - g_warning ("Could not load desktop file '%s': %s" - " - Session management may not work now.", - desktop_file_path, error->message); - g_error_free (error); - G_UNLOCK (egg_desktop_file); - return; - } - - /* Set localized application name and default window icon */ - if (egg_desktop_file->name) - g_set_application_name (egg_desktop_file->name); - if (egg_desktop_file->icon) - { - if (g_path_is_absolute (egg_desktop_file->icon)) - gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL); - else - gtk_window_set_default_icon_name (egg_desktop_file->icon); - } + egg_set_desktop_file_internal (desktop_file_path, TRUE); +} - G_UNLOCK (egg_desktop_file); +/** + * egg_set_desktop_file_without_defaults: + * @desktop_file_path: path to the application's desktop file + * + * Creates an #EggDesktopFile for the application from the data at + * @desktop_file_path. + * See egg_set_desktop_file() for a variant of this function that + * sets the application name and default window icon from the information + * in the desktop file. + * + * Note that for thread safety reasons, this function can only + * be called once, and is mutually exclusive with calling + * egg_set_desktop_file(). + **/ +void +egg_set_desktop_file_without_defaults (const char *desktop_file_path) +{ + egg_set_desktop_file_internal (desktop_file_path, FALSE); } /** diff --git a/src/libeggsmclient/eggdesktopfile.h b/src/libeggsmclient/eggdesktopfile.h index f130541..16c5426 100644 --- a/src/libeggsmclient/eggdesktopfile.h +++ b/src/libeggsmclient/eggdesktopfile.h @@ -31,22 +31,23 @@ typedef enum { EGG_DESKTOP_FILE_TYPE_APPLICATION, EGG_DESKTOP_FILE_TYPE_LINK, - EGG_DESKTOP_FILE_TYPE_DIRECTORY, + EGG_DESKTOP_FILE_TYPE_DIRECTORY } EggDesktopFileType; EggDesktopFile *egg_desktop_file_new (const char *desktop_file_path, GError **error); -EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, - GError **error); -EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *desktop, +EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, + GError **error); +EggDesktopFile *egg_desktop_file_new_from_dirs (const char *desktop_file_path, + const char **search_dirs, + GError **error); +EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *key_file, const char *source, GError **error); void egg_desktop_file_free (EggDesktopFile *desktop_file); -GKeyFile *egg_desktop_file_get_key_file (EggDesktopFile *desktop_file); - const char *egg_desktop_file_get_source (EggDesktopFile *desktop_file); EggDesktopFileType egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file); @@ -109,6 +110,37 @@ typedef enum { #define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS "StartupWMClass" #define EGG_DESKTOP_FILE_KEY_URL "URL" +/* Accessors */ +gboolean egg_desktop_file_has_key (EggDesktopFile *desktop_file, + const char *key, + GError **error); +char *egg_desktop_file_get_string (EggDesktopFile *desktop_file, + const char *key, + GError **error) G_GNUC_MALLOC; +char *egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + GError **error) G_GNUC_MALLOC; +gboolean egg_desktop_file_get_boolean (EggDesktopFile *desktop_file, + const char *key, + GError **error); +double egg_desktop_file_get_numeric (EggDesktopFile *desktop_file, + const char *key, + GError **error); +int egg_desktop_file_get_integer (EggDesktopFile *desktop_file, + const char *key, + GError **error); +char **egg_desktop_file_get_string_list (EggDesktopFile *desktop_file, + const char *key, + gsize *length, + GError **error) G_GNUC_MALLOC; +char **egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + gsize *length, + GError **error) G_GNUC_MALLOC; + + /* Errors */ #define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark() @@ -117,12 +149,13 @@ GQuark egg_desktop_file_error_quark (void); typedef enum { EGG_DESKTOP_FILE_ERROR_INVALID, EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, - EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION, + EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION } EggDesktopFileError; /* Global application desktop file */ -void egg_set_desktop_file (const char *desktop_file_path); -EggDesktopFile *egg_get_desktop_file (void); +void egg_set_desktop_file (const char *desktop_file_path); +void egg_set_desktop_file_without_defaults (const char *desktop_file_path); +EggDesktopFile *egg_get_desktop_file (void); G_END_DECLS diff --git a/src/libeggsmclient/eggsmclient-osx.c b/src/libeggsmclient/eggsmclient-osx.c deleted file mode 100644 index 066ccc2..0000000 --- a/src/libeggsmclient/eggsmclient-osx.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2007 Novell, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#define G_LOG_DOMAIN "EggSMClient" - -#include "eggsmclient.h" - -#define EGG_TYPE_SM_CLIENT_OSX (egg_sm_client_osx_get_type ()) -#define EGG_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSX)) -#define EGG_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass)) -#define EGG_IS_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_OSX)) -#define EGG_IS_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_OSX)) -#define EGG_SM_CLIENT_OSX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass)) - -typedef struct _EggSMClientOSX EggSMClientOSX; -typedef struct _EggSMClientOSXClass EggSMClientOSXClass; - -struct _EggSMClientOSX { - EggSMClient parent; - -} - -struct _EggSMClientOSXClass -{ - EggSMClientClass parent_class; - -}; - -static void sm_client_osx_startup (EggSMClient *client, - const char *client_id); -static void sm_client_osx_will_quit (EggSMClient *client, - gboolean will_quit); -static gboolean sm_client_osx_end_session (EggSMClient *client, - EggSMClientEndStyle style, - gboolean request_confirmation); - -static GdkFilterReturn sm_client_osx_filter (GdkXEvent *xevent, - GdkEvent *event, - gpointer data); - -G_DEFINE_TYPE (EggSMClientOSX, egg_sm_client_osx, EGG_TYPE_SM_CLIENT) - -static void -egg_sm_client_osx_init (EggSMClientOSX *osxclient) -{ - ; -} - -static void -egg_sm_client_osx_class_init (EggSMClientOSXClass *klass) -{ - EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); - - sm_client_class->startup = sm_client_osx_startup; - sm_client_class->will_quit = sm_client_osx_will_quit; - sm_client_class->end_session = sm_client_osx_end_session; -} - -EggSMClient * -egg_sm_client_osx_new (void) -{ - return g_object_new (EGG_TYPE_SM_CLIENT_OSX, NULL); -} - -static void -sm_client_osx_startup (EggSMClient *client) -{ - gdk_window_add_filter (NULL, sm_client_osx_filter, client); -} - -void -sm_client_osx_will_quit (EggSMClient *client, - gboolean will_quit) -{ - EggSMClientOSX *osxclient = (EggSMClientOSX *)client; - - if (will_quit) - { - /* OS X doesn't send another message. We're supposed to just - * quit after agreeing that it's OK. - * - * FIXME: do it from an idle handler though. - */ - egg_sm_client_quit (client); - } - else - { - /* FIXME: "respond to the event by returning a userCancelledErr - * error" - */ - } -} - -void -sm_client_osx_end_session (EggSMClient *client, - EggSMClientEndStyle style, - gboolean request_confirmation) -{ - EggSMClientOSX *osxclient = (EggSMClientOSX *)client; - FIXME_t event; - - switch (style) - { - case EGG_SM_CLIENT_END_SESSION_DEFAULT: - case EGG_SM_CLIENT_END_LOGOUT: - event = request_confirmation ? kAELogOut : kAEReallyLogOut; - break; - case EGG_SM_CLIENT_END_REBOOT: - event = request_confirmation ? kAEShowRestartDialog : kAERestart; - break; - case EGG_SM_CLIENT_END_SHUTDOWN: - event = request_confirmation ? kAEShowShutdownDialog : kAEShutDown; - break; - } - - /* FIXME: send event to loginwindow process */ -} - -static GdkFilterReturn -egg_sm_client_osx_filter (GdkXEvent *xevent, - GdkEvent *event, - gpointer data) -{ - EggSMClientOSX *osxclient = data; - EggSMClient *client = data; - NSEvent *nsevent = (NSEvent *)xevent; - - switch (FIXME_get_apple_event_type (nsevent)) - { - case kAEQuitApplication: - if (FIXME_app_is_a_foreground_app) - egg_sm_client_quit_requested (client); - else - egg_sm_client_quit (client); - return GDK_FILTER_REMOVE; - - default: - return GDK_FILTER_CONTINUE; - } -} diff --git a/src/libeggsmclient/eggsmclient-private.h b/src/libeggsmclient/eggsmclient-private.h index ccb10bf..0c98eee 100644 --- a/src/libeggsmclient/eggsmclient-private.h +++ b/src/libeggsmclient/eggsmclient-private.h @@ -20,7 +20,13 @@ #ifndef __EGG_SM_CLIENT_PRIVATE_H__ #define __EGG_SM_CLIENT_PRIVATE_H__ +#include <gtk/gtk.h> + +#if !GTK_CHECK_VERSION(2,91,7) && !GTK_CHECK_VERSION(3,0,0) +/* GTK+ 3 includes this automatically */ #include <gdkconfig.h> +#endif + #include "eggsmclient.h" G_BEGIN_DECLS diff --git a/src/libeggsmclient/eggsmclient-win32.c b/src/libeggsmclient/eggsmclient-win32.c index 91a0f74..49e63ae 100644 --- a/src/libeggsmclient/eggsmclient-win32.c +++ b/src/libeggsmclient/eggsmclient-win32.c @@ -17,6 +17,35 @@ * Boston, MA 02111-1307, USA. */ +/* EggSMClientWin32 + * + * For details on the Windows XP logout process, see: + * http://msdn.microsoft.com/en-us/library/aa376876.aspx. + * + * Vista adds some new APIs which EggSMClient does not make use of; see + * http://msdn.microsoft.com/en-us/library/ms700677(VS.85).aspx + * + * When shutting down, Windows sends every top-level window a + * WM_QUERYENDSESSION event, which the application must respond to + * synchronously, saying whether or not it will quit. To avoid main + * loop re-entrancy problems (and to avoid having to muck about too + * much with the guts of the gdk-win32 main loop), we watch for this + * event in a separate thread, which then signals the main thread and + * waits for the main thread to handle the event. Since we don't want + * to require g_thread_init() to be called, we do this all using + * Windows-specific thread methods. + * + * After the application handles the WM_QUERYENDSESSION event, + * Windows then sends it a WM_ENDSESSION event with a TRUE or FALSE + * parameter indicating whether the session is or is not actually + * going to end now. We handle this from the other thread as well. + * + * As mentioned above, Vista introduces several additional new APIs + * that don't fit into the (current) EggSMClient API. Windows also has + * an entirely separate shutdown-notification scheme for non-GUI apps, + * which we also don't handle here. + */ + #include "config.h" #include "eggsmclient-private.h" @@ -25,6 +54,7 @@ #define WIN32_LEAN_AND_MEAN #define UNICODE #include <windows.h> +#include <process.h> #define EGG_TYPE_SM_CLIENT_WIN32 (egg_sm_client_win32_get_type ()) #define EGG_SM_CLIENT_WIN32(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32)) @@ -39,7 +69,10 @@ typedef struct _EggSMClientWin32Class EggSMClientWin32Class; struct _EggSMClientWin32 { EggSMClient parent; - GAsyncQueue *msg_queue; + HANDLE message_event, response_event; + + volatile GSourceFunc event; + volatile gboolean will_quit; }; struct _EggSMClientWin32Class @@ -56,7 +89,10 @@ static gboolean sm_client_win32_end_session (EggSMClient *client, EggSMClientEndStyle style, gboolean request_confirmation); -static gpointer sm_client_thread (gpointer data); +static GSource *g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, + gpointer user_data); +static gboolean got_message (gpointer user_data); +static void sm_client_thread (gpointer data); G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT) @@ -88,9 +124,10 @@ sm_client_win32_startup (EggSMClient *client, { EggSMClientWin32 *win32 = (EggSMClientWin32 *)client; - /* spawn another thread to listen for logout signals on */ - win32->msg_queue = g_async_queue_new (); - g_thread_create (sm_client_thread, client, FALSE, NULL); + win32->message_event = CreateEvent (NULL, FALSE, FALSE, NULL); + win32->response_event = CreateEvent (NULL, FALSE, FALSE, NULL); + g_win32_handle_source_add (win32->message_event, got_message, win32); + _beginthread (sm_client_thread, 0, client); } static void @@ -99,8 +136,8 @@ sm_client_win32_will_quit (EggSMClient *client, { EggSMClientWin32 *win32 = (EggSMClientWin32 *)client; - /* Can't push NULL onto a GAsyncQueue, so we add 1 to the value... */ - g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (will_quit + 1)); + win32->will_quit = will_quit; + SetEvent (win32->response_event); } static gboolean @@ -143,9 +180,7 @@ sm_client_win32_end_session (EggSMClient *client, static gboolean emit_quit_requested (gpointer smclient) { - gdk_threads_enter (); egg_sm_client_quit_requested (smclient); - gdk_threads_leave (); return FALSE; } @@ -155,11 +190,9 @@ emit_quit (gpointer smclient) { EggSMClientWin32 *win32 = smclient; - gdk_threads_enter (); egg_sm_client_quit (smclient); - gdk_threads_leave (); - g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (1)); + SetEvent (win32->response_event); return FALSE; } @@ -168,29 +201,82 @@ emit_quit_cancelled (gpointer smclient) { EggSMClientWin32 *win32 = smclient; - gdk_threads_enter (); egg_sm_client_quit_cancelled (smclient); - gdk_threads_leave (); - g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (1)); + SetEvent (win32->response_event); return FALSE; } +static gboolean +got_message (gpointer smclient) +{ + EggSMClientWin32 *win32 = smclient; + + win32->event (win32); + return TRUE; +} + +/* Windows HANDLE GSource */ -/* logout-listener thread */ +typedef struct { + GSource source; + GPollFD pollfd; +} GWin32HandleSource; -static int -async_emit (EggSMClientWin32 *win32, GSourceFunc emitter) +static gboolean +g_win32_handle_source_prepare (GSource *source, gint *timeout) +{ + *timeout = -1; + return FALSE; +} + +static gboolean +g_win32_handle_source_check (GSource *source) { - /* ensure message queue is empty */ - while (g_async_queue_try_pop (win32->msg_queue)) - ; + GWin32HandleSource *hsource = (GWin32HandleSource *)source; - /* Emit signal in the main thread and wait for a response */ - g_idle_add (emitter, win32); - return GPOINTER_TO_INT (g_async_queue_pop (win32->msg_queue)) - 1; + return hsource->pollfd.revents; } +static gboolean +g_win32_handle_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ + return (*callback) (user_data); +} + +static void +g_win32_handle_source_finalize (GSource *source) +{ + ; +} + +GSourceFuncs g_win32_handle_source_funcs = { + g_win32_handle_source_prepare, + g_win32_handle_source_check, + g_win32_handle_source_dispatch, + g_win32_handle_source_finalize +}; + +static GSource * +g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, gpointer user_data) +{ + GWin32HandleSource *hsource; + GSource *source; + + source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource)); + hsource = (GWin32HandleSource *)source; + hsource->pollfd.fd = (int)handle; + hsource->pollfd.events = G_IO_IN; + hsource->pollfd.revents = 0; + g_source_add_poll (source, &hsource->pollfd); + + g_source_set_callback (source, callback, user_data, NULL); + g_source_attach (source, NULL); + return source; +} + +/* logout-listener thread */ + LRESULT CALLBACK sm_client_win32_window_procedure (HWND hwnd, UINT message, @@ -203,19 +289,27 @@ sm_client_win32_window_procedure (HWND hwnd, switch (message) { case WM_QUERYENDSESSION: - return async_emit (win32, emit_quit_requested); + win32->event = emit_quit_requested; + SetEvent (win32->message_event); + + WaitForSingleObject (win32->response_event, INFINITE); + return win32->will_quit; case WM_ENDSESSION: if (wParam) { /* The session is ending */ - async_emit (win32, emit_quit); + win32->event = emit_quit; } else { /* Nope, the session *isn't* ending */ - async_emit (win32, emit_quit_cancelled); + win32->event = emit_quit_cancelled; } + + SetEvent (win32->message_event); + WaitForSingleObject (win32->response_event, INFINITE); + return 0; default: @@ -223,11 +317,11 @@ sm_client_win32_window_procedure (HWND hwnd, } } -static gpointer +static void sm_client_thread (gpointer smclient) { HINSTANCE instance; - WNDCLASSEXW wcl; + WNDCLASSEXW wcl; ATOM klass; HWND window; MSG msg; @@ -250,6 +344,4 @@ sm_client_thread (gpointer smclient) /* main loop */ while (GetMessage (&msg, NULL, 0, 0)) DispatchMessage (&msg); - - return NULL; } diff --git a/src/libeggsmclient/eggsmclient-xsmp.c b/src/libeggsmclient/eggsmclient-xsmp.c index 8e13ff4..84a8eb8 100644 --- a/src/libeggsmclient/eggsmclient-xsmp.c +++ b/src/libeggsmclient/eggsmclient-xsmp.c @@ -36,6 +36,7 @@ #include <X11/SM/SMlib.h> #include <gdk/gdk.h> +#include <gdk/gdkx.h> #define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ()) #define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP)) @@ -62,11 +63,10 @@ typedef enum XSMP_STATE_INTERACT, XSMP_STATE_SAVE_YOURSELF_DONE, XSMP_STATE_SHUTDOWN_CANCELLED, - XSMP_STATE_CONNECTION_CLOSED, + XSMP_STATE_CONNECTION_CLOSED } EggSMClientXSMPState; static const char *state_names[] = { - "start", "idle", "save-yourself", "interact-request", @@ -221,19 +221,14 @@ sm_client_xsmp_set_initial_properties (gpointer user_data) desktop_file = egg_get_desktop_file (); if (desktop_file) { - GKeyFile *key_file; GError *err = NULL; char *cmdline, **argv; int argc; - key_file = egg_desktop_file_get_key_file (desktop_file); - if (xsmp->restart_style == SmRestartIfRunning) { - if (g_key_file_has_key (key_file, EGG_DESKTOP_FILE_GROUP, - "X-GNOME-AutoRestart", NULL) && - g_key_file_get_boolean (key_file, EGG_DESKTOP_FILE_GROUP, - "X-GNOME-AutoRestart", NULL)) + if (egg_desktop_file_get_boolean (desktop_file, + "X-GNOME-AutoRestart", NULL)) xsmp->restart_style = SmRestartImmediately; } @@ -252,6 +247,7 @@ sm_client_xsmp_set_initial_properties (gpointer user_data) err->message); g_error_free (err); } + g_free (cmdline); } } @@ -371,9 +367,11 @@ sm_client_xsmp_startup (EggSMClient *client, xsmp->client_id = g_strdup (ret_client_id); free (ret_client_id); - gdk_threads_enter (); +#if !GTK_CHECK_VERSION(2,91,7) && !GTK_CHECK_VERSION(3,0,0) gdk_set_sm_client_id (xsmp->client_id); - gdk_threads_leave (); +#else + gdk_x11_set_sm_client_id (xsmp->client_id); +#endif g_debug ("Got client ID \"%s\"", xsmp->client_id); } @@ -541,8 +539,6 @@ idle_do_pending_events (gpointer data) EggSMClientXSMP *xsmp = data; EggSMClient *client = data; - gdk_threads_enter (); - xsmp->idle = 0; if (xsmp->waiting_to_emit_quit) @@ -566,7 +562,6 @@ idle_do_pending_events (gpointer data) } out: - gdk_threads_leave (); return FALSE; } @@ -771,30 +766,6 @@ do_save_yourself (EggSMClientXSMP *xsmp) } static void -merge_keyfiles (GKeyFile *dest, GKeyFile *source) -{ - int g, k; - char **groups, **keys, *value; - - groups = g_key_file_get_groups (source, NULL); - for (g = 0; groups[g]; g++) - { - keys = g_key_file_get_keys (source, groups[g], NULL, NULL); - for (k = 0; keys[k]; k++) - { - value = g_key_file_get_value (source, groups[g], keys[k], NULL); - if (value) - { - g_key_file_set_value (dest, groups[g], keys[k], value); - g_free (value); - } - } - g_strfreev (keys); - } - g_strfreev (groups); -} - -static void save_state (EggSMClientXSMP *xsmp) { GKeyFile *state_file; @@ -824,30 +795,61 @@ save_state (EggSMClientXSMP *xsmp) if (desktop_file) { GKeyFile *merged_file; - char *exec; - int i; + char *desktop_file_path; merged_file = g_key_file_new (); - merge_keyfiles (merged_file, egg_desktop_file_get_key_file (desktop_file)); - merge_keyfiles (merged_file, state_file); - - g_key_file_free (state_file); - state_file = merged_file; - - /* Update Exec key using "--sm-client-state-file %k" */ - restart = generate_command (xsmp->restart_command, - NULL, "%k"); - for (i = 0; i < restart->len; i++) - restart->pdata[i] = g_shell_quote (restart->pdata[i]); - g_ptr_array_add (restart, NULL); - exec = g_strjoinv (" ", (char **)restart->pdata); - g_strfreev ((char **)restart->pdata); - g_ptr_array_free (restart, FALSE); - - g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, - EGG_DESKTOP_FILE_KEY_EXEC, - exec); - g_free (exec); + desktop_file_path = + g_filename_from_uri (egg_desktop_file_get_source (desktop_file), + NULL, NULL); + if (desktop_file_path && + g_key_file_load_from_file (merged_file, desktop_file_path, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, NULL)) + { + guint g, k, i; + char **groups, **keys, *value, *exec; + + groups = g_key_file_get_groups (state_file, NULL); + for (g = 0; groups[g]; g++) + { + keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL); + for (k = 0; keys[k]; k++) + { + value = g_key_file_get_value (state_file, groups[g], + keys[k], NULL); + if (value) + { + g_key_file_set_value (merged_file, groups[g], + keys[k], value); + g_free (value); + } + } + g_strfreev (keys); + } + g_strfreev (groups); + + g_key_file_free (state_file); + state_file = merged_file; + + /* Update Exec key using "--sm-client-state-file %k" */ + restart = generate_command (xsmp->restart_command, + NULL, "%k"); + for (i = 0; i < restart->len; i++) + restart->pdata[i] = g_shell_quote (restart->pdata[i]); + g_ptr_array_add (restart, NULL); + exec = g_strjoinv (" ", (char **)restart->pdata); + g_strfreev ((char **)restart->pdata); + g_ptr_array_free (restart, FALSE); + + g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + exec); + g_free (exec); + } + else + desktop_file = NULL; + + g_free (desktop_file_path); } /* Now write state_file to disk. (We can't use mktemp(), because @@ -1050,13 +1052,13 @@ generate_command (char **restart_command, const char *client_id, if (client_id) { - g_ptr_array_add (cmd, "--sm-client-id"); + g_ptr_array_add (cmd, (char *)"--sm-client-id"); g_ptr_array_add (cmd, (char *)client_id); } if (state_file) { - g_ptr_array_add (cmd, "--sm-client-state-file"); + g_ptr_array_add (cmd, (char *)"--sm-client-state-file"); g_ptr_array_add (cmd, (char *)state_file); } @@ -1076,7 +1078,7 @@ set_properties (EggSMClientXSMP *xsmp, ...) GPtrArray *props; SmProp *prop; va_list ap; - int i; + guint i; props = g_ptr_array_new (); @@ -1129,7 +1131,7 @@ delete_properties (EggSMClientXSMP *xsmp, ...) * until you're done with the SmProp. */ static SmProp * -array_prop (const char *name, ...) +array_prop (const char *name, ...) { SmProp *prop; SmPropValue pv; @@ -1139,7 +1141,7 @@ array_prop (const char *name, ...) prop = g_new (SmProp, 1); prop->name = (char *)name; - prop->type = SmLISTofARRAY8; + prop->type = (char *)SmLISTofARRAY8; vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); @@ -1169,11 +1171,11 @@ ptrarray_prop (const char *name, GPtrArray *values) SmProp *prop; SmPropValue pv; GArray *vals; - int i; + guint i; prop = g_new (SmProp, 1); prop->name = (char *)name; - prop->type = SmLISTofARRAY8; + prop->type = (char *)SmLISTofARRAY8; vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); @@ -1203,7 +1205,7 @@ string_prop (const char *name, const char *value) prop = g_new (SmProp, 1); prop->name = (char *)name; - prop->type = SmARRAY8; + prop->type = (char *)SmARRAY8; prop->num_vals = 1; prop->vals = g_new (SmPropValue, 1); @@ -1228,7 +1230,7 @@ card8_prop (const char *name, unsigned char value) prop = g_new (SmProp, 1); prop->name = (char *)name; - prop->type = SmCARD8; + prop->type = (char *)SmCARD8; prop->num_vals = 1; prop->vals = g_new (SmPropValue, 2); @@ -1280,9 +1282,7 @@ process_ice_messages (IceConn ice_conn) { IceProcessMessagesStatus status; - gdk_threads_enter (); status = IceProcessMessages (ice_conn, NULL, NULL); - gdk_threads_leave (); switch (status) { @@ -1347,13 +1347,13 @@ ice_error_handler (IceConn ice_conn, IcePointer values) { /* Do nothing */ -} +} static void ice_io_error_handler (IceConn ice_conn) { /* Do nothing */ -} +} static void smc_error_handler (SmcConn smc_conn, diff --git a/src/libeggsmclient/eggsmclient.c b/src/libeggsmclient/eggsmclient.c index d778de5..92be8a7 100644 --- a/src/libeggsmclient/eggsmclient.c +++ b/src/libeggsmclient/eggsmclient.c @@ -17,6 +17,8 @@ * Boston, MA 02111-1307, USA. */ +#include "config.h" + #include <string.h> #include <glib/gi18n.h> @@ -36,7 +38,7 @@ enum { LAST_SIGNAL }; -static guint signals[LAST_SIGNAL] = { 0 }; +static guint signals[LAST_SIGNAL]; struct _EggSMClientPrivate { GKeyFile *state_file; @@ -176,19 +178,7 @@ egg_sm_client_class_init (EggSMClientClass *klass) static gboolean sm_client_disable = FALSE; static char *sm_client_state_file = NULL; static char *sm_client_id = NULL; - -static GOptionEntry entries[] = { - { "sm-client-disable", 0, 0, - G_OPTION_ARG_NONE, &sm_client_disable, - N_("Disable connection to session manager"), NULL }, - { "sm-client-state-file", 0, 0, - G_OPTION_ARG_STRING, &sm_client_state_file, - N_("Specify file containing saved configuration"), N_("FILE") }, - { "sm-client-id", 0, 0, - G_OPTION_ARG_STRING, &sm_client_id, - N_("Specify session management ID"), N_("ID") }, - { NULL } -}; +static char *sm_config_prefix = NULL; static gboolean sm_client_post_parse_func (GOptionContext *context, @@ -198,7 +188,22 @@ sm_client_post_parse_func (GOptionContext *context, { EggSMClient *client = egg_sm_client_get (); - if (EGG_SM_CLIENT_GET_CLASS (client)->startup) + if (sm_client_id == NULL) + { + const gchar *desktop_autostart_id; + + desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + + if (desktop_autostart_id != NULL) + sm_client_id = g_strdup (desktop_autostart_id); + } + + /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to + * use the same client id. */ + g_unsetenv ("DESKTOP_AUTOSTART_ID"); + + if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED && + EGG_SM_CLIENT_GET_CLASS (client)->startup) EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id); return TRUE; } @@ -215,6 +220,29 @@ sm_client_post_parse_func (GOptionContext *context, GOptionGroup * egg_sm_client_get_option_group (void) { + const GOptionEntry entries[] = { + { "sm-client-disable", 0, 0, + G_OPTION_ARG_NONE, &sm_client_disable, + N_("Disable connection to session manager"), NULL }, + { "sm-client-state-file", 0, 0, + G_OPTION_ARG_FILENAME, &sm_client_state_file, + N_("Specify file containing saved configuration"), N_("FILE") }, + { "sm-client-id", 0, 0, + G_OPTION_ARG_STRING, &sm_client_id, + N_("Specify session management ID"), N_("ID") }, + /* GnomeClient compatibility option */ + { "sm-disable", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, &sm_client_disable, + NULL, NULL }, + /* GnomeClient compatibility option. This is a dummy option that only + * exists so that sessions saved by apps with GnomeClient can be restored + * later when they've switched to EggSMClient. See bug #575308. + */ + { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, &sm_config_prefix, + NULL, NULL }, + { NULL } + }; GOptionGroup *group; /* Use our own debug handler for the "EggSMClient" domain. */ @@ -222,8 +250,8 @@ egg_sm_client_get_option_group (void) egg_sm_client_debug_handler, NULL); group = g_option_group_new ("sm-client", - _("Session Management Options"), - _("Show Session Management options"), + _("Session management options:"), + _("Show session management options"), NULL, NULL); g_option_group_add_entries (group, entries); g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func); @@ -238,9 +266,9 @@ egg_sm_client_get_option_group (void) * Sets the "mode" of #EggSMClient as follows: * * %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely - * disabled. The application will not even connect to the session - * manager. (egg_sm_client_get() will still return an #EggSMClient, - * but it will just be a dummy object.) + * disabled, until the mode is changed again. The application will + * not even connect to the session manager. (egg_sm_client_get() + * will still return an #EggSMClient object.) * * %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to * the session manager (and thus will receive notification when the @@ -250,12 +278,27 @@ egg_sm_client_get_option_group (void) * %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will * function normally. * - * This must be called before the application's main loop begins. + * This must be called before the application's main loop begins and + * before any call to egg_sm_client_get(), unless the mode was set + * earlier to %EGG_SM_CLIENT_MODE_DISABLED and this call enables + * session management. Note that option parsing will call + * egg_sm_client_get(). **/ void egg_sm_client_set_mode (EggSMClientMode mode) { + EggSMClientMode old_mode = global_client_mode; + + g_return_if_fail (global_client == NULL || global_client_mode == EGG_SM_CLIENT_MODE_DISABLED); + g_return_if_fail (!(global_client != NULL && mode == EGG_SM_CLIENT_MODE_DISABLED)); + global_client_mode = mode; + + if (global_client != NULL && old_mode == EGG_SM_CLIENT_MODE_DISABLED) + { + if (EGG_SM_CLIENT_GET_CLASS (global_client)->startup) + EGG_SM_CLIENT_GET_CLASS (global_client)->startup (global_client, sm_client_id); + } } /** @@ -290,24 +333,23 @@ egg_sm_client_get (void) { if (!global_client) { - if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED && - !sm_client_disable) + if (!sm_client_disable) { #if defined (GDK_WINDOWING_WIN32) global_client = egg_sm_client_win32_new (); #elif defined (GDK_WINDOWING_QUARTZ) global_client = egg_sm_client_osx_new (); #else - /* If both D-Bus and XSMP are compiled in, try D-Bus first - * and fall back to XSMP if D-Bus session management isn't - * available. + /* If both D-Bus and XSMP are compiled in, try XSMP first + * (since it supports state saving) and fall back to D-Bus + * if XSMP isn't available. */ -# ifdef EGG_SM_CLIENT_BACKEND_DBUS - global_client = egg_sm_client_dbus_new (); -# endif # ifdef EGG_SM_CLIENT_BACKEND_XSMP + global_client = egg_sm_client_xsmp_new (); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS if (!global_client) - global_client = egg_sm_client_xsmp_new (); + global_client = egg_sm_client_dbus_new (); # endif #endif } |