pidgin/purple-plugin-pack

Merged in default (pull request #19)

2020-03-05, Gary Kramlich
e5b792c00e1d
Merged in default (pull request #19)

Remove defunct plugins

Approved-by: Elliott Sales de Andrade
  • +0 -11
    autoprofile/README.md
  • +0 -145
    autoprofile/autoaway.c
  • +0 -867
    autoprofile/autoprofile.c
  • +0 -110
    autoprofile/autoprofile.h
  • +0 -324
    autoprofile/autoreply.c
  • +0 -438
    autoprofile/comp_countdownup.c
  • +0 -169
    autoprofile/comp_executable.c
  • +0 -204
    autoprofile/comp_http.c
  • +0 -1042
    autoprofile/comp_logstats.c
  • +0 -28
    autoprofile/comp_logstats.h
  • +0 -355
    autoprofile/comp_logstats_gtk.c
  • +0 -602
    autoprofile/comp_quotation.c
  • +0 -477
    autoprofile/comp_rss.c
  • +0 -52
    autoprofile/comp_rss.h
  • +0 -352
    autoprofile/comp_rss_parser.c
  • +0 -118
    autoprofile/comp_rss_xanga.c
  • +0 -268
    autoprofile/comp_textfile.c
  • +0 -142
    autoprofile/comp_timestamp.c
  • +0 -100
    autoprofile/comp_uptime.c
  • +0 -89
    autoprofile/component.c
  • +0 -77
    autoprofile/component.h
  • +0 -341
    autoprofile/gtk_actions.c
  • +0 -487
    autoprofile/gtk_away_msgs.c
  • +0 -778
    autoprofile/gtk_widget.c
  • +0 -38
    autoprofile/meson.build
  • +0 -750
    autoprofile/preferences.c
  • +0 -49
    autoprofile/sizes.h
  • +0 -221
    autoprofile/utility.c
  • +0 -41
    autoprofile/utility.h
  • +0 -191
    autoprofile/utility_rfc822.c
  • +0 -607
    autoprofile/widget.c
  • +0 -96
    autoprofile/widget.h
  • +0 -9
    awaynotify/README.md
  • +0 -268
    awaynotify/awaynotify.c
  • +0 -9
    awaynotify/meson.build
  • +0 -11
    bit/README.md
  • +0 -240
    bit/bit.c
  • +0 -9
    bit/meson.build
  • +0 -11
    buddytime/README.md
  • +0 -412
    buddytime/buddyedit.c
  • +0 -490
    buddytime/buddytime.c
  • +0 -260
    buddytime/buddytime.c.old
  • +0 -52
    buddytime/buddytime.h
  • +0 -150
    buddytime/gtkbuddytime.c
  • +0 -229
    buddytime/gtktimezone.c
  • +0 -335
    buddytime/gtktimezonetest.c
  • +0 -1250
    buddytime/localtime.c
  • +0 -16
    buddytime/localtime.h
  • +0 -30
    buddytime/meson.build
  • +0 -316
    buddytime/private.h
  • +0 -141
    buddytime/recurse.c
  • +0 -9
    buddytime/recurse.h
  • +0 -25
    buddytime/recursetest.c
  • +0 -28
    buddytime/timetest.c
  • +0 -175
    buddytime/tzfile.h
  • +0 -9
    chronic/README.md
  • +0 -115
    chronic/chronic.c
  • +0 -9
    chronic/meson.build
  • +0 -8
    findip/README.md
  • +0 -182
    findip/findip.c
  • +0 -9
    findip/meson.build
  • +0 -9
    hideconv/README.md
  • +0 -291
    hideconv/hideconv.c
  • +0 -9
    hideconv/meson.build
  • +0 -10
    ignorance/README.md
  • +0 -701
    ignorance/callbacks.c
  • +0 -60
    ignorance/callbacks.h
  • +0 -1209
    ignorance/ignorance.c
  • +0 -20
    ignorance/ignorance.conf
  • +0 -45
    ignorance/ignorance.h
  • +0 -62
    ignorance/ignorance_denizen.c
  • +0 -42
    ignorance/ignorance_denizen.h
  • +0 -72
    ignorance/ignorance_internal.h
  • +0 -328
    ignorance/ignorance_level.c
  • +0 -99
    ignorance/ignorance_level.h
  • +0 -243
    ignorance/ignorance_rule.c
  • +0 -117
    ignorance/ignorance_rule.h
  • +0 -45
    ignorance/ignorance_violation.c
  • +0 -32
    ignorance/ignorance_violation.h
  • +0 -284
    ignorance/interface.c
  • +0 -48
    ignorance/interface.h
  • +0 -26
    ignorance/meson.build
  • +0 -4952
    ignorance/regex.c
  • +0 -490
    ignorance/regex.h
  • +0 -158
    ignorance/support.c
  • +0 -58
    ignorance/support.h
  • +0 -9
    infopane/README.md
  • +0 -261
    infopane/infopane.c
  • +0 -9
    infopane/meson.build
  • +0 -16
    meson.build
  • +0 -9
    msglen/README.md
  • +0 -9
    msglen/meson.build
  • +0 -258
    msglen/msglen.c
  • +0 -0
    napster/16/napster.png
  • +0 -0
    napster/22/napster.png
  • +0 -0
    napster/48/napster.png
  • +0 -9
    napster/README.md
  • +0 -16
    napster/meson.build
  • +0 -765
    napster/napster.c
  • +0 -9
    nomobility/README.md
  • +0 -9
    nomobility/meson.build
  • +0 -314
    nomobility/nomobility.c
  • +0 -10
    smartear/README.md
  • +0 -123
    smartear/gtksmartear.c
  • +0 -566
    smartear/interface.c
  • +0 -29
    smartear/meson.build
  • +0 -266
    smartear/smartear.c
  • +0 -10
    stocker/README.md
  • +0 -513
    stocker/gtkticker.c
  • +0 -99
    stocker/gtkticker.h
  • +0 -11
    stocker/meson.build
  • +0 -412
    stocker/stocker.c
  • +0 -323
    stocker/stocker_prefs.c
  • +0 -39
    stocker/stocker_prefs.h
  • +0 -9
    stress/README.md
  • +0 -9
    stress/meson.build
  • +0 -494
    stress/stress.c
  • +0 -9
    talkfilters/README.md
  • +0 -11
    talkfilters/meson.build
  • +0 -416
    talkfilters/talkfilters.c
  • --- a/autoprofile/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -# AutoProfile
    -
    -status: incomplete
    -dependencies: pidgin
    -authors: Casey Ho
    -introduced: 2.4.0
    -
    -Allows user to place dynamic text into profiles and status messages, with the text automatically updated whenever content changes
    -
    -status: incomplete
    -
    --- a/autoprofile/autoaway.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,145 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "idle.h"
    -#include "conversation.h"
    -
    -#define AP_IDLE_CHECK_INTERVAL 5
    -
    -static guint check_timeout = 0;
    -static guint pref_cb = 0;
    -static time_t last_active_time = 0;
    -
    -static gboolean is_idle ()
    -{
    - PurpleIdleUiOps *ui_ops;
    - time_t time_idle;
    - const gchar *idle_reporting;
    -
    - ui_ops = purple_idle_get_ui_ops ();
    -
    - idle_reporting = purple_prefs_get_string ("/purple/away/idle_reporting");
    - if (!strcmp (idle_reporting, "system") &&
    - (ui_ops != NULL) && (ui_ops->get_time_idle != NULL)) {
    - time_idle = time (NULL) - last_active_time;
    - } else if (!strcmp (idle_reporting, "gaim")) {
    - time_idle = time (NULL) - last_active_time;
    - } else {
    - time_idle = 0;
    - }
    -
    - return (time_idle >
    - (60 * purple_prefs_get_int("/purple/away/mins_before_away")));
    -}
    -
    -static gboolean ap_check_idleness (gpointer data)
    -{
    - gboolean auto_away;
    -
    - // ap auto idle
    - // 0 0 0 don't do anything
    - // 0 0 1 ap_use_idleaway ()
    - // 1 0 x don't do anything, we're already away
    - // 1 1 0 ap_dont_use_idleaway ()
    - // 1 1 1 don't do anything
    -
    - if (ap_is_currently_away () && !ap_autoaway_in_use ()) return TRUE;
    - auto_away = purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/away_when_idle");
    -
    - if (is_idle ()) {
    - if (auto_away && !ap_is_currently_away () && !ap_autoaway_in_use ()) {
    - ap_autoaway_enable ();
    - }
    - } else {
    - if (ap_is_currently_away () && ap_autoaway_in_use ()) {
    - ap_autoaway_disable ();
    - }
    - }
    -
    - return TRUE;
    -}
    -
    -void ap_autoaway_touch ()
    -{
    - time (&last_active_time);
    -}
    -
    -
    -static gboolean writing_im_msg_cb (PurpleAccount *account, const char *who,
    - char **message, PurpleConversation *conv, PurpleMessageFlags flags)
    -{
    - ap_autoaway_touch ();
    - ap_check_idleness (NULL);
    - return FALSE;
    -}
    -
    -static void auto_pref_cb (
    - const char *name, PurplePrefType type, gconstpointer val, gpointer data)
    -{
    - if (!purple_prefs_get_bool ("/purple/away/away_when_idle")) return;
    -
    - purple_notify_error (NULL, NULL,
    - N_("This preference is disabled"),
    - N_("This preference currently has no effect because AutoProfile is in "
    - "use. To modify this behavior, use the AutoProfile configuration "
    - "menu."));
    -
    - purple_prefs_set_bool ("/purple/away/away_when_idle", FALSE);
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Global functions to start it all *
    - *--------------------------------------------------------------------------*/
    -void ap_autoaway_start ()
    -{
    - purple_prefs_set_bool ("/purple/away/away_when_idle", FALSE);
    -
    - check_timeout = purple_timeout_add (AP_IDLE_CHECK_INTERVAL * 1000,
    - ap_check_idleness, NULL);
    -
    - purple_signal_connect (purple_conversations_get_handle (), "writing-im-msg",
    - ap_get_plugin_handle (), PURPLE_CALLBACK(writing_im_msg_cb), NULL);
    -
    - pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (),
    - "/purple/away/away_when_idle", auto_pref_cb, NULL);
    -
    - ap_autoaway_touch ();
    -}
    -
    -void ap_autoaway_finish ()
    -{
    - // Assumes signals are disconnected globally
    -
    - purple_prefs_disconnect_callback (pref_cb);
    - pref_cb = 0;
    -
    - if (check_timeout > 0) purple_timeout_remove (check_timeout);
    - check_timeout = 0;
    -
    - purple_prefs_set_bool ("/purple/away/away_when_idle",
    - purple_prefs_get_bool ("/plugins/gtk/autoprofile/away_when_idle"));
    -}
    -
    -
    --- a/autoprofile/autoprofile.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,867 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -
    -#include "version.h"
    -#include "savedstatuses.h"
    -
    -/* General functions */
    -static void ap_status_changed (
    - const char *, PurplePrefType, gconstpointer, gpointer);
    -static void ap_account_connected (PurpleConnection *);
    -
    -static void ap_delete_legacy_prefs ();
    -
    -static void ap_update_queue_start ();
    -static void ap_update_queue_finish ();
    -
    -/*--------------------------------------------------------------------------
    - * GENERAL VARIABLES
    - *------------------------------------------------------------------------*/
    -
    -static PurplePlugin *plugin_handle = NULL;
    -static PurpleSavedStatus *current_ap_status = NULL;
    -
    -static GStaticMutex update_timeout_mutex = G_STATIC_MUTEX_INIT;
    -static GHashTable *update_timeouts = NULL;
    -
    -static gboolean using_idleaway = FALSE;
    -
    -static GStaticMutex update_queue_mutex = G_STATIC_MUTEX_INIT;
    -static GList *queued_profiles = NULL;
    -static guint update_queue_timeout = 0;
    -
    -/* Functions related to general variables */
    -PurplePlugin *ap_get_plugin_handle () { return plugin_handle; }
    -
    -gboolean ap_is_currently_away () {
    - return current_ap_status != NULL &&
    - purple_savedstatus_get_type (current_ap_status) == PURPLE_STATUS_AWAY;
    -}
    -
    -/*--------------------------------------------------------------------------
    - * REQUIRED GAIM FUNCTIONS- INFO, INITIALIZATION, UNLOADING
    - *------------------------------------------------------------------------*/
    -/* What to do when plugin is loaded */
    -static gboolean plugin_load (PurplePlugin *plugin)
    -{
    - GList *accounts_pref;
    -
    - ap_debug ("general", "AutoProfile is being loaded");
    -
    - plugin_handle = plugin;
    - current_ap_status = purple_savedstatus_new (NULL, PURPLE_STATUS_UNSET);
    - update_timeouts = g_hash_table_new (NULL, NULL);
    -
    - ap_delete_legacy_prefs ();
    -
    - /* The core autoprofile tracking system */
    - purple_prefs_connect_callback (plugin_handle, "/purple/savedstatus/current",
    - ap_status_changed, NULL);
    - purple_signal_connect (purple_connections_get_handle (),
    - "signed-on", plugin_handle,
    - PURPLE_CALLBACK (ap_account_connected), NULL);
    -
    - ap_component_start ();
    - ap_gtk_start ();
    -
    - accounts_pref = purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/profile_accounts");
    - ap_gtk_set_progress_visible (AP_UPDATE_PROFILE, (accounts_pref != NULL));
    - free_string_list (accounts_pref);
    -
    - ap_update_after_delay (AP_UPDATE_STATUS);
    - ap_update_after_delay (AP_UPDATE_PROFILE);
    -
    - ap_autoaway_start ();
    - ap_autoreply_start ();
    -
    - ap_update_queue_start ();
    -
    - return TRUE;
    -}
    -
    -/* What to do when plugin is unloaded */
    -static gboolean plugin_unload (PurplePlugin *plugin)
    -{
    - ap_update_queue_finish ();
    -
    - ap_autoreply_finish ();
    - ap_autoaway_finish ();
    -
    - /* General vars */
    - using_idleaway = FALSE;
    -
    - ap_update_stop (AP_UPDATE_STATUS);
    - ap_update_stop (AP_UPDATE_PROFILE);
    -
    - /* Disconnect tracking system */
    - purple_signals_disconnect_by_handle (plugin);
    -
    - ap_actions_finish ();
    - ap_gtk_finish ();
    - ap_component_finish ();
    -
    - g_hash_table_destroy (update_timeouts);
    - return TRUE;
    -}
    -
    -/* General information */
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD, /* type */
    - PIDGIN_PLUGIN_TYPE, /* ui_requirement */
    - 0, /* flags */
    - NULL, /* dependencies */
    - PURPLE_PRIORITY_DEFAULT, /* priority */
    -
    - "gtk-kluge-autoprofile", /* id */
    - NULL, /* name */
    - PP_VERSION, /* version */
    - NULL, /* summary */
    - NULL, /* description */
    - NULL, /* author */
    - PP_WEBSITE, /* homepage */
    - plugin_load, /* load */
    - plugin_unload, /* unload */
    - NULL, /* destroy */
    -
    - &ui_info, /* ui_info */
    - NULL, /* extra_info */
    - NULL, /* prefs_info */
    - actions, /* actions */
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -/*--------------------------------------------------------------------------
    - * CORE FUNCTIONS
    - *------------------------------------------------------------------------*/
    -
    -static gint get_max_size_status (
    - const PurpleAccount *account, const PurpleStatusPrimitive type) {
    - const char *id;
    -
    - if (account == NULL) {
    - switch (type) {
    - case PURPLE_STATUS_AVAILABLE: return AP_SIZE_AVAILABLE_MAX;
    - case PURPLE_STATUS_AWAY: return AP_SIZE_AWAY_MAX;
    - default: return AP_SIZE_MAXIMUM;
    - }
    - } else {
    - id = purple_account_get_protocol_id (account);
    -
    - switch (type) {
    - case PURPLE_STATUS_AVAILABLE:
    - if (!strcmp (id, "prpl-oscar")) return AP_SIZE_AVAILABLE_AIM;
    - else if (!strcmp (id, "prpl-msn")) return AP_SIZE_AVAILABLE_MSN;
    - else if (!strcmp (id, "prpl-yahoo")) return AP_SIZE_AVAILABLE_YAHOO;
    - else return AP_SIZE_AVAILABLE_MAX;
    - case PURPLE_STATUS_AWAY:
    - if (!strcmp (id, "prpl-oscar")) return AP_SIZE_AWAY_AIM;
    - else return AP_SIZE_AWAY_MAX;
    - default:
    - return AP_SIZE_MAXIMUM;
    - }
    - }
    -}
    -
    -static const char *ap_savedstatus_get_message (
    - const PurpleSavedStatus *status, const PurpleAccount *account)
    -{
    - const PurpleSavedStatusSub *substatus;
    -
    - substatus = purple_savedstatus_get_substatus(status, account);
    - if (substatus != NULL) {
    - return purple_savedstatus_substatus_get_message (substatus);
    - }
    - return purple_savedstatus_get_message (status);
    -}
    -
    -static PurpleStatusPrimitive ap_savedstatus_get_type (
    - const PurpleSavedStatus *status, const PurpleAccount *account)
    -{
    - const PurpleSavedStatusSub *substatus;
    -
    - substatus = purple_savedstatus_get_substatus(status, account);
    - if (substatus != NULL) {
    - return purple_status_type_get_primitive (
    - purple_savedstatus_substatus_get_type (substatus));
    - }
    - return purple_savedstatus_get_type (status);
    -}
    -
    -gchar *ap_get_sample_status_message (PurpleAccount *account)
    -{
    - const PurpleSavedStatus *s;
    - const gchar *message;
    - PurpleStatusPrimitive type;
    -
    - s = (using_idleaway? purple_savedstatus_get_idleaway () :
    - purple_savedstatus_get_current ());
    - message = ap_savedstatus_get_message (s, account);
    - type = ap_savedstatus_get_type (s, account);
    -
    - if (!message) return NULL;
    - return ap_generate (message, get_max_size_status (account, type));
    -}
    -
    -/* Generator helper */
    -static gchar *ap_process_replacement (const gchar *f) {
    - GString *s;
    - struct widget *w;
    - gchar *result;
    -
    - w = ap_widget_find (f);
    - if (w) {
    - result = w->component->generate (w);
    - return result;
    - } else {
    - s = g_string_new ("");
    - g_string_printf (s, "[%s]", f);
    - result = s->str;
    - g_string_free (s, FALSE);
    - return result;
    - }
    -}
    -
    -/* The workhorse generation function! */
    -gchar *ap_generate (const gchar *f, gint max_length) {
    - GString *output;
    - gchar *result;
    - gchar *format, *format_start, *percent_start;
    - gchar *replacement;
    - int state;
    -
    - output = g_string_new ("");
    - format_start = format = purple_utf8_salvage (f);
    -
    - /* When a % has been read (and searching for next %), state is 1
    - * otherwise it is 0
    - */
    - state = 0;
    - percent_start = NULL;
    -
    - while (*format) {
    - if (state == 1) {
    - if (*format == '[') {
    - g_string_append_unichar (output, g_utf8_get_char ("["));
    - *format = '\0';
    - g_string_append (output, percent_start);
    - percent_start = format = format+1;
    - } else if (*format == ']') {
    - *format = '\0';
    - format++;
    - state = 0;
    - replacement = ap_process_replacement (percent_start);
    - percent_start = NULL;
    - g_string_append (output, replacement);
    - free (replacement);
    - } else {
    - format = g_utf8_next_char (format);
    - }
    - } else {
    - if (*format == '\n') {
    - g_string_append (output, "<br>");
    - } else if (*format == '[') {
    - state = 1;
    - percent_start = format+1;
    - } else {
    - g_string_append_unichar (output, g_utf8_get_char (format));
    - }
    - format = g_utf8_next_char (format);
    - }
    - }
    -
    - /* Deal with case where final ] not found */
    - if (state == 1) {
    - g_string_append_unichar (output, g_utf8_get_char ("["));
    - g_string_append (output, percent_start);
    - }
    -
    - /* Set size limit */
    - g_string_truncate (output, max_length);
    -
    - /* Finish up */
    - free (format_start);
    - result = purple_utf8_salvage(output->str);
    - g_string_free (output, TRUE);
    -
    - return result;
    -}
    -
    -void ap_account_enable_profile (const PurpleAccount *account, gboolean enable) {
    - GList *original, *new;
    - gboolean original_status;
    -
    - gchar *username, *protocol_id;
    -
    - GList *node, *tmp;
    - GList *ret = NULL;
    -
    - original_status = ap_account_has_profile_enabled (account);
    - if (original_status == enable) {
    - ap_debug_warn ("profile", "New status identical to original, skipping");
    - return;
    - }
    -
    - original = purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/profile_accounts");
    - username = strdup (purple_account_get_username (account));
    - protocol_id = strdup (purple_account_get_protocol_id (account));
    -
    - if (!enable) {
    - /* Remove from the list */
    - ap_debug ("profile", "Disabling profile updates for account");
    -
    - while (original) {
    - if (!strcmp (original->data, username) &&
    - !strcmp (original->next->data, protocol_id)) {
    - node = original;
    - tmp = node->next;
    - original = original->next->next;
    - free (node->data);
    - free (tmp->data);
    - g_list_free_1 (node);
    - g_list_free_1 (tmp);
    - free (username);
    - free (protocol_id);
    - } else {
    - node = original;
    - original = original->next->next;
    - node->next->next = ret;
    - ret = node;
    - }
    - }
    -
    - new = ret;
    - } else {
    - /* Otherwise add on */
    - GList *ret_start, *ret_end;
    -
    - ap_debug ("profile", "enabling profile updates for account");
    -
    - ret_start = (GList *) malloc (sizeof (GList));
    - ret_end = (GList *) malloc (sizeof (GList));
    - ret_start->data = username;
    - ret_start->next = ret_end;
    - ret_end->data = protocol_id;
    - ret_end->next = original;
    -
    - new = ret_start;
    - }
    -
    - purple_prefs_set_string_list (
    - "/plugins/gtk/autoprofile/profile_accounts", new);
    - ap_gtk_set_progress_visible (AP_UPDATE_PROFILE, (new != NULL));
    -
    - free_string_list (new);
    -}
    -
    -gboolean ap_account_has_profile_enabled (const PurpleAccount *account) {
    - GList *accounts_list, *start_list;
    -
    - accounts_list = purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/profile_accounts");
    -
    - start_list = accounts_list;
    -
    - /* Search through list of values */
    - while (accounts_list) {
    - // Make sure these things come in pairs
    - if (accounts_list->next == NULL) {
    - ap_debug_error ("is_account_profile_enabled", "invalid account string");
    - free_string_list (start_list);
    - return FALSE;
    - }
    -
    - // Check usernames
    - if (!strcmp ((char *) accounts_list->data, account->username)) {
    - // Check protocol
    - if (!strcmp ((char *) accounts_list->next->data, account->protocol_id))
    - {
    - free_string_list (start_list);
    - return TRUE;
    - }
    - }
    -
    - accounts_list = accounts_list->next->next;
    - }
    -
    - /* Not found, hence it wasn't enabled */
    - free_string_list (start_list);
    - return FALSE;
    -}
    -
    -/* Profiles: Update every so often */
    -static gboolean ap_update_profile () {
    - PurpleAccount *account;
    - const GList *purple_accounts;
    - gboolean account_updated;
    -
    - const char *format;
    - const char *old_info;
    - char *generated_profile;
    -
    - /* Generate the profile text */
    - format = purple_prefs_get_string ("/plugins/gtk/autoprofile/profile");
    -
    - if (format == NULL) {
    - ap_debug_error ("general", "profile is null");
    - return FALSE;
    - }
    -
    - generated_profile = ap_generate (format, AP_SIZE_PROFILE_MAX);
    -
    - // If string is blank, nothing would happen
    - if (*generated_profile == '\0') {
    - free (generated_profile);
    - ap_debug_misc ("general", "empty profile set");
    - generated_profile = strdup (" ");
    - }
    -
    - /* Get all accounts and search through each */
    - account_updated = FALSE;
    - for (purple_accounts = purple_accounts_get_all ();
    - purple_accounts != NULL;
    - purple_accounts = purple_accounts->next) {
    - account = (PurpleAccount *)purple_accounts->data;
    - old_info = purple_account_get_user_info (account);
    -
    - /* Check to see if update option set on account */
    - if (ap_account_has_profile_enabled (account) &&
    - (old_info == NULL || strcmp (old_info, generated_profile))) {
    - purple_account_set_user_info (account, generated_profile);
    - account_updated = TRUE;
    -
    - if (purple_account_is_connected (account)) {
    - g_static_mutex_lock (&update_queue_mutex);
    - if (g_list_find (queued_profiles, account) == NULL) {
    - queued_profiles = g_list_append (queued_profiles, account);
    - }
    - g_static_mutex_unlock (&update_queue_mutex);
    - } else {
    - ap_debug_misc ("general", "account not online, not setting profile");
    - }
    - }
    - }
    -
    - if (account_updated) {
    - ap_gtk_add_message (AP_UPDATE_PROFILE, AP_MESSAGE_TYPE_PROFILE,
    - generated_profile);
    - }
    -
    - free (generated_profile);
    - return account_updated;
    -}
    -
    -static gboolean ap_update_status ()
    -{
    - const PurpleSavedStatus *template_status;
    - GHashTable *substatus_messages;
    - gchar *new_message, *new_substatus_message;
    - const gchar *sample_message, *old_message;
    - const GList *accounts;
    - gboolean updated;
    - PurpleStatusPrimitive old_type, new_type;
    - const PurpleStatusType *substatus_type;
    - PurpleAccount *account;
    - PurpleSavedStatusSub *substatus;
    -
    - template_status = (using_idleaway? purple_savedstatus_get_idleaway () :
    - purple_savedstatus_get_current ());
    - updated = FALSE;
    -
    - /* If there are substatuses */
    - if (purple_savedstatus_has_substatuses (template_status)) {
    - substatus_messages = g_hash_table_new (NULL, NULL);
    - for (accounts = purple_accounts_get_all ();
    - accounts != NULL;
    - accounts = accounts->next)
    - {
    - account = (PurpleAccount *) accounts->data;
    -
    - substatus = purple_savedstatus_get_substatus (template_status, account);
    - if (substatus) {
    - new_type = purple_status_type_get_primitive (
    - purple_savedstatus_substatus_get_type (substatus));
    - sample_message =
    - purple_savedstatus_substatus_get_message (substatus);
    -
    - if (sample_message) {
    - new_substatus_message = ap_generate (sample_message,
    - get_max_size_status (account, new_type));
    - } else {
    - new_substatus_message = NULL;
    - }
    -
    - g_hash_table_insert (substatus_messages, account,
    - new_substatus_message);
    -
    - if (!updated) {
    - old_type = ap_savedstatus_get_type (current_ap_status, account);
    - old_message =
    - ap_savedstatus_get_message (current_ap_status, account);
    -
    - if ((old_type != new_type) ||
    - ((old_message == NULL || new_substatus_message == NULL) &&
    - (old_message != new_substatus_message)) ||
    - (old_message != NULL && new_substatus_message != NULL &&
    - strcmp (old_message, new_substatus_message)))
    - {
    - updated = TRUE;
    - }
    - }
    - }
    - }
    - } else {
    - substatus_messages = NULL;
    - }
    -
    - /* And then the generic main message */
    - sample_message = purple_savedstatus_get_message (template_status);
    - if (sample_message) {
    - new_message = ap_generate (sample_message, get_max_size_status (NULL,
    - purple_savedstatus_get_type (template_status)));
    - } else {
    - new_message = NULL;
    - }
    -
    - new_type = purple_savedstatus_get_type (template_status);
    -
    - if (!updated) {
    - old_type = purple_savedstatus_get_type (current_ap_status);
    - old_message = purple_savedstatus_get_message (current_ap_status);
    -
    - if ((old_type != new_type) ||
    - ((old_message == NULL || new_message == NULL) &&
    - (old_message != new_message)) ||
    - (old_message != NULL && new_message != NULL &&
    - strcmp (old_message, new_message)))
    - {
    - updated = TRUE;
    - }
    - }
    -
    - if (updated) {
    - APMessageType type;
    - PurpleSavedStatus *new_status;
    -
    - new_status = purple_savedstatus_new (NULL, new_type);
    -
    - if (new_message) {
    - purple_savedstatus_set_message (new_status, new_message);
    - }
    -
    - for (accounts = purple_accounts_get_all ();
    - accounts != NULL;
    - accounts = accounts->next) {
    - account = (PurpleAccount *) accounts->data;
    - substatus = purple_savedstatus_get_substatus (template_status, account);
    -
    - if (substatus != NULL) {
    - substatus_type = purple_savedstatus_substatus_get_type (substatus);
    - new_substatus_message = (gchar *)
    - g_hash_table_lookup (substatus_messages, account);
    - purple_savedstatus_set_substatus (
    - new_status, account, substatus_type, new_substatus_message);
    - free (new_substatus_message);
    - }
    -
    - purple_savedstatus_activate_for_account (new_status, account);
    - }
    -
    - current_ap_status = new_status;
    -
    - if (new_type == PURPLE_STATUS_AVAILABLE) type = AP_MESSAGE_TYPE_AVAILABLE;
    - else if (new_type == PURPLE_STATUS_AWAY) type = AP_MESSAGE_TYPE_AWAY;
    - else type = AP_MESSAGE_TYPE_STATUS;
    -
    - ap_gtk_add_message (AP_UPDATE_STATUS, type, new_message);
    - }
    -
    - if (new_message) free (new_message);
    - if (substatus_messages) {
    - g_hash_table_destroy (substatus_messages);
    - }
    -
    - ap_update_queueing ();
    -
    - return updated;
    -}
    -
    -static gboolean ap_update_cb (gpointer data) {
    - gboolean result;
    - guint timeout;
    - guint delay;
    -
    - g_static_mutex_lock (&update_timeout_mutex);
    -
    - /* Start by removing timeout to self no matter what */
    - timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts, data));
    - if (timeout) purple_timeout_remove (timeout);
    -
    - /* In future, check here if widget content has changed? */
    -
    - switch (GPOINTER_TO_INT (data)) {
    - case AP_UPDATE_STATUS:
    - result = ap_update_status ();
    - break;
    - case AP_UPDATE_PROFILE:
    - result = ap_update_profile ();
    - break;
    - default:
    - result = TRUE;
    - }
    -
    - if (!result) {
    - ap_debug ("general", "Content hasn't changed, updating later");
    - delay = AP_SCHEDULE_UPDATE_DELAY;
    - } else {
    - ap_debug ("general", "Content updated");
    - delay =
    - purple_prefs_get_int ("/plugins/gtk/autoprofile/delay_update") * 1000;
    - }
    - timeout = purple_timeout_add (delay, ap_update_cb, data);
    - g_hash_table_insert (update_timeouts, data, GINT_TO_POINTER (timeout));
    -
    - g_static_mutex_unlock (&update_timeout_mutex);
    -
    - return FALSE;
    -}
    -
    -void ap_update (APUpdateType type)
    -{
    - ap_update_cb (GINT_TO_POINTER (type));
    -}
    -
    -void ap_update_after_delay (APUpdateType type)
    -{
    - guint timeout;
    -
    - g_static_mutex_lock (&update_timeout_mutex);
    -
    - timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts,
    - GINT_TO_POINTER (type)));
    - if (timeout) purple_timeout_remove (timeout);
    -
    - timeout = purple_timeout_add (AP_SCHEDULE_UPDATE_DELAY, ap_update_cb,
    - GINT_TO_POINTER (type));
    - g_hash_table_insert (update_timeouts, GINT_TO_POINTER (type),
    - GINT_TO_POINTER (timeout));
    -
    - g_static_mutex_unlock (&update_timeout_mutex);
    -}
    -
    -void ap_update_stop (APUpdateType type)
    -{
    - guint timeout;
    -
    - g_static_mutex_lock (&update_timeout_mutex);
    -
    - timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts,
    - GINT_TO_POINTER (type)));
    - if (timeout) purple_timeout_remove (timeout);
    -
    - g_hash_table_insert (update_timeouts, GINT_TO_POINTER (type), 0);
    -
    - g_static_mutex_unlock (&update_timeout_mutex);
    -}
    -
    -static void ap_account_connected (PurpleConnection *gc) {
    - ap_debug ("general", "Account connection detected");
    - ap_update_after_delay (AP_UPDATE_PROFILE);
    - ap_update_after_delay (AP_UPDATE_STATUS);
    -}
    -
    -void ap_update_queueing () {
    - if (ap_is_currently_away ()) {
    - if (purple_prefs_get_bool(
    - "/plugins/gtk/autoprofile/queue_messages_when_away")) {
    - purple_prefs_set_string (PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "away");
    - } else {
    - purple_prefs_set_string (PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never");
    - }
    - }
    -}
    -
    -/* Called whenever current status is changed by Purple's status menu
    - (in buddy list) */
    -static void ap_status_changed (
    - const char *name, PurplePrefType type, gconstpointer val, gpointer data) {
    - ap_debug ("general", "Status change detected");
    -
    - using_idleaway = FALSE;
    - ap_autoaway_touch ();
    - ap_update (AP_UPDATE_STATUS);
    -}
    -
    -void ap_autoaway_enable () {
    - ap_debug ("idle", "Using idleaway");
    -
    - using_idleaway = TRUE;
    - ap_update (AP_UPDATE_STATUS);
    -}
    -
    -void ap_autoaway_disable () {
    - ap_debug ("idle", "Disabling idleaway");
    -
    - using_idleaway = FALSE;
    - ap_update (AP_UPDATE_STATUS);
    -}
    -
    -gboolean ap_autoaway_in_use () {
    - return using_idleaway;
    -}
    -
    -static gboolean ap_update_queue (gpointer data)
    -{
    - PurpleAccount *account = NULL;
    - PurpleConnection *gc = NULL;
    -
    - g_static_mutex_lock (&update_queue_mutex);
    -
    - if (queued_profiles != NULL) {
    - account = (PurpleAccount *) queued_profiles->data;
    - queued_profiles = queued_profiles->next;
    - }
    -
    - g_static_mutex_unlock (&update_queue_mutex);
    -
    - gc = purple_account_get_connection (account);
    - if (gc != NULL) {
    - serv_set_info (gc, purple_account_get_user_info (account));
    - }
    -
    - return TRUE;
    -}
    -
    -static void ap_update_queue_start ()
    -{
    - update_queue_timeout = purple_timeout_add (2000, ap_update_queue, NULL);
    -}
    -
    -static void ap_update_queue_finish ()
    -{
    - purple_timeout_remove (update_queue_timeout);
    - update_queue_timeout = 0;
    -}
    -/*--------------------------------------------------------------------------*
    - * Preferences *
    - *--------------------------------------------------------------------------*/
    -static void ap_delete_legacy_prefs () {
    - if (purple_prefs_exists ("/plugins/gtk/autoprofile/tab_number")) {
    - ap_debug ("general", "Deleting legacy preferences");
    -
    - purple_prefs_remove ("/plugins/gtk/autoprofile/components");
    -
    - purple_prefs_remove ("/plugins/gtk/autoprofile/tab_number");
    -
    - purple_prefs_remove ("/plugins/gtk/autoprofile/accounts/enable_away");
    - purple_prefs_remove ("/plugins/gtk/autoprofile/accounts/enable_profile");
    - purple_prefs_remove ("/plugins/gtk/autoprofile/accounts");
    -
    - purple_prefs_remove ("/plugins/gtk/autoprofile/message_titles");
    - purple_prefs_remove ("/plugins/gtk/autoprofile/message_texts");
    -
    - purple_prefs_remove ("/plugins/gtk/autoprofile/default_profile");
    - purple_prefs_remove ("/plugins/gtk/autoprofile/default_away");
    - purple_prefs_remove ("/plugins/gtk/autoprofile/current_away");
    - purple_prefs_remove ("/plugins/gtk/autoprofile/added_text");
    -
    - purple_prefs_remove ("/plugins/gtk/autoprofile/delay_profile");
    - purple_prefs_remove ("/plugins/gtk/autoprofile/delay_away");
    -
    - purple_prefs_rename ("/plugins/gtk/autoprofile/text_respond",
    - "/plugins/gtk/autoprofile/autorespond/text");
    - purple_prefs_rename ("/plugins/gtk/autoprofile/text_trigger",
    - "/plugins/gtk/autoprofile/autorespond/trigger");
    - purple_prefs_rename ("/plugins/gtk/autoprofile/delay_respond",
    - "/plugins/gtk/autoprofile/autorespond/delay");
    - purple_prefs_rename ("/plugins/gtk/autoprofile/use_trigger",
    - "/plugins/gtk/autoprofile/autorespond/enable");
    - }
    -}
    -
    -static void ap_init_preferences () {
    - ap_debug ("general", "Initializing preference defaults if necessary");
    -
    - /* Adding the folders */
    - purple_prefs_add_none ("/plugins/gtk");
    - purple_prefs_add_none ("/plugins/gtk/autoprofile");
    - purple_prefs_add_none ("/plugins/gtk/autoprofile/widgets");
    - purple_prefs_add_none ("/plugins/gtk/autoprofile/autorespond");
    -
    - /* Behavior-settings */
    - purple_prefs_add_int ("/plugins/gtk/autoprofile/delay_update", 30);
    - purple_prefs_add_string ("/plugins/gtk/autoprofile/show_summary", "always");
    - purple_prefs_add_bool ("/plugins/gtk/autoprofile/queue_messages_when_away",
    - FALSE);
    - purple_prefs_add_bool ("/plugins/gtk/autoprofile/away_when_idle",
    - purple_prefs_get_bool ("/purple/away/away_when_idle"));
    -
    - /* Auto-response settings */
    - purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/auto_reply",
    - purple_prefs_get_string ("/purple/away/auto_reply"));
    - purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/text",
    - _("Say the magic word if you want me to talk more!"));
    - purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/trigger",
    - _("please"));
    - purple_prefs_add_int ("/plugins/gtk/autoprofile/autorespond/delay", 2);
    - purple_prefs_add_bool ("/plugins/gtk/autoprofile/autorespond/enable", TRUE);
    -
    - /* Profile settings */
    - purple_prefs_add_string_list(
    - "/plugins/gtk/autoprofile/profile_accounts", NULL);
    - purple_prefs_add_string ("/plugins/gtk/autoprofile/profile",
    - _("Get AutoProfile for Purple at <a href=\""
    - "http://autoprofile.sourceforge.net/\">"
    - "autoprofile.sourceforge.net</a><br><br>[Timestamp]"));
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Last Call *
    - *--------------------------------------------------------------------------*/
    -static void init_plugin (PurplePlugin *plugin)
    -{
    - info.name = _("AutoProfile");
    - info.summary = _("User profile and status message content generator");
    - info.description = _("Allows user to place dynamic text into profiles\n"
    - "and status messages, with the text automatically\n"
    - "updated whenever content changes");
    - info.author = _("Casey Ho <casey at hkn-berkeley-edu>\n\t\t\taim:caseyho");
    -
    - ap_debug ("general", "Initializing AutoProfile");
    -
    - ap_init_preferences ();
    - ap_widget_init ();
    -}
    -
    -PURPLE_INIT_PLUGIN (autoprofile, init_plugin, info)
    -
    --- a/autoprofile/autoprofile.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,110 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#ifndef AUTOPROFILE_H
    -#define AUTOPROFILE_H
    -
    -#include "../common/pp_internal.h"
    -
    -#include "sizes.h"
    -#include "widget.h"
    -#include "utility.h"
    -
    -#include "plugin.h"
    -#include "gtkplugin.h"
    -
    -#include "signals.h"
    -#include "prefs.h"
    -#include "util.h"
    -#include "notify.h"
    -
    -#include "string.h"
    -#include "time.h"
    -
    -#define AP_SCHEDULE_UPDATE_DELAY 3000
    -#define AP_GTK_MAX_MESSAGES 50
    -
    -/* Data types */
    -typedef enum
    -{
    - AP_MESSAGE_TYPE_OTHER = -1,
    - AP_MESSAGE_TYPE_PROFILE,
    - AP_MESSAGE_TYPE_AWAY,
    - AP_MESSAGE_TYPE_AVAILABLE,
    - AP_MESSAGE_TYPE_STATUS
    -} APMessageType;
    -
    -typedef enum
    -{
    - AP_UPDATE_UNKNOWN = 0,
    - AP_UPDATE_STATUS,
    - AP_UPDATE_PROFILE
    -} APUpdateType;
    -
    -/* Variable access functions */
    -PurplePlugin *ap_get_plugin_handle ();
    -gboolean ap_is_currently_away ();
    -
    -void ap_account_enable_profile (const PurpleAccount *, gboolean);
    -gboolean ap_account_has_profile_enabled (const PurpleAccount *);
    -
    -/* Core behavior functions */
    -gchar *ap_generate (const char *, gint);
    -gchar *ap_get_sample_status_message (PurpleAccount *account);
    -void ap_update (APUpdateType);
    -void ap_update_after_delay (APUpdateType);
    -void ap_update_stop (APUpdateType);
    -
    -/* Queueing functions */
    -void ap_update_queueing ();
    -
    -/* Auto-away functions */
    -void ap_autoaway_start ();
    -void ap_autoaway_finish ();
    -void ap_autoaway_touch ();
    -void ap_autoaway_enable ();
    -void ap_autoaway_disable ();
    -gboolean ap_autoaway_in_use ();
    -
    -/* Auto-reply functions */
    -void ap_autoreply_start ();
    -void ap_autoreply_finish ();
    -
    -/* Gtk Away Messages */
    -void ap_gtk_start ();
    -void ap_gtk_finish ();
    -void ap_gtk_make_visible ();
    -void ap_gtk_add_message (APUpdateType, APMessageType, const gchar *);
    -void ap_gtk_set_progress_visible (APUpdateType, gboolean);
    -
    -/* Gtk Actions */
    -GList *actions (PurplePlugin *, gpointer);
    -void ap_actions_finish ();
    -
    -/* Preferences */
    -extern PidginPluginUiInfo ui_info;
    -void ap_preferences_display ();
    -void ap_gtk_prefs_add_summary_option (GtkWidget *);
    -GtkWidget *get_account_page ();
    -
    -#endif /* #ifndef AUTOPROFILE_H */
    --- a/autoprofile/autoreply.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,324 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "conversation.h"
    -
    -#define SECS_BEFORE_RESENDING_AUTORESPONSE 600
    -#define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
    -#define MILLISECS_BEFORE_PROCESSING_MSG 100
    -
    -static guint pref_cb;
    -
    -static GSList *last_auto_responses = NULL;
    -struct last_auto_response {
    - PurpleConnection *gc;
    - char name[80];
    - time_t sent;
    -};
    -
    -static time_t response_timeout = 0;
    -
    -/*--------------------------------------------------------------------------*
    - * Auto-response utility functions *
    - *--------------------------------------------------------------------------*/
    -static gboolean
    -expire_last_auto_responses(gpointer data)
    -{
    - GSList *tmp, *cur;
    - struct last_auto_response *lar;
    -
    - tmp = last_auto_responses;
    -
    - while (tmp) {
    - cur = tmp;
    - tmp = tmp->next;
    - lar = (struct last_auto_response *)cur->data;
    -
    - if ((time(NULL) - lar->sent) > SECS_BEFORE_RESENDING_AUTORESPONSE) {
    - last_auto_responses = g_slist_remove(last_auto_responses, lar);
    - g_free(lar);
    - }
    - }
    -
    - return FALSE; /* do not run again */
    -}
    -
    -static struct last_auto_response *
    -get_last_auto_response(PurpleConnection *gc, const char *name)
    -{
    - GSList *tmp;
    - struct last_auto_response *lar;
    -
    - /* because we're modifying or creating a lar, schedule the
    - * function to expire them as the pref dictates */
    - purple_timeout_add((SECS_BEFORE_RESENDING_AUTORESPONSE + 5) * 1000,
    - expire_last_auto_responses, NULL);
    -
    - tmp = last_auto_responses;
    -
    - while (tmp) {
    - lar = (struct last_auto_response *)tmp->data;
    -
    - if (gc == lar->gc && !strncmp(name, lar->name, sizeof(lar->name)))
    - return lar;
    -
    - tmp = tmp->next;
    - }
    -
    - lar = (struct last_auto_response *)g_new0(struct last_auto_response, 1);
    - g_snprintf(lar->name, sizeof(lar->name), "%s", name);
    - lar->gc = gc;
    - lar->sent = 0;
    - last_auto_responses = g_slist_append(last_auto_responses, lar);
    -
    - return lar;
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Message send/receive general functionality *
    - *--------------------------------------------------------------------------*/
    -/* Detecting sent message stuff */
    -static void sent_im_msg_cb (PurpleAccount *account, const char *receiver,
    - const char *message)
    -{
    - PurpleConnection *gc;
    - PurplePresence *presence;
    - const gchar *auto_reply_pref;
    -
    - gc = purple_account_get_connection (account);
    - presence = purple_account_get_presence (account);
    -
    - /*
    - * FIXME - If "only auto-reply when away & idle" is set, then shouldn't
    - * this only reset lar->sent if we're away AND idle?
    - */
    - auto_reply_pref =
    - purple_prefs_get_string ("/plugins/gtk/autoprofile/autorespond/auto_reply");
    - if ((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
    - !purple_presence_is_available(presence) &&
    - strcmp(auto_reply_pref, "never"))
    - {
    - struct last_auto_response *lar;
    - lar = get_last_auto_response(gc, receiver);
    - lar->sent = time(NULL);
    - }
    -}
    -
    -/* Detecting received message stuff */
    -struct received_im_msg {
    - PurpleAccount *account;
    - char *sender;
    - char *message;
    -};
    -
    -static gint process_received_im_msg (gpointer data)
    -{
    - struct received_im_msg *received_im;
    - PurpleAccount *account;
    - char *sender;
    - char *message;
    - PurpleConnection *gc;
    - PurpleConversation *conv;
    -
    - received_im = (struct received_im_msg *) data;
    - account = received_im->account;
    - sender = received_im->sender;
    - message = received_im->message;
    - free (data);
    -
    - gc = purple_account_get_connection (account);
    -
    - /* search for conversation again in case it was created by other handlers */
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
    - sender, gc->account);
    - if (conv == NULL)
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
    -
    - /*
    - * Don't autorespond if:
    - *
    - * - it's not supported on this connection
    - * - we are available
    - * - or it's disabled
    - * - or we're not idle and the 'only auto respond if idle' pref
    - * is set
    - */
    - if (gc->flags & PURPLE_CONNECTION_AUTO_RESP)
    - {
    - PurplePresence *presence;
    - PurpleStatus *status;
    - PurpleStatusType *status_type;
    - PurpleStatusPrimitive primitive;
    - const gchar *auto_reply_pref;
    - char *away_msg = NULL;
    -
    - auto_reply_pref = purple_prefs_get_string(
    - "/plugins/gtk/autoprofile/autorespond/auto_reply");
    -
    - presence = purple_account_get_presence(account);
    - status = purple_presence_get_active_status(presence);
    - status_type = purple_status_get_type(status);
    - primitive = purple_status_type_get_primitive(status_type);
    - if ((primitive == PURPLE_STATUS_AVAILABLE) ||
    - (primitive == PURPLE_STATUS_INVISIBLE) ||
    - (primitive == PURPLE_STATUS_MOBILE) ||
    - !strcmp(auto_reply_pref, "never") ||
    - (!purple_presence_is_idle(presence) &&
    - !strcmp(auto_reply_pref, "awayidle")))
    - {
    - free (sender);
    - free (message);
    - return FALSE;
    - }
    -
    - away_msg = ap_get_sample_status_message (account);
    -
    - if ((away_msg != NULL) && (*away_msg != '\0')) {
    - struct last_auto_response *lar;
    - gboolean autorespond_enable;
    - time_t now = time(NULL);
    -
    - autorespond_enable = purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/autorespond/enable");
    - /*
    - * This used to be based on the conversation window. But um, if
    - * you went away, and someone sent you a message and got your
    - * auto-response, and then you closed the window, and then they
    - * sent you another one, they'd get the auto-response back too
    - * soon. Besides that, we need to keep track of this even if we've
    - * got a queue. So the rest of this block is just the auto-response,
    - * if necessary.
    - */
    - lar = get_last_auto_response(gc, sender);
    - if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE) {
    - lar->sent = now;
    - // Send basic autoresponse
    - serv_send_im (gc, sender, away_msg, PURPLE_MESSAGE_AUTO_RESP);
    - purple_conv_im_write (PURPLE_CONV_IM(conv), NULL, away_msg,
    - PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
    - now);
    -
    - // Send additional hint if enabled
    - if (autorespond_enable) {
    - const gchar *query = purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/autorespond/text");
    - serv_send_im (gc, sender, query, PURPLE_MESSAGE_AUTO_RESP);
    - purple_conv_im_write (PURPLE_CONV_IM (conv), NULL, query,
    - PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
    - now);
    - }
    -
    - } else if (autorespond_enable &&
    - difftime (time(NULL), response_timeout) >
    - purple_prefs_get_int ("/plugins/gtk/autoprofile/autorespond/delay")) {
    - gchar *text = purple_markup_strip_html (message);
    - if (match_start (text, purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/autorespond/trigger")) == 1) {
    - serv_send_im (gc, sender, away_msg, PURPLE_MESSAGE_AUTO_RESP);
    - purple_conv_im_write (PURPLE_CONV_IM (conv), NULL, away_msg,
    - PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
    - now);
    -
    - response_timeout = time (NULL);
    - ap_debug ("autorespond", "string matched, responding");
    - }
    - free (text);
    - }
    - }
    -
    - free (away_msg);
    - }
    -
    - free (sender);
    - free (message);
    -
    - return FALSE;
    -}
    -
    -static void received_im_msg_cb (PurpleAccount *account, char *sender,
    - char *message, PurpleConversation *conv, PurpleMessageFlags flags)
    -{
    - struct received_im_msg *received_im;
    -
    - received_im =
    - (struct received_im_msg *) malloc (sizeof (struct received_im_msg));
    - received_im->account = account;
    - received_im->sender = strdup (sender);
    - received_im->message = strdup (message);
    -
    - purple_timeout_add (MILLISECS_BEFORE_PROCESSING_MSG, process_received_im_msg,
    - received_im);
    -}
    -
    -static void auto_pref_cb (
    - const char *name, PurplePrefType type, gconstpointer val, gpointer data)
    -{
    - if (!strcmp (purple_prefs_get_string ("/purple/away/auto_reply"), "never"))
    - return;
    -
    - purple_notify_error (NULL, NULL,
    - N_("This preference is disabled"),
    - N_("This preference currently has no effect because AutoProfile is in "
    - "use. To modify this behavior, use the AutoProfile configuration "
    - "menu."));
    -
    - purple_prefs_set_string ("/purple/away/auto_reply", "never");
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Global functions *
    - *--------------------------------------------------------------------------*/
    -void ap_autoreply_start ()
    -{
    - purple_prefs_set_string ("/purple/away/auto_reply", "never");
    -
    - purple_signal_connect (purple_conversations_get_handle (), "sent-im-msg",
    - ap_get_plugin_handle (), PURPLE_CALLBACK(sent_im_msg_cb), NULL);
    - purple_signal_connect (purple_conversations_get_handle (), "received-im-msg",
    - ap_get_plugin_handle (), PURPLE_CALLBACK(received_im_msg_cb), NULL);
    -
    - pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (),
    - "/purple/away/auto_reply", auto_pref_cb, NULL);
    -}
    -
    -void ap_autoreply_finish ()
    -{
    - GSList *tmp;
    -
    - // Assumes signals are disconnected globally
    -
    - purple_prefs_disconnect_callback (pref_cb);
    - pref_cb = 0;
    -
    - purple_prefs_set_string ("/purple/away/auto_reply", purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/autorespond/auto_reply"));
    -
    - while (last_auto_responses) {
    - tmp = last_auto_responses->next;
    - g_free (last_auto_responses->data);
    - g_slist_free_1 (last_auto_responses);
    - last_auto_responses = tmp;
    - }
    -}
    -
    --- a/autoprofile/comp_countdownup.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,438 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "../common/pp_internal.h"
    -
    -#include "component.h"
    -#include "gtkprefs.h"
    -#include "utility.h"
    -
    -#include <math.h>
    -
    -static GtkWidget *spin_secs;
    -static GtkWidget *spin_mins;
    -static GtkWidget *spin_hour;
    -static GtkWidget *spin_day;
    -static GtkWidget *spin_month;
    -static GtkWidget *spin_year;
    -
    -/* Generate the time! */
    -char *count_generate (struct widget *w)
    -{
    - double d_secs, d_mins, d_hours, d_days;
    - char *s_secs, *s_mins, *s_hours, *s_days;
    - double difference;
    - int l, s;
    -
    - struct tm *ref_time;
    - char *result;
    -
    - ref_time = (struct tm *) malloc (sizeof (struct tm));
    -
    - ref_time->tm_sec = ap_prefs_get_int (w, "secs");
    - ref_time->tm_min = ap_prefs_get_int (w, "mins");
    - ref_time->tm_hour = ap_prefs_get_int (w, "hour");
    - ref_time->tm_mday = ap_prefs_get_int (w, "day");
    - ref_time->tm_mon = ap_prefs_get_int (w, "month") - 1;
    - ref_time->tm_year = ap_prefs_get_int (w, "year") - 1900;
    - ref_time->tm_isdst = -1;
    -
    - mktime (ref_time);
    -
    - if (ap_prefs_get_int (w, "down") == 1)
    - difference = difftime (mktime (ref_time), time(NULL));
    - else
    - difference = difftime (time(NULL), mktime (ref_time));
    -
    - if (difference < 0) {
    - d_secs = 0;
    - d_mins = 0;
    - d_hours = 0;
    - d_days = 0;
    - } else {
    - d_mins = floor (difference / 60);
    - d_secs = difference - (d_mins * 60);
    - d_hours = floor (d_mins / 60);
    - d_mins = d_mins - (d_hours * 60);
    - d_days = floor (d_hours / 24);
    - d_hours = d_hours - (d_days * 24);
    - }
    -
    - result = (char *)malloc(sizeof (char) * AP_SIZE_MAXIMUM);
    - l = ap_prefs_get_int (w, "large");
    - s = ap_prefs_get_int (w, "small");
    -
    - if (l < s) {
    - g_snprintf(result, AP_SIZE_MAXIMUM,
    - "%.0f days, %.0f hours, %.0f minutes, %.0f seconds",
    - d_days, d_hours, d_mins, d_secs);
    - free (ref_time);
    - return result;
    - }
    -
    - if (l < 3)
    - d_hours = d_hours + (d_days * 24);
    - if (l < 2)
    - d_mins = d_mins + (d_hours * 60);
    - if (l < 1)
    - d_secs = d_secs + (d_mins * 60);
    -
    - if (d_days == 1.0)
    - s_days = g_strdup ("day");
    - else
    - s_days = g_strdup ("days");
    -
    - if (d_hours == 1.0)
    - s_hours = g_strdup ("hour");
    - else
    - s_hours = g_strdup ("hours");
    -
    - if (d_mins == 1.0)
    - s_mins = g_strdup ("minute");
    - else
    - s_mins = g_strdup ("minutes");
    -
    - if (d_secs == 1.0)
    - s_secs = g_strdup ("second");
    - else
    - s_secs = g_strdup ("seconds");
    -
    - switch (l) {
    - case 3:
    - switch (s) {
    - case 3:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
    - d_days, s_days);
    - break;
    - case 2:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s",
    - d_days, s_days, d_hours, s_hours);
    - break;
    - case 1:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s, %.0f %s",
    - d_days, s_days, d_hours, s_hours, d_mins, s_mins);
    - break;
    - case 0:
    - g_snprintf (result, AP_SIZE_MAXIMUM,
    - "%.0f %s, %.0f %s, %.0f %s, %.0f %s",
    - d_days, s_days, d_hours, s_hours, d_mins, s_mins, d_secs, s_secs);
    - break;
    - default:
    - *result = '\0';
    - }
    - break;
    - case 2:
    - switch (s) {
    - case 2:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
    - d_hours, s_hours);
    - break;
    - case 1:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s",
    - d_hours, s_hours, d_mins, s_mins);
    - break;
    - case 0:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s, %.0f %s",
    - d_hours, s_hours, d_mins, s_mins, d_secs, s_secs);
    - break;
    - default:
    - *result = '\0';
    - }
    - break;
    - case 1:
    - switch (s) {
    - case 1:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
    - d_mins, s_mins);
    - break;
    - case 0:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s",
    - d_mins, s_mins, d_secs, s_secs);
    - break;
    - default:
    - *result = '\0';
    - }
    - break;
    - case 0:
    - g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
    - d_secs, s_secs);
    - break;
    - default:
    - *result = '\0';
    - }
    -
    - free (s_days);
    - free (s_hours);
    - free (s_mins);
    - free (s_secs);
    - free (ref_time);
    -
    - return result;
    -}
    -
    -static void update_year (GtkSpinButton *spinner, struct widget *w)
    -{
    - int value = gtk_spin_button_get_value_as_int (spinner);
    - ap_prefs_set_int (w, "year", value);
    -}
    -
    -static void update_month (GtkSpinButton *spinner, struct widget *w)
    -{
    - int value = gtk_spin_button_get_value_as_int (spinner);
    - ap_prefs_set_int (w, "month", value);
    -}
    -
    -static void update_day (GtkSpinButton *spinner, struct widget *w)
    -{
    - int value = gtk_spin_button_get_value_as_int (spinner);
    - ap_prefs_set_int (w, "day", value);
    -}
    -
    -static void update_hour (GtkSpinButton *spinner, struct widget *w)
    -{
    - int value = gtk_spin_button_get_value_as_int (spinner);
    - ap_prefs_set_int (w, "hour", value);
    -}
    -
    -static void update_mins (GtkSpinButton *spinner, struct widget *w)
    -{
    - int value = gtk_spin_button_get_value_as_int (spinner);
    - ap_prefs_set_int (w, "mins", value);
    -}
    -
    -static void update_secs (GtkSpinButton *spinner, struct widget *w)
    -{
    - int value = gtk_spin_button_get_value_as_int (spinner);
    - ap_prefs_set_int (w, "secs", value);
    -}
    -
    -static void set_to_current_time (GtkButton *button, struct widget *w)
    -{
    - time_t the_time;
    - struct tm *ref_time;
    -
    - the_time = time(NULL);
    - ref_time = ap_localtime(&the_time);
    - ap_prefs_set_int (w, "year", ref_time->tm_year + 1900);
    - ap_prefs_set_int (w, "month", ref_time->tm_mon + 1);
    - ap_prefs_set_int (w, "day", ref_time->tm_mday);
    - ap_prefs_set_int (w, "hour", ref_time->tm_hour);
    - ap_prefs_set_int (w, "mins", ref_time->tm_min);
    - ap_prefs_set_int (w, "secs", ref_time->tm_sec);
    - free (ref_time);
    -
    - if (spin_secs != NULL) {
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_secs),
    - ap_prefs_get_int (w, "secs"));
    - }
    - if (spin_mins != NULL) {
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_mins),
    - ap_prefs_get_int (w, "mins"));
    - }
    - if (spin_hour != NULL) {
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_hour),
    - ap_prefs_get_int (w, "hour"));
    - }
    - if (spin_day != NULL) {
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_day),
    - ap_prefs_get_int (w, "day"));
    - }
    - if (spin_month != NULL) {
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_month),
    - ap_prefs_get_int (w, "month"));
    - }
    - if (spin_year != NULL) {
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_year),
    - ap_prefs_get_int (w, "year"));
    - }
    -}
    -
    -GtkWidget *count_menu (struct widget *w)
    -{
    - GtkWidget *vbox, *hbox, *big_hbox, *frame;
    - GtkWidget *label, *spinner, *dropbox, *button;
    - GList *options;
    -
    - big_hbox = gtk_hbox_new (FALSE, 6);
    -
    - frame = pidgin_make_frame (big_hbox, _("Start/end time"));
    - vbox = gtk_vbox_new (FALSE, 6);
    - gtk_container_add (GTK_CONTAINER (frame), vbox);
    -
    - hbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new (_("Year: "));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
    - spinner = gtk_spin_button_new_with_range (1970, 2035, 1);
    - gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "year"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_year), w);
    - spin_year = spinner;
    -
    - hbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new (_("Month: "));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
    - spinner = gtk_spin_button_new_with_range (1, 12, 1);
    - gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "month"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_month), w);
    - spin_month = spinner;
    -
    - hbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new (_("Day: "));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
    - spinner = gtk_spin_button_new_with_range (1, 31, 1);
    - gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "day"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_day), w);
    - spin_day = spinner;
    -
    - hbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new (_("Hour: "));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
    - spinner = gtk_spin_button_new_with_range (0, 23, 1);
    - gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "hour"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_hour), w);
    - spin_hour = spinner;
    -
    - hbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new (_("Minutes: "));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
    - spinner = gtk_spin_button_new_with_range (0, 59, 1);
    - gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "mins"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_mins), w);
    - spin_mins = spinner;
    -
    - hbox = gtk_hbox_new (FALSE, 12);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new (_("Seconds: "));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
    - spinner = gtk_spin_button_new_with_range (0, 59, 1);
    - gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "secs"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_secs), w);
    - spin_secs = spinner;
    -
    - hbox = gtk_hbox_new (FALSE, 12);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - button = gtk_button_new_with_label ("Set to current time");
    -
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (set_to_current_time), w);
    - gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    -
    - frame = pidgin_make_frame (big_hbox, _("Which way"));
    - vbox = gtk_vbox_new (FALSE, 6);
    - gtk_container_add (GTK_CONTAINER (frame), vbox);
    -
    - options = g_list_append (NULL, (char *) _("Count down to stop date"));
    - options = g_list_append (options, GINT_TO_POINTER(1));
    - options = g_list_append (options, (char *)
    - _("Count time since start date"));
    - options = g_list_append (options, GINT_TO_POINTER(0));
    -
    - dropbox = ap_prefs_dropdown_from_list (w, vbox, NULL,
    - PURPLE_PREF_INT, "down", options);
    - g_list_free (options);
    -
    - options = g_list_append (NULL, (char *) _("Days"));
    - options = g_list_append (options, GINT_TO_POINTER(3));
    - options = g_list_append (options, (char *) _("Hours"));
    - options = g_list_append (options, GINT_TO_POINTER(2));
    - options = g_list_append (options, (char *) _("Minutes"));
    - options = g_list_append (options, GINT_TO_POINTER(1));
    - options = g_list_append (options, (char *) _("Seconds"));
    - options = g_list_append (options, GINT_TO_POINTER(0));
    -
    - dropbox = ap_prefs_dropdown_from_list (w, vbox,
    - _("Largest units displayed"), PURPLE_PREF_INT, "large", options);
    - dropbox = ap_prefs_dropdown_from_list (w, vbox,
    - _("Smallest units displayed"), PURPLE_PREF_INT, "small", options);
    - g_list_free (options);
    -
    - return big_hbox;
    -}
    -
    -/* Init prefs */
    -void count_init (struct widget *w) {
    - time_t the_time;
    - struct tm *ref_time;
    -
    - the_time = time(NULL);
    - ref_time = ap_localtime(&the_time);
    -
    - ap_prefs_add_int (w, "down", 1);
    - ap_prefs_add_int (w, "small", 0);
    - ap_prefs_add_int (w, "large", 3);
    - ap_prefs_add_int (w, "year",
    - ref_time->tm_year + 1900);
    - ap_prefs_add_int (w, "month",
    - ref_time->tm_mon + 1);
    - ap_prefs_add_int (w, "day",
    - ref_time->tm_mday);
    - ap_prefs_add_int (w, "hour",
    - ref_time->tm_hour);
    - ap_prefs_add_int (w, "mins",
    - ref_time->tm_min);
    - ap_prefs_add_int (w, "secs",
    - ref_time->tm_sec);
    - free (ref_time);
    -}
    -
    -struct component count =
    -{
    - N_("Countdown timer"),
    - N_("Given a date, shows amount of time until it (or since it)"),
    - "Timer",
    - &count_generate,
    - &count_init,
    - NULL,
    - NULL,
    - NULL,
    - &count_menu
    -};
    -
    --- a/autoprofile/comp_executable.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,169 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "../common/pp_internal.h"
    -
    -#include "component.h"
    -#include "utility.h"
    -
    -#include <string.h>
    -
    -/*---------- EXECUTABLE: STDOUT from a program ----------*/
    -static GtkWidget *file_selector;
    -static GtkWidget *file_entry;
    -
    -/* Read file into string and return */
    -char *executable_generate (struct widget *w)
    -{
    - char *text, *text_start;
    - int max;
    - gboolean exec;
    - GError *return_error;
    -
    - max = ap_prefs_get_int (w, "max_size");
    - exec = g_spawn_command_line_sync (ap_prefs_get_string (w, "command"),
    - &text_start, NULL, NULL, &return_error);
    -
    - if (!exec) {
    - /* Excution failed */
    - ap_debug ("executable", "command failed to execute");
    - return g_strdup (_("[ERROR: command failed to execute]"));
    - }
    -
    - if (strlen (text_start) < max)
    - text = text_start + strlen(text_start);
    - else
    - text = text_start + max;
    -
    - /* Should back off only if the last item is newline */
    - /* Gets rid of the extra <BR> in output */
    - text--;
    - if (*text != '\n')
    - text++;
    -
    - *text = '\0';
    - return text_start;
    -}
    -
    -void executable_filename (GtkWidget *widget, gpointer user_data) {
    - const gchar *selected_filename;
    -
    - selected_filename = gtk_file_selection_get_filename (
    - GTK_FILE_SELECTION (file_selector));
    -
    - ap_prefs_set_string ((struct widget *) user_data, "command",
    - selected_filename);
    - gtk_entry_set_text (GTK_ENTRY (file_entry), selected_filename);
    -}
    -
    -/* Creates and pops up file selection dialog for fortune file */
    -void executable_selection (GtkWidget *widget, struct widget *w) {
    - const char *cur_file;
    -
    - /* Create the selector */
    - file_selector = gtk_file_selection_new (
    - "Select the location of the program");
    -
    - cur_file = ap_prefs_get_string (w, "command");
    - if (strlen (cur_file) > 1) {
    - gtk_file_selection_set_filename (
    - GTK_FILE_SELECTION (file_selector), cur_file);
    - }
    -
    - g_signal_connect (GTK_OBJECT(
    - GTK_FILE_SELECTION(file_selector)->ok_button),
    - "clicked", G_CALLBACK (executable_filename), w);
    -
    - /* Destroy dialog box when the user clicks button. */
    - g_signal_connect_swapped (GTK_OBJECT(
    - GTK_FILE_SELECTION(file_selector)->ok_button),
    - "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector);
    -
    - g_signal_connect_swapped (GTK_OBJECT (
    - GTK_FILE_SELECTION (file_selector)->cancel_button),
    - "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector);
    -
    - /* Display dialog */
    - gtk_widget_show (file_selector);
    -}
    -
    -static gboolean executable_update (GtkWidget *widget, GdkEventFocus *evt,
    - gpointer data)
    -{
    - ap_prefs_set_string ((struct widget *) data, "command",
    - gtk_entry_get_text (GTK_ENTRY (file_entry)));
    - return FALSE;
    -}
    -
    -/* Create the menu */
    -GtkWidget *executable_menu (struct widget *w)
    -{
    - GtkWidget *ret = gtk_vbox_new (FALSE, 5);
    - GtkWidget *hbox, *label, *button;
    -
    - label = gtk_label_new (
    - _("Specify the command line you wish to execute"));
    - gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    - /* Text entry to type in program name */
    - file_entry = gtk_entry_new ();
    - gtk_box_pack_start (GTK_BOX (hbox), file_entry, FALSE, FALSE, 0);
    - gtk_entry_set_text (GTK_ENTRY (file_entry),
    - ap_prefs_get_string (w, "command"));
    - g_signal_connect (G_OBJECT (file_entry), "focus-out-event",
    - G_CALLBACK (executable_update), w);
    - /* Button to bring up file select dialog */
    - button = gtk_button_new_with_label ("Browse for program");
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (executable_selection), w);
    -
    - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
    -
    - ap_prefs_labeled_spin_button (w, ret,
    - _("Max characters to read from output: "), "max_size",
    - 1, AP_SIZE_MAXIMUM, NULL);
    -
    - return ret;
    -}
    -
    -void executable_init (struct widget *w) {
    - ap_prefs_add_string (w, "command", "date");
    - ap_prefs_add_int (w, "max_size", 1000);
    -}
    -
    -struct component executable =
    -{
    - N_("Command Line"),
    - N_("Reproduces standard output of running a program on the command line"),
    - "Command",
    - &executable_generate,
    - &executable_init,
    - NULL,
    - NULL,
    - NULL,
    - &executable_menu
    -};
    -
    --- a/autoprofile/comp_http.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,204 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "../common/pp_internal.h"
    -
    -#include "component.h"
    -
    -static GHashTable *refresh_timeouts = NULL;
    -
    -/*---------- HTTP: HTTP requested Data ----------*/
    -static void http_response (PurpleUtilFetchUrlData *reuqest_data, gpointer data, const char *c, gsize len, const gchar *error_message)
    -{
    - struct widget *w;
    - w = (struct widget *) data;
    -
    - // Invalid URL!
    - if (c == NULL) {
    - ap_prefs_set_string (w, "http_data",
    - _("[AutoProfile error: Invalid URL or no internet connection]"));
    - return;
    - }
    -
    - w = (struct widget *) data;
    - ap_prefs_set_string (w, "http_data", c);
    -}
    -
    -static char* http_generate (struct widget *w)
    -{
    - const char *result, *url;
    -
    - url = ap_prefs_get_string (w, "http_url");
    - if (!url || url[0] == '\0') {
    - return g_strdup (_("[AutoProfile error: No URL specified]"));
    - }
    -
    - result = ap_prefs_get_string (w, "http_data");
    - if (result == NULL) return g_strdup ("");
    - return g_strdup (result);
    -}
    -
    -static gboolean http_refresh_update (gpointer user_data)
    -{
    - struct widget *w;
    - char *http_url;
    -
    - w = (struct widget *) user_data;
    - http_url = g_strdup (ap_prefs_get_string (w, "http_url"));
    -
    - if( http_url && (http_url[0] != '\0') ) {
    - purple_util_fetch_url(http_url, TRUE, NULL, FALSE, http_response, w);
    - } else {
    - ap_prefs_set_string (w, "http_data", "");
    - }
    -
    - free (http_url);
    - return TRUE;
    -}
    -
    -static void http_load (struct widget *w)
    -{
    - gpointer http_refresh_timeout;
    -
    - if (refresh_timeouts == NULL) {
    - refresh_timeouts = g_hash_table_new (NULL, NULL);
    - }
    -
    - http_refresh_update (w);
    - http_refresh_timeout = GINT_TO_POINTER (g_timeout_add (
    - ap_prefs_get_int (w, "http_refresh_mins") * 60 * 1000,
    - http_refresh_update, w));
    - g_hash_table_insert (refresh_timeouts, w, http_refresh_timeout);
    -}
    -
    -static void http_unload (struct widget *w)
    -{
    - gpointer http_refresh_timeout;
    -
    - http_refresh_timeout = g_hash_table_lookup (refresh_timeouts, w);
    - g_source_remove (GPOINTER_TO_INT (http_refresh_timeout));
    - g_hash_table_remove (refresh_timeouts, w);
    -}
    -
    -static void http_init (struct widget *w)
    -{
    - ap_prefs_add_string (w, "http_url", "");
    - ap_prefs_add_string (w, "http_data", "");
    - ap_prefs_add_int (w, "http_refresh_mins", 1);
    -}
    -
    -static gboolean http_url_update (GtkWidget *widget, GdkEventFocus *evt,
    - gpointer data)
    -{
    - struct widget *w = (struct widget *) data;
    - ap_prefs_set_string (w, "http_url",
    - gtk_entry_get_text (GTK_ENTRY (widget)));
    -
    - return FALSE;
    -}
    -
    -static gboolean http_refresh_mins_update (GtkWidget *widget, gpointer data)
    -{
    - struct widget *w;
    - gpointer timeout;
    - int minutes;
    -
    - minutes = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
    -
    - w = (struct widget *) data;
    - ap_prefs_set_int (w, "http_refresh_mins", minutes);
    -
    - // Kill the current timer and run a new one
    - timeout = g_hash_table_lookup (refresh_timeouts, w);
    - g_source_remove (GPOINTER_TO_INT(timeout));
    - timeout = GINT_TO_POINTER (g_timeout_add (minutes * 60 * 1000,
    - http_refresh_update, w));
    - g_hash_table_replace (refresh_timeouts, w, timeout);
    -
    - return FALSE;
    -}
    -
    -static void http_data_update (GtkWidget *w, gpointer data) {
    - http_refresh_update (data);
    -}
    -
    -static GtkWidget *http_menu (struct widget *w)
    -{
    - GtkWidget *ret = gtk_vbox_new (FALSE, 5);
    - GtkWidget *label, *hbox, *button, *spinner;
    - GtkWidget *http_url_entry;
    -
    - label = gtk_label_new (_("Select URL with source content"));
    - gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    -
    - // URL Entry
    - http_url_entry = gtk_entry_new ();
    - gtk_box_pack_start (GTK_BOX (hbox), http_url_entry, TRUE, TRUE, 0);
    - gtk_entry_set_text (GTK_ENTRY (http_url_entry),
    - ap_prefs_get_string (w, "http_url"));
    - g_signal_connect (G_OBJECT (http_url_entry), "focus-out-event",
    - G_CALLBACK (http_url_update), w);
    -
    - // Update Now!
    - button = gtk_button_new_with_label (_("Fetch page now!"));
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (http_data_update), w);
    -
    - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new (_("Delay"));
    - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    -
    - spinner = gtk_spin_button_new_with_range (1, 60, 1);
    - gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "http_refresh_mins"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (http_refresh_mins_update), w);
    -
    - label = gtk_label_new (_("minutes between page fetches"));
    - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    -
    - return ret;
    -}
    -
    -struct component http =
    -{
    - N_("Webpage"),
    - N_("Data fetched from an internet URL using HTTP"),
    - "Webpage",
    - &http_generate,
    - &http_init,
    - &http_load,
    - &http_unload,
    - NULL,
    - &http_menu
    -};
    -
    --- a/autoprofile/comp_logstats.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1042 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "log.h"
    -#include "account.h"
    -#include "conversation.h"
    -#include "utility.h"
    -#include "util.h"
    -
    -#include "comp_logstats.h"
    -
    -#include <ctype.h>
    -#include <string.h>
    -
    -struct conversation_time {
    - time_t *start_time;
    - char *name;
    -};
    -
    -/* Represents data about a particular 24 hour period in the logs */
    -struct log_date {
    - int year; // The year
    - int month; // The month
    - int day; // The day
    - int received_msgs; // # msgs received
    - int received_words; // # words received
    - int sent_msgs; // # msgs sent
    - int sent_words; // # words sent
    - GSList *conversation_times; // List of conversation_time pointers
    -};
    -
    -/* List of struct log_dates
    - This is SORTED by most recent first */
    -static GSList *dates = NULL;
    -
    -/* Hashtable of log_dates */
    -static GHashTable *dates_table = NULL;
    -
    -/* Is the current line part of a message sent or received? */
    -static gboolean receiving = FALSE;
    -/* Shortcut vars */
    -static char *cur_receiver = NULL;
    -static char *cur_sender = NULL;
    -
    -/* Implements GCompareFunc */
    -static gint conversation_time_compare (gconstpointer x, gconstpointer y) {
    - const struct conversation_time *a = x;
    - const struct conversation_time *b = y;
    -
    - if (difftime (*(a->start_time), *(b->start_time)) == 0.0) {
    - if (!strcmp (a->name, b->name))
    - return 0;
    - }
    -
    - return -1;
    -}
    -
    -/* Implements GCompareFunc */
    -static gint log_date_compare (gconstpointer x, gconstpointer y)
    -{
    - const struct log_date *a = y;
    - const struct log_date *b = x;
    -
    - if (a->year == b->year) {
    - if (a->month == b->month) {
    - if (a->day == b->day)
    - return 0;
    - else
    - return a->day - b->day;
    - } else {
    - return a->month - b->month;
    - }
    - } else {
    - return a->year - b->year;
    - }
    -}
    -
    -/* Implements GHashFunc */
    -static guint log_date_hash (gconstpointer key)
    -{
    - const struct log_date *d = key;
    - return ((d->year * 365) + (d->month * 12) + (d->day));
    -}
    -
    -/* Implements GEqualFunc */
    -static gboolean log_date_equal (gconstpointer x, gconstpointer y)
    -{
    - const struct log_date *a = y;
    - const struct log_date *b = x;
    -
    - if (a->year == b->year &&
    - a->month == b->month &&
    - a->day == b->day) {
    - return TRUE;
    - }
    - return FALSE;
    -}
    -
    -/* Returns the struct log_date associated with a particular date.
    - Will MODIFY list of dates and insert sorted if not yet created */
    -static struct log_date *get_date (int year, int month, int day)
    -{
    - struct log_date *cur_date;
    - gpointer *node;
    -
    - cur_date = (struct log_date *)malloc(sizeof(struct log_date));
    - cur_date->year = year;
    - cur_date->month = month;
    - cur_date->day = day;
    -
    - if ((node = g_hash_table_lookup (dates_table, cur_date))) {
    - free (cur_date);
    - return (struct log_date *)node;
    - } else {
    - g_hash_table_insert (dates_table, cur_date, cur_date);
    - cur_date->received_msgs = 0;
    - cur_date->received_words = 0;
    - cur_date->sent_msgs = 0;
    - cur_date->sent_words = 0;
    - cur_date->conversation_times = NULL;
    - return cur_date;
    - }
    -}
    -
    -/* Like get_date, except specific to the current date */
    -static struct log_date *get_today ()
    -{
    - time_t the_time;
    - struct tm *cur_time;
    -
    - time (&the_time);
    - cur_time = localtime (&the_time);
    -
    - return get_date (cur_time->tm_year, cur_time->tm_mon, cur_time->tm_mday);
    -}
    -
    -static int string_word_count (const char *line)
    -{
    - int count, state;
    -
    - count = 0;
    - state = 0;
    -
    - /* If state is 1, currently processing a word */
    - while (*line) {
    - if (state == 0) {
    - if (!isspace (*line))
    - state = 1;
    - } else {
    - if (isspace (*line)) {
    - state = 0;
    - count++;
    - }
    - }
    - line++;
    - }
    -
    - if (state == 1)
    - count++;
    -
    - return count;
    -}
    -
    -/* Figure out if a person is yourself or someone else */
    -static gboolean is_self (PurpleAccount *a, const char *name) {
    - GList *accounts, *aliases, *aliases_start;
    - PurpleAccount *account;
    -
    - char *normalized;
    - const char *normalized_alias;
    -
    - if (cur_sender && !strcmp (cur_sender, name)) {
    - return TRUE;
    - }
    -
    - if (cur_receiver && !strcmp (cur_receiver, name)) {
    - return FALSE;
    - }
    -
    - normalized = strdup (purple_normalize (a, name));
    - accounts = purple_accounts_get_all ();
    -
    - aliases_start = aliases = purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/components/logstat/aliases");
    -
    - while (aliases) {
    - normalized_alias = purple_normalize (a, (char *)aliases->data);
    -
    - if (!strcmp (normalized, normalized_alias)) {
    - free_string_list (aliases_start);
    - free (normalized);
    -
    - if (cur_sender)
    - free (cur_sender);
    - cur_sender = strdup (name);
    -
    - return TRUE;
    - }
    -
    - aliases = aliases->next;
    - }
    -
    - free_string_list (aliases_start);
    -
    - while (accounts) {
    - account = (PurpleAccount *)accounts->data;
    - if (!strcmp (normalized, purple_account_get_username (account))) {
    - free (normalized);
    - if (cur_sender)
    - free (cur_sender);
    - cur_sender = strdup (name);
    - return TRUE;
    - }
    - accounts = accounts->next;
    - }
    -
    - free (normalized);
    -
    - if (cur_receiver)
    - free (cur_receiver);
    - cur_receiver = strdup (name);
    - return FALSE;
    -}
    -
    -/* Parses a line of a conversation */
    -static void parse_line (PurpleLog *cur_log, char *l, struct log_date *d)
    -{
    - char *cur_line, *cur_line_start;
    - char *name;
    - char *message;
    -
    - char *line = l;
    -
    - if (strlen (line) > 14 && *line == ' ')
    - line++;
    -
    - if (strlen (line) > 13 &&
    - *line == '(' &&
    - isdigit (*(line + 1)) &&
    - isdigit (*(line + 2)) &&
    - *(line + 3) == ':' &&
    - isdigit (*(line + 4)) &&
    - isdigit (*(line + 5)) &&
    - *(line + 6) == ':' &&
    - isdigit (*(line + 7)) &&
    - isdigit (*(line + 8)) &&
    - *(line + 9) == ')' &&
    - isspace (*(line + 10))) {
    - cur_line_start = cur_line = line + 11;
    - while (*cur_line) {
    - if (*cur_line == ':') {
    - *cur_line = '\0';
    - name = cur_line_start;
    - message = ++cur_line;
    -
    - receiving = !is_self (cur_log->account, name);
    -
    - if (receiving) {
    - d->received_msgs++;
    - d->received_words += string_word_count (message);
    - } else {
    - d->sent_msgs++;
    - d->sent_words += string_word_count (message);
    - }
    -
    - return;
    - }
    - cur_line++;
    - }
    -
    - }
    -
    - if (receiving) {
    - d->received_words += string_word_count (line);
    - } else {
    - d->sent_words += string_word_count (line);
    - }
    -}
    -
    -/* Parses a conversation if hasn't been handled yet */
    -static void parse_log (PurpleLog *cur_log)
    -{
    - struct log_date *the_date;
    - struct tm *the_time;
    - struct conversation_time *conv_time;
    -
    - PurpleLogReadFlags flags;
    - char *content, *cur_content, *cur_content_start, *temp;
    -
    - the_time = localtime (&(cur_log->time));
    - the_date = get_date (the_time->tm_year, the_time->tm_mon, the_time->tm_mday);
    -
    - /* Check for old log and if no conflicts, add to list */
    - conv_time = (struct conversation_time *)malloc (
    - sizeof (struct conversation_time));
    - conv_time->start_time = (time_t *)malloc (sizeof(time_t));
    - *(conv_time->start_time) = cur_log->time;
    - conv_time->name = strdup (cur_log->name);
    -
    - if (g_slist_find_custom (the_date->conversation_times, conv_time,
    - conversation_time_compare)) {
    - /* We already processed this! Halt! */
    - free (conv_time->start_time);
    - free (conv_time->name);
    - free (conv_time);
    - return;
    - }
    -
    - the_date->conversation_times = g_slist_prepend (the_date->conversation_times,
    - conv_time);
    -
    - /* Start rolling the counters! */
    - temp = purple_log_read (cur_log, &flags);
    - if (!strcmp ("html", cur_log->logger->id)) {
    - content = purple_markup_strip_html (temp);
    - free (temp);
    - } else {
    - content = temp;
    - }
    -
    - cur_content_start = cur_content = content;
    -
    - /* Splits the conversation into lines (each line may not necessarily
    - be a seperate message */
    - while (*cur_content) {
    - if (*cur_content == '\n') {
    - *cur_content = '\0';
    - parse_line (cur_log, cur_content_start, the_date);
    - cur_content_start = cur_content + 1;
    - }
    - cur_content++;
    - }
    -
    - parse_line (cur_log, cur_content_start, the_date);
    -
    - free (content);
    -}
    -
    -/* Get names of users in logs */
    -static GList *logstats_get_names (PurpleLogType type, PurpleAccount *account)
    -{
    - GDir *dir;
    - const char *prpl;
    - GList *ret;
    - const char *filename;
    - char *path, *me, *tmp;
    -
    - ret = NULL;
    -
    - if (type == PURPLE_LOG_CHAT)
    - me = g_strdup_printf ("%s.chat", purple_normalize(account,
    - purple_account_get_username(account)));
    - else
    - me = g_strdup (purple_normalize(account,
    - purple_account_get_username(account)));
    -
    - /* Get the old logger names */
    - path = g_build_filename(purple_user_dir(), "logs", NULL);
    - if (!(dir = g_dir_open(path, 0, NULL))) {
    - g_free(path);
    - return ret;
    - }
    -
    - while ((filename = g_dir_read_name (dir))) {
    - if (purple_str_has_suffix (filename, ".log")) {
    - tmp = strdup (filename);
    - *(tmp + strlen (filename) - 4) = '\0';
    - if (!string_list_find (ret, tmp))
    - ret = g_list_prepend (ret, strdup (tmp));
    - free (tmp);
    - }
    - }
    -
    - g_dir_close (dir);
    - g_free (path);
    -
    - /* Get the account-specific names */
    - prpl = PURPLE_PLUGIN_PROTOCOL_INFO
    - (purple_find_prpl (purple_account_get_protocol_id(account)))->list_icon(
    - account, NULL);
    -
    - path = g_build_filename(purple_user_dir(), "logs", prpl, me, NULL);
    - g_free (me);
    -
    - if (!(dir = g_dir_open(path, 0, NULL))) {
    - g_free(path);
    - return ret;
    - }
    -
    - while ((filename = g_dir_read_name (dir))) {
    - if (!string_list_find (ret, filename))
    - ret = g_list_prepend (ret, strdup (filename));
    - }
    -
    - g_dir_close (dir);
    - g_free (path);
    -
    - return ret;
    -}
    -
    -/* On load, reads in all logs and initializes stats database */
    -static void logstats_read_logs ()
    -{
    - GList *accounts, *logs, *logs_start, *names, *names_start;
    - PurpleLog *cur_log;
    -
    - accounts = purple_accounts_get_all();
    -
    - ap_debug ("logstats", "parsing log files");
    -
    - while (accounts) {
    - names_start = names = logstats_get_names (PURPLE_LOG_IM,
    - (PurpleAccount *)accounts->data);
    -
    - while (names) {
    - logs_start = purple_log_get_logs (PURPLE_LOG_IM, (char *)names->data,
    - (PurpleAccount *)accounts->data);
    - logs = logs_start;
    -
    - while (logs) {
    - cur_log = (PurpleLog *)logs->data;
    - parse_log (cur_log);
    - purple_log_free (cur_log);
    - logs = logs->next;
    - }
    -
    - g_list_free (logs_start);
    - names = names->next;
    - }
    -
    - free_string_list (names_start);
    - accounts = accounts->next;
    - }
    -
    - /* Cleanup */
    -
    - ap_debug ("logstats", "finished parsing log files");
    -}
    -
    -/* Implements GHFunc */
    -static void add_element (gpointer key, gpointer value, gpointer data)
    -{
    - dates = g_slist_insert_sorted (dates, value, log_date_compare);
    -}
    -
    -/* Updates GList against hashtable */
    -static void logstats_update_dates ()
    -{
    - g_slist_free (dates);
    - dates = NULL;
    - g_hash_table_foreach (dates_table, add_element, NULL);
    -}
    -
    -/*--------------------- Total calculations -------------------*/
    -static int get_total (const char *field)
    -{
    - GSList *cur_day;
    - int count;
    - struct log_date *d;
    -
    - cur_day = dates;
    - count = 0;
    - while (cur_day) {
    - d = (struct log_date *)cur_day->data;
    - if (!strcmp (field, "received_msgs")) {
    - count += d->received_msgs;
    - } else if (!strcmp (field, "received_words")) {
    - count += d->received_words;
    - } else if (!strcmp (field, "sent_msgs")) {
    - count += d->sent_msgs;
    - } else if (!strcmp (field, "sent_words")) {
    - count += d->sent_words;
    - } else if (!strcmp (field, "num_convos")) {
    - count += g_slist_length (d->conversation_times);
    - }
    -
    - cur_day = cur_day->next;
    - }
    -
    - return count;
    -}
    -
    -static int get_recent_total (const char *field, int hours)
    -{
    - GSList *cur_day;
    - int count;
    - struct log_date *d;
    - time_t cur_day_time;
    -
    - cur_day = dates;
    - count = 0;
    -
    - while (cur_day) {
    - d = (struct log_date *)cur_day->data;
    - cur_day_time = purple_time_build (d->year + 1900, d->month + 1, d->day,
    - 0, 0, 0);
    - if (difftime (time (NULL), cur_day_time) > (double) hours * 60.0 * 60.0)
    - break;
    -
    - if (!strcmp (field, "received_msgs")) {
    - count += d->received_msgs;
    - } else if (!strcmp (field, "sent_msgs")) {
    - count += d->sent_msgs;
    - } else if (!strcmp (field, "num_convos")) {
    - count += g_slist_length (d->conversation_times);
    - }
    -
    - cur_day = cur_day->next;
    - }
    -
    - return count;
    -}
    -
    -static int num_days_since_start ()
    -{
    - GSList *first_day;
    - double difference;
    - struct log_date *d;
    -
    - first_day = g_slist_last (dates);
    -
    - if (!first_day)
    - return 0;
    -
    - d = (struct log_date *)first_day->data;
    -
    - difference = difftime (
    - time (NULL), purple_time_build (d->year + 1900, d->month + 1, d->day,
    - 0, 0, 0));
    -
    - return (int) difference / (60.0 * 60.0 * 24.0);
    -}
    -
    -static struct log_date *get_max_date (const char *field)
    -{
    - struct log_date *max_date, *cur_date;
    - int max_so_far, cur_max;
    - GSList *cur_day;
    -
    - max_so_far = 0;
    - max_date = NULL;
    - cur_day = dates;
    -
    - while (cur_day) {
    - cur_date = (struct log_date *)cur_day->data;
    - if (!strcmp (field, "conversations")) {
    - cur_max = g_slist_length (cur_date->conversation_times);
    - } else if (!strcmp (field, "received")) {
    - cur_max = cur_date->received_msgs;
    - } else if (!strcmp (field, "sent")) {
    - cur_max = cur_date->sent_msgs;
    - } else if (!strcmp (field, "total")) {
    - cur_max = cur_date->sent_msgs + cur_date->received_msgs;
    - } else {
    - cur_max = 0;
    - }
    -
    - if (cur_max >= max_so_far) {
    - max_date = cur_date;
    - max_so_far = cur_max;
    - }
    -
    - cur_day = cur_day->next;
    - }
    -
    - return max_date;
    -}
    -
    -static char *date_string (const char *field)
    -{
    - struct log_date *d;
    - char *output;
    - struct tm *t_struct;
    - time_t t;
    - GSList *last_day;
    -
    - last_day = g_slist_last (dates);
    -
    - if (!last_day)
    - return NULL;
    -
    - if (!strcmp (field, "first")) {
    - d = (struct log_date *) last_day->data;
    - } else {
    - d = get_max_date (field);
    - }
    -
    - if (!d)
    - return NULL;
    -
    - output = (char *)malloc (sizeof(char) * AP_SIZE_MAXIMUM);
    - t_struct = (struct tm *)malloc(sizeof(struct tm));
    - t_struct->tm_year = d->year;
    - t_struct->tm_mon = d->month;
    - t_struct->tm_mday = d->day;
    - t_struct->tm_sec = 0;
    - t_struct->tm_min = 0;
    - t_struct->tm_hour = 0;
    - t = mktime (t_struct);
    - free (t_struct);
    - t_struct = localtime (&t);
    -
    - strftime (output, AP_SIZE_MAXIMUM - 1, "%a %b %d, %Y", t_struct);
    - return output;
    -}
    -
    -static int get_max (const char *field)
    -{
    - struct log_date *max_date = get_max_date (field);
    -
    - if (!max_date)
    - return 0;
    -
    - if (!strcmp (field, "conversations")) {
    - return g_slist_length (max_date->conversation_times);
    - } else if (!strcmp (field, "received")) {
    - return max_date->received_msgs;
    - } else if (!strcmp (field, "sent")) {
    - return max_date->sent_msgs;
    - } else if (!strcmp (field, "total")) {
    - return max_date->sent_msgs + max_date->received_msgs;
    - } else {
    - ap_debug ("logstats", "get-max: invalid parameter");
    - return 0;
    - }
    -
    -}
    -
    -
    -/*--------------------- Signal handlers ----------------------*/
    -static void logstats_received_im (PurpleAccount *account, char *sender,
    - char *message, int flags)
    -{
    - struct log_date *the_date;
    -
    - the_date = get_today ();
    - the_date->received_msgs++;
    - the_date->received_words += string_word_count (message);
    -
    - receiving = TRUE;
    -}
    -
    -static void logstats_sent_im (PurpleAccount *account, const char *receiver,
    - const char *message)
    -{
    - struct log_date *the_date;
    -
    - the_date = get_today ();
    - the_date->sent_msgs++;
    - the_date->sent_words += string_word_count (message);
    -
    - receiving = FALSE;
    -}
    -
    -static void logstats_conv_created (PurpleConversation *conv)
    -{
    - struct log_date *the_date;
    - struct conversation_time *the_time;
    -
    - if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
    - the_time = malloc (sizeof(struct conversation_time));
    - the_time->name = strdup (purple_conversation_get_name(conv));
    - the_time->start_time = malloc (sizeof(time_t));
    - time (the_time->start_time);
    -
    - the_date = get_today ();
    - the_date->conversation_times = g_slist_prepend (
    - the_date->conversation_times, the_time);
    -
    - logstats_update_dates ();
    - }
    -}
    -
    -/*--------------------------- Main functions -------------------------*/
    -
    -/* Component load */
    -void logstats_load (struct widget *w)
    -{
    - int count;
    - char *msg;
    -
    - if (!purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled")) {
    - return;
    - }
    -
    - /* Initialize database */
    - dates_table = g_hash_table_new (log_date_hash, log_date_equal);
    - logstats_read_logs ();
    - logstats_update_dates ();
    -
    - /* Debug */
    - msg = (char *)malloc (sizeof(char) * AP_SIZE_MAXIMUM);
    - count = get_total ("received_msgs");
    - g_snprintf (msg, AP_SIZE_MAXIMUM, "received msg total is %d", count);
    - ap_debug ("logstats", msg);
    - count = get_total ("sent_msgs");
    - g_snprintf (msg, AP_SIZE_MAXIMUM, "sent msg total is %d", count);
    - ap_debug ("logstats", msg);
    - count = get_total ("received_words");
    - g_snprintf (msg, AP_SIZE_MAXIMUM, "received word total is %d", count);
    - ap_debug ("logstats", msg);
    - count = get_total ("sent_words");
    - g_snprintf (msg, AP_SIZE_MAXIMUM, "sent word total is %d", count);
    - ap_debug ("logstats", msg);
    - count = get_total ("num_convos");
    - g_snprintf (msg, AP_SIZE_MAXIMUM, "num conversations is %d", count);
    - ap_debug ("logstats", msg);
    - count = g_slist_length (dates);
    - g_snprintf (msg, AP_SIZE_MAXIMUM, "num days with conversations is %d", count);
    - ap_debug ("logstats", msg);
    -
    - free(msg);
    -
    - /* Connect signals */
    - purple_signal_connect (purple_conversations_get_handle (),
    - "received-im-msg", ap_get_plugin_handle (),
    - PURPLE_CALLBACK (logstats_received_im), NULL);
    - purple_signal_connect (purple_conversations_get_handle (),
    - "sent-im-msg", ap_get_plugin_handle (),
    - PURPLE_CALLBACK (logstats_sent_im), NULL);
    - purple_signal_connect (purple_conversations_get_handle (),
    - "conversation-created", ap_get_plugin_handle (),
    - PURPLE_CALLBACK (logstats_conv_created), NULL);
    -}
    -
    -/* Component unload */
    -void logstats_unload (struct widget *w)
    -{
    - struct log_date *cur_date;
    - struct conversation_time *cur_time;
    - GSList *temp;
    -
    - if (!purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled")) {
    - return;
    - }
    -
    - /* Disconnect signals */
    - purple_signal_disconnect (purple_conversations_get_handle (),
    - "received-im-msg", ap_get_plugin_handle (),
    - PURPLE_CALLBACK (logstats_received_im));
    - purple_signal_disconnect (purple_conversations_get_handle (),
    - "sent-im-msg", ap_get_plugin_handle (),
    - PURPLE_CALLBACK (logstats_sent_im));
    - purple_signal_disconnect (purple_conversations_get_handle (),
    - "conversation-created", ap_get_plugin_handle (),
    - PURPLE_CALLBACK (logstats_conv_created));
    -
    - logstats_update_dates ();
    -
    - /* Free all the memory */
    - while (dates) {
    - cur_date = (struct log_date *)dates->data;
    - while (cur_date->conversation_times) {
    - temp = cur_date->conversation_times;
    - cur_time = (struct conversation_time *)temp->data;
    - cur_date->conversation_times = temp->next;
    - free (cur_time->start_time);
    - free (cur_time->name);
    - free (cur_time);
    - g_slist_free_1 (temp);
    - }
    - free (cur_date);
    - temp = dates;
    - dates = dates->next;
    - g_slist_free_1 (temp);
    - }
    -
    - if (cur_receiver) {
    - free (cur_receiver);
    - cur_receiver = NULL;
    - }
    - if (cur_sender) {
    - free (cur_sender);
    - cur_sender = NULL;
    - }
    - g_hash_table_destroy (dates_table);
    - dates_table = NULL;
    -}
    -
    -/* Generate the output */
    -static char *logstats_generate (struct widget *w)
    -{
    - char *buf, *output, *date;
    - int state;
    - const char *format;
    -
    - if (!purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled")) {
    - return NULL;
    - }
    -
    - format = purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/components/logstat/format");
    -
    - output = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM);
    - *output = '\0';
    - buf = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM);
    - *buf = '\0';
    -
    - state = 0;
    -
    - while (*format) {
    - if (state == 1) {
    - switch (*format) {
    - case '%':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format);
    - break;
    - case 'R':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("received_msgs"));
    - break;
    - case 'r':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("received_words"));
    - break;
    - case 'S':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("sent_msgs"));
    - break;
    - case 's':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("sent_words"));
    - break;
    - case 'T':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - get_total ("sent_msgs") + get_total ("received_msgs"));
    - break;
    - case 't':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - get_total ("sent_words") + get_total ("received_words"));
    - break;
    - case 'D':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - num_days_since_start ());
    - break;
    - case 'd':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, g_slist_length (dates));
    - break;
    - case 'N':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("num_convos"));
    - break;
    - case 'n':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("num_convos") / (double) g_slist_length (dates));
    - break;
    - case 'i':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("conversations"));
    - break;
    - case 'I':
    - date = date_string ("conversations");
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
    - free (date);
    - break;
    - case 'j':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("sent"));
    - break;
    - case 'J':
    - date = date_string ("sent");
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
    - free (date);
    - break;
    - case 'k':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("received"));
    - break;
    - case 'K':
    - date = date_string ("received");
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
    - free (date);
    - break;
    - case 'l':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("total"));
    - break;
    - case 'L':
    - date = date_string ("total");
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
    - free (date);
    - break;
    - case 'f':
    - date = date_string ("first");
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
    - free (date);
    - break;
    - case 'u':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("received_words") / (double) get_total ("received_msgs"));
    - break;
    - case 'v':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("sent_words") / (double) get_total ("sent_msgs"));
    - break;
    - case 'w':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) (get_total ("received_words") + get_total ("sent_words")) / (double) (get_total("received_msgs") + get_total ("sent_msgs")));
    - break;
    - case 'U':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("received_msgs") / (double) get_total ("num_convos"));
    - break;
    - case 'V':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("sent_msgs") / (double) get_total ("num_convos"));
    - break;
    - case 'W':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) (get_total ("received_msgs") + get_total ("sent_msgs")) / (double) get_total("num_convos"));
    - break;
    - case 'x':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("received_words") / (double) g_slist_length (dates));
    - break;
    - case 'y':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("sent_words") / (double) g_slist_length (dates));
    - break;
    - case 'z':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - ((double) get_total ("received_words") + (double) get_total ("sent_words")) / (double) g_slist_length (dates));
    - break;
    - case 'X':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("received_msgs") / (double) g_slist_length (dates));
    - break;
    - case 'Y':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) get_total ("sent_msgs") / (double) g_slist_length (dates));
    - break;
    - case 'Z':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
    - (double) (get_total ("received_msgs") + get_total ("sent_msgs")) / (double) g_slist_length (dates));
    - break;
    - case 'p':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.1f", output,
    - 100.0 * (double) g_slist_length (dates) / (double) num_days_since_start ());
    - break;
    - case 'a':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - ((struct log_date *) dates->data)->received_msgs);
    - break;
    - case 'b':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - ((struct log_date *) dates->data)->sent_msgs);
    - break;
    - case 'c':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - g_slist_length (((struct log_date *) dates->data)->conversation_times));
    - break;
    - case 'e':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - ((struct log_date *) dates->data)->sent_msgs + ((struct log_date *) dates->data)->received_msgs);
    - break;
    - case 'A':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("received_msgs", 24 * 7));
    - break;
    - case 'B':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("sent_msgs", 24 * 7));
    - break;
    - case 'C':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("num_convos", 24 * 7));
    - break;
    - case 'E':
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
    - get_recent_total ("received_msgs", 24 * 7) + get_recent_total ("received_msgs", 24 * 7));
    - break;
    - default:
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format);
    - break;
    - }
    -
    - strcpy (output, buf);
    - format++;
    - state = 0;
    - } else {
    - if (*format == '%') {
    - state = 1;
    - } else {
    - g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format);
    - strcpy (output, buf);
    - }
    - format++;
    - }
    -
    - }
    -
    - free (buf);
    - return output;
    -}
    -
    -/* Initialize preferences */
    -static void logstats_init (struct widget *w)
    -{
    - purple_prefs_add_none ("/plugins/gtk/autoprofile/components/logstat");
    - purple_prefs_add_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled", FALSE);
    - purple_prefs_add_string (
    - "/plugins/gtk/autoprofile/components/logstat/format", "");
    - purple_prefs_add_string_list (
    - "/plugins/gtk/autoprofile/components/logstat/aliases", NULL);
    -}
    -
    -/* The heart of the component */
    -static char *identifiers [7] = {
    - N_("logs"),
    - N_("log"),
    - N_("stat"),
    - N_("stats"),
    - N_("logstats"),
    - N_("log statistics"),
    - NULL
    -};
    -
    -struct component logstats =
    -{
    - N_("Purple log statistics"),
    - N_("Display various statistics about your message and system logs"),
    - identifiers,
    - logstats_generate,
    - logstats_init,
    - logstats_load,
    - logstats_unload,
    - NULL,
    - logstats_prefs
    -};
    -
    --- a/autoprofile/comp_logstats.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,28 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -void logstats_load (struct widget *w);
    -void logstats_unload (struct widget *w);
    -GtkWidget *logstats_prefs (struct widget *w);
    -
    -
    --- a/autoprofile/comp_logstats_gtk.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,355 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "comp_logstats.h"
    -#include "request.h"
    -
    -GtkWidget *checkbox = NULL;
    -
    -GtkListStore *alias_list = NULL;
    -GtkWidget *alias_view = NULL;
    -
    -/* General callbacks from main preferences */
    -static void logstats_response_cb (GtkDialog *dialog, gint id,
    - GtkWidget *widget)
    -{
    - purple_prefs_set_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled", TRUE);
    - logstats_load (NULL);
    - gtk_widget_set_sensitive (widget, TRUE);
    -
    - gtk_widget_destroy (GTK_WIDGET(dialog));
    -}
    -
    -static void toggle_enable (GtkButton *button, gpointer data)
    -{
    - GtkWidget *popup, *vbox, *label;
    - vbox = data;
    -
    - if (purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled")) {
    - logstats_unload (NULL);
    - purple_prefs_set_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled", FALSE);
    - gtk_widget_set_sensitive (vbox, FALSE);
    - } else {
    - popup = gtk_dialog_new_with_buttons (
    - "Enable stats for logs", NULL, 0,
    - GTK_STOCK_OK, 42, NULL);
    - g_signal_connect (G_OBJECT(popup), "response",
    - G_CALLBACK(logstats_response_cb), vbox);
    -
    - label = gtk_label_new(NULL);
    - gtk_label_set_markup(GTK_LABEL(label),
    - "\nEnabling this component will have some minor side effects. Doing so "
    - "will cause Purple to take slightly longer to start up because it must "
    - "parse a large amount of data to gather statistics. On average, this "
    - "can take slightly over a second for every 100,000 messages in your "
    - "logs.\n\nThe time from when you press the OK button to the time "
    - "when this dialog vanishes is a good approximation of how much extra "
    - "time will elapse before the login screen is shown.\n"
    - );
    - gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
    - gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
    - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup)->vbox), label,
    - FALSE, FALSE, 0);
    -
    - gtk_widget_show_all (popup);
    - }
    -}
    -
    -static gboolean logstat_format (GtkWidget *widget, GdkEventFocus *event,
    - gpointer data)
    -{
    - purple_prefs_set_string (
    - "/plugins/gtk/autoprofile/components/logstat/format",
    - gtk_entry_get_text (GTK_ENTRY (widget)));
    - return FALSE;
    -}
    -
    -static void new_alias (gpointer data, PurpleRequestFields *fields)
    -{
    - GtkTreeIter iter;
    - GList *aliases;
    -
    - const char *alias;
    -
    - alias = purple_request_fields_get_string (fields, "alias");
    - aliases = purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/components/logstat/aliases");
    -
    - aliases = g_list_append (aliases, strdup (alias));
    - purple_prefs_set_string_list (
    - "/plugins/gtk/autoprofile/components/logstat/aliases", aliases);
    - free_string_list (aliases);
    -
    - gtk_list_store_insert (alias_list, &iter, 0);
    - gtk_list_store_set (alias_list, &iter,
    - 0, alias, -1);
    -}
    -
    -static void alias_add (GtkButton *button, gpointer data)
    -{
    - PurpleRequestFields *fields;
    - PurpleRequestFieldGroup *group;
    - PurpleRequestField *field;
    -
    - fields = purple_request_fields_new();
    -
    - group = purple_request_field_group_new(NULL);
    - purple_request_fields_add_group(fields, group);
    -
    - field = purple_request_field_string_new("alias", _("Alias"),
    - NULL, FALSE);
    - purple_request_field_set_required(field, TRUE);
    - purple_request_field_set_type_hint(field, "alias");
    - purple_request_field_group_add_field(group, field);
    -
    - purple_request_fields(purple_get_blist(), _("Add Alias"),
    - NULL,
    - _("Type in the alias that you use"),
    - fields,
    - _("OK"), G_CALLBACK(new_alias),
    - _("Cancel"), NULL,
    - NULL, NULL, NULL, NULL);
    -}
    -
    -static void alias_delete (GtkButton *button, gpointer data)
    -{
    - GtkTreeSelection *selection;
    - GtkTreeIter iter;
    - char *alias;
    -
    - GList *aliases, *aliases_start, *new_aliases;
    -
    - selection = gtk_tree_view_get_selection (
    - GTK_TREE_VIEW (alias_view));
    -
    - if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
    - return;
    -
    - gtk_tree_model_get (GTK_TREE_MODEL (alias_list), &iter,
    - 0, &alias, -1);
    - gtk_list_store_remove (alias_list, &iter);
    -
    - aliases = purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/components/logstat/aliases");
    - aliases_start = aliases;
    -
    - new_aliases = NULL;
    -
    - while (aliases) {
    - if (strcmp ((char *)aliases->data, alias)) {
    - new_aliases = g_list_append (new_aliases, aliases->data);
    - }
    -
    - aliases = aliases->next;
    - }
    -
    - purple_prefs_set_string_list (
    - "/plugins/gtk/autoprofile/components/logstat/aliases", new_aliases);
    -
    - free_string_list (aliases_start);
    - g_list_free (new_aliases);
    - free (alias);
    -}
    -
    -static void alias_what (GtkButton *button, gpointer data)
    -{
    - purple_notify_formatted (NULL, _("Aliases"), _("What this list is for"), NULL,
    - _("Logs in Purple are stored verbatim with what you see on the screen. "
    - "The names of the people in the conversation (both yourself and your "
    - "buddy) are shown with their given aliases as opposed to actual screen "
    - "names. If you have given yourself an alias in a conversation, list "
    - "it using this dialog. If you do not, messages written by you will "
    - "be incorrectly identified as received instead of sent.<br><br>Correct "
    - "capitalization and whitespace are not required for detection to "
    - "work.<br><br>You must disable/re-enable log stats to refresh the "
    - "database after an alias change."),
    - NULL, NULL);
    -}
    -
    -/* The main window */
    -GtkWidget *logstats_prefs (struct widget *w)
    -{
    - GtkWidget *ret, *vbox, *hbox;
    - GtkWidget *label, *button, *entry, *sw;
    -
    - GtkCellRenderer *renderer;
    - GtkTreeSelection *selection;
    - GtkTreeViewColumn *col;
    - GtkTreeIter iter;
    - GList *aliases, *aliases_start;
    -
    - ret = gtk_vbox_new (FALSE, 6);
    -
    - /* Checkbox for enabling/disabling */
    - checkbox = gtk_check_button_new_with_mnemonic (
    - "Enable statistics for logs");
    - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbox),
    - purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled"));
    - gtk_box_pack_start (GTK_BOX(ret), checkbox, FALSE, FALSE, 0);
    -
    - vbox = gtk_vbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(ret), vbox, TRUE, TRUE, 0);
    -
    - /* Le format string */
    - label = gtk_label_new (NULL);
    - gtk_label_set_markup (GTK_LABEL(label), "<b>Format string for output</b>");
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
    -
    - entry = gtk_entry_new ();
    - gtk_box_pack_start (GTK_BOX(vbox), entry, FALSE, FALSE, 0);
    - gtk_entry_set_max_length (GTK_ENTRY(entry), 1000);
    - gtk_entry_set_text (GTK_ENTRY(entry), purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/components/logstat/format"));
    - g_signal_connect (G_OBJECT (entry), "focus-out-event",
    - G_CALLBACK (logstat_format), NULL);
    -
    - label = gtk_label_new (_(
    - "%R\tTotal messages received\n"
    - "%r\tTotal words received\n"
    - "%S\tTotal messages sent\n"
    - "%s\tTotal words sent\n"
    - "%T\tTotal messages sent/received\n"
    - "%t\tTotal words sent/received\n"
    - "%D\tNumber of days since first logged conversation\n"
    - "%d\tNumber of days with logged conversations\n"
    - "%N\tNumber of logged conversations\n"
    - "%n\tAverage number of conversations per day with logs\n"
    - "%i\tMost conversations in a single day\n"
    - "%I\tDate with most conversations\n"
    - "%j\tMost messages sent in a single day\n"
    - "%J\tDate with most messages sent\n"
    - "%k\tMost messages received in a single day\n"
    - "%K\tDate with most messages received\n"
    - "%l\tMost total messages sent/received in a single day\n"
    - "%L\tDate with most total messages sent/received\n"
    - "%f\tDate of first logged conversation\n"
    - "%u\tAverage words per message received\n"
    - "%v\tAverage words per message sent\n"
    - "%w\tAverage words per message sent/received\n"
    - "%U\tAverage messages received per conversation\n"
    - "%V\tAverage messages sent per conversation\n"
    - "%W\tAverage messages sent/received per conversation\n"
    - "%x\tAverage words received per day with logs\n"
    - "%y\tAverage words sent per day with logs\n"
    - "%z\tAverage words sent/received per day with logs\n"
    - "%X\tAverage messages received per day with logs\n"
    - "%Y\tAverage messages sent per day with logs\n"
    - "%Z\tAverage messages sent/received per day with logs\n"
    - "%p\tPercentage of days with logs\n"
    - "%a\tNumber of messages received today\n"
    - "%b\tNumber of messages sent today\n"
    - "%c\tNumber of conversations started today\n"
    - "%e\tNumber of messages sent/received today\n"
    - "%A\tNumber of messages received in last week\n"
    - "%B\tNumber of messages sent in last week\n"
    - "%C\tNumber of conversations started in last week\n"
    - "%E\tNumber of messages sent/received in last week\n"
    - "%%\t%"));
    -
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - sw = gtk_scrolled_window_new (NULL,NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
    - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
    - gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE , 0);
    - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(sw), label);
    -
    - /* Aliases */
    - label = gtk_label_new (NULL);
    - gtk_label_set_markup (GTK_LABEL(label), "<b>Personal aliases</b>");
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
    -
    - label = gtk_label_new (NULL);
    - gtk_label_set_markup (GTK_LABEL(label),
    - "You need this if you have an alias for your own screen name,\n"
    - "else IM's you sent will be incorrectly counted as received");
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 3);
    - gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label (_("Add alias"));
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK (alias_add), NULL);
    - gtk_box_pack_start (GTK_BOX(hbox), button, TRUE, TRUE, 0);
    - button = gtk_button_new_with_label (_("Delete alias"));
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK (alias_delete), NULL);
    - gtk_box_pack_start (GTK_BOX(hbox), button, TRUE, TRUE, 0);
    - button = gtk_button_new_with_label (_("?"));
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK (alias_what), NULL);
    - gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    -
    - sw = gtk_scrolled_window_new (0, 0);
    - gtk_box_pack_start (GTK_BOX(vbox), sw, FALSE, FALSE, 0);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_NEVER, GTK_POLICY_NEVER);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    -
    - alias_list = gtk_list_store_new (1, G_TYPE_STRING);
    - alias_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (alias_list));
    - gtk_container_add (GTK_CONTAINER(sw), alias_view);
    -
    - renderer = gtk_cell_renderer_text_new ();
    - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(alias_view), FALSE);
    - selection = gtk_tree_view_get_selection (
    - GTK_TREE_VIEW (alias_view));
    - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
    -
    - col = gtk_tree_view_column_new_with_attributes (
    - _("Alias"), renderer, "text", 0, NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW(alias_view), col);
    -
    - aliases = purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/components/logstat/aliases");
    - aliases_start = aliases;
    -
    - while (aliases) {
    - gtk_list_store_append (alias_list, &iter);
    - gtk_list_store_set (alias_list, &iter,
    - 0, (char *)aliases->data, -1);
    - aliases = aliases->next;
    - }
    - free_string_list (aliases_start);
    -
    - /* Finish up the checkbox stuff */
    - g_signal_connect (G_OBJECT(checkbox), "clicked",
    - G_CALLBACK(toggle_enable), vbox);
    - if (!purple_prefs_get_bool (
    - "/plugins/gtk/autoprofile/components/logstat/enabled")) {
    - gtk_widget_set_sensitive (vbox, FALSE);
    - } else {
    - gtk_widget_set_sensitive (vbox, TRUE);
    - }
    -
    - return ret;
    -}
    --- a/autoprofile/comp_quotation.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,602 +0,0 @@
    -/*----------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *----------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "request.h"
    -
    -#include "component.h"
    -#include "utility.h"
    -
    -enum {
    - QUOTATION_LIST_STORE = 1,
    - QUOTATION_FILE_SELECTOR,
    - QUOTATION_TREE_VIEW
    -};
    -
    -/*--------------------------------------------------------------------------*
    - * Menu related things *
    - *--------------------------------------------------------------------------*/
    -static void append_quote (struct widget *w, GtkListStore *ls, gchar *quote)
    -{
    - GString *s;
    - GtkTreeIter iter;
    - gchar *quote_tmp;
    - GtkWidget *treeview;
    - GtkTreeSelection *selection;
    -
    - gtk_list_store_append (ls, &iter);
    -
    - quote_tmp = purple_markup_strip_html (quote);
    - s = g_string_new ("");
    - g_string_printf (s, "%ld bytes", g_utf8_strlen (quote, -1));
    -
    - gtk_list_store_set (ls, &iter,
    - 0, quote_tmp,
    - 1, quote,
    - 2, s->str,
    - -1);
    - g_free (quote_tmp);
    - g_string_free (s, TRUE);
    -
    - treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW);
    - if (treeview == NULL) return;
    -
    - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
    - gtk_tree_selection_select_iter (selection, &iter);
    -}
    -
    -static void file_dialog_cb (GtkWidget *dialog, int response, struct widget *w)
    -{
    - GtkWidget *checkbox;
    - gchar *filename;
    - GList *quotes, *quotes_start, *new_quotes;
    - gboolean include_html;
    -
    - GtkListStore *ls;
    -
    - switch (response) {
    - case GTK_RESPONSE_ACCEPT:
    - ls = ap_widget_get_data (w, QUOTATION_LIST_STORE);
    - if (ls == NULL) break;
    -
    - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
    - checkbox = gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER(dialog));
    - g_object_get (checkbox, "active", &include_html, NULL);
    -
    - quotes = ap_prefs_get_string_list (w, "quotes");
    - new_quotes = read_fortune_file (filename, !include_html);
    - g_free (filename);
    -
    - quotes = g_list_concat (quotes, new_quotes);
    - ap_prefs_set_string_list (w, "quotes", quotes);
    -
    - quotes_start = quotes;
    -
    - for (quotes = new_quotes; quotes != NULL; quotes = quotes->next) {
    - append_quote (w, ls, quotes->data);
    - }
    -
    - free_string_list (quotes_start);
    - break;
    - case GTK_RESPONSE_CANCEL:
    - case GTK_RESPONSE_DELETE_EVENT:
    - break;
    - }
    -
    - ap_widget_set_data (w, QUOTATION_FILE_SELECTOR, NULL);
    - gtk_widget_destroy (dialog);
    -}
    -
    -static void quotation_explain_fortune_file (GtkMenuItem *item, gpointer data)
    -{
    - purple_notify_formatted (NULL, _("Fortune files"),
    - _("A quick definition of a fortune file"), NULL,
    - _("A fortune file is a simple text file with a number of quotes. "
    - "The following is an example:<br><br>"
    - "<b>\"Glory is fleeing, but obscurity is forver.\"<br>"
    - "- Napoleon Bonaparte (1769-1821)<br>"
    - "%<br>"
    - "Blagggghhhh!<br>"
    - "%<br>"
    - "Yet another quote<br>"
    - "%<br></b><br>"
    - "Quotes can have any sort of text within them. They end when there "
    - "is a newline followed by a percent sign \"%\" on the next line.<br>"
    - "<br>Fortune files with pre-selected quotes can be found on the"
    - "internet."),
    - NULL, NULL);
    -}
    -
    -static void quotation_select_import_file (GtkMenuItem *item, struct widget *w)
    -{
    - GtkWidget *dialog;
    - GtkWidget *checkbox;
    -
    - dialog = gtk_file_chooser_dialog_new (
    - _("Select fortune file to import quotes from"),
    - NULL,
    - GTK_FILE_CHOOSER_ACTION_OPEN,
    - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
    - NULL);
    - g_signal_connect (G_OBJECT(dialog), "response", G_CALLBACK (file_dialog_cb),
    - w);
    - ap_widget_set_data (w, QUOTATION_FILE_SELECTOR, dialog);
    -
    - checkbox = gtk_check_button_new_with_label (
    - _("Interpret bracketed text (such as \"<br>\") as HTML tags"));
    - gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), checkbox);
    -
    - gtk_widget_show_all (dialog);
    -}
    -
    -static void quotation_edit_dialog_cb (struct widget *w, const char *quote)
    -{
    - GtkWidget *treeview;
    - GtkTreeSelection *selection;
    - GtkTreeIter iter;
    - GtkTreeModel *model;
    -
    - treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW);
    - if (treeview == NULL) return;
    -
    - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
    -
    - if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
    - GString *s;
    - gchar *quote_tmp, *old_quote;
    - GList *start, *node;
    -
    - gtk_tree_model_get (model, &iter, 1, &old_quote, -1);
    - start = ap_prefs_get_string_list (w, "quotes");
    - /* FIXME: this could grab the wrong quote, if two quotes are identical */
    - for (node = start; node != NULL; node = node->next) {
    - if (!strcmp ((char *) node->data, old_quote)) {
    - /* Update saved prefs */
    - g_free (node->data);
    - node->data = strdup (quote);
    -
    - ap_prefs_set_string_list (w, "quotes", start);
    -
    - free_string_list (start);
    - g_free (old_quote);
    -
    - /* Update list store */
    - quote_tmp = purple_markup_strip_html (quote);
    - s = g_string_new ("");
    - g_string_printf (s, "%ld bytes", g_utf8_strlen (quote, -1));
    -
    - gtk_list_store_set (GTK_LIST_STORE (model), &iter,
    - 0, quote_tmp,
    - 1, quote,
    - 2, s->str,
    - -1);
    - g_free (quote_tmp);
    - g_string_free (s, TRUE);
    - return;
    - }
    - }
    -
    - free_string_list (start);
    - g_free (old_quote);
    - } else {
    - purple_notify_error (NULL, NULL,
    - N_("Unable to edit quote"),
    - N_("No quote is currently selected"));
    - }
    -
    -}
    -
    -static void quotation_edit_dialog (struct widget *w, const gchar *quote)
    -{
    - purple_request_input (ap_get_plugin_handle (), NULL,
    - _("Edit quote"), NULL,
    - quote,
    - TRUE, FALSE, "html",
    - _("Save"), G_CALLBACK(quotation_edit_dialog_cb),
    - _("Cancel"), NULL, NULL, NULL, NULL,
    - w);
    -}
    -
    -static void quotation_edit (GtkWidget *button, struct widget *w)
    -{
    - GtkWidget *treeview;
    - GtkTreeSelection *selection;
    - GtkTreeIter iter;
    - GtkTreeModel *model;
    - gchar *quote;
    -
    - treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW);
    - if (treeview == NULL) return;
    -
    - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
    -
    - if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
    - gtk_tree_model_get (model, &iter, 1, &quote, -1);
    - quotation_edit_dialog (w, quote);
    - g_free (quote);
    - } else {
    - purple_notify_error (NULL, NULL,
    - N_("Unable to edit quote"),
    - N_("No quote is currently selected"));
    - }
    -}
    -
    -static void quotation_create (GtkWidget *button, struct widget *w)
    -{
    - GtkListStore *ls;
    - GList *quotes;
    -
    - ls = ap_widget_get_data (w, QUOTATION_LIST_STORE);
    - if (ls == NULL) return;
    -
    - append_quote (w, ls, "");
    -
    - quotes = ap_prefs_get_string_list (w, "quotes");
    - quotes = g_list_append (quotes, strdup (""));
    - ap_prefs_set_string_list (w, "quotes", quotes);
    -
    - free_string_list (quotes);
    -
    - quotation_edit_dialog (w, "");
    -}
    -
    -static void quotation_delete (GtkWidget *button, struct widget *w)
    -{
    - GtkWidget *treeview;
    - GtkTreeSelection *selection;
    - GtkTreeIter iter;
    - GtkTreeModel *model;
    - gchar *quote;
    -
    - GList *start, *node;
    -
    - treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW);
    - if (treeview == NULL) return;
    -
    - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
    -
    - if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
    - gtk_tree_model_get (model, &iter, 1, &quote, -1);
    - start = ap_prefs_get_string_list (w, "quotes");
    -
    - /* FIXME: this could grab the wrong quote, if two quotes are identical */
    - for (node = start; node != NULL; node = node->next) {
    - if (!strcmp ((char *) node->data, quote)) {
    - start = g_list_remove_link (start, node);
    - g_list_free_1 (node);
    - g_free (node->data);
    - ap_prefs_set_string_list (w, "quotes", start);
    -
    - free_string_list (start);
    - g_free (quote);
    -
    - gtk_list_store_remove (GTK_LIST_STORE(model), &iter);
    - return;
    - }
    - }
    -
    - free_string_list (start);
    - g_free (quote);
    - } else {
    - purple_notify_error (NULL, NULL,
    - N_("Unable to delete quote"),
    - N_("No quote is currently selected"));
    - }
    -}
    -
    -static void quotation_delete_all_cb (struct widget *w)
    -{
    - GtkListStore *ls;
    -
    - ls = ap_widget_get_data (w, QUOTATION_LIST_STORE);
    - if (ls == NULL) return;
    -
    - gtk_list_store_clear (ls);
    -
    - ap_prefs_set_string_list (w, "quotes", NULL);
    -}
    -
    -static void quotation_delete_all (GtkMenuItem *item, struct widget *w)
    -{
    - purple_request_ok_cancel (ap_get_plugin_handle (),
    - NULL, _("Delete all quotes?"), NULL, 0, NULL, NULL,
    - NULL, w, G_CALLBACK(quotation_delete_all_cb), NULL);
    -}
    -
    -static void quotation_more_menu (GtkWidget *button, struct widget *w)
    -{
    - GtkWidget *menu;
    - GtkWidget *menu_item;
    -
    - menu = gtk_menu_new ();
    -
    - menu_item = gtk_menu_item_new_with_label (_("Delete all quotes"));
    - gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);
    - g_signal_connect (G_OBJECT(menu_item), "activate",
    - G_CALLBACK(quotation_delete_all), w);
    -
    - menu_item = gtk_separator_menu_item_new ();
    - gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);
    -
    - menu_item = gtk_menu_item_new_with_label (
    - _("Import quotes from from fortune file"));
    - gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);
    - g_signal_connect (G_OBJECT(menu_item), "activate",
    - G_CALLBACK(quotation_select_import_file), w);
    -
    - menu_item = gtk_menu_item_new_with_label (
    - _("What is a fortune file?"));
    - gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);
    - g_signal_connect (G_OBJECT(menu_item), "activate",
    - G_CALLBACK(quotation_explain_fortune_file), NULL);
    -
    - gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, w, 0,
    - gtk_get_current_event_time ());
    - gtk_widget_show_all (menu);
    -}
    -
    -static void quotation_rate_changed (GtkSpinButton *spinner, struct widget *w)
    -{
    - int value = gtk_spin_button_get_value_as_int (spinner);
    - ap_prefs_set_int (w, "update_rate", value);
    -}
    -
    -static void quotation_force_change (GtkButton *button, struct widget *w)
    -{
    - ap_prefs_set_int (w, "current_index",
    - ap_prefs_get_int (w, "current_index") + 1);
    -}
    -
    -
    -
    -static gboolean
    -search_func(GtkTreeModel *model, gint column, const gchar *key,
    - GtkTreeIter *iter, gpointer search_data)
    -{
    - gboolean result;
    - char *haystack;
    -
    - gtk_tree_model_get (model, iter, 1, &haystack, -1);
    - result = (purple_strcasestr(haystack, key) == NULL);
    - g_free(haystack);
    -
    - return result;
    -}
    -
    -static void menu_destroy_cb (GtkWidget *widget, struct widget *w)
    -{
    - GtkWidget *file_selector;
    -
    - ap_widget_set_data (w, QUOTATION_LIST_STORE, NULL);
    - ap_widget_set_data (w, QUOTATION_TREE_VIEW, NULL);
    -
    - file_selector = (GtkWidget *) ap_widget_get_data (w, QUOTATION_FILE_SELECTOR);
    - if (file_selector != NULL) {
    - file_dialog_cb (file_selector, GTK_RESPONSE_DELETE_EVENT, w);
    - }
    -}
    -
    -static GtkWidget *quotation_menu (struct widget *w)
    -{
    - GtkWidget *ret, *hbox;
    - GtkWidget *button, *label, *spinner;
    - GtkWidget *sw;
    - GList *quotes, *quotes_start;
    -
    - GtkWidget *treeview;
    - GtkListStore *ls;
    - GtkTreeViewColumn *col;
    - GtkCellRenderer *rend;
    -
    - ret = gtk_vbox_new (FALSE, 6);
    - g_signal_connect (G_OBJECT(ret), "destroy", G_CALLBACK (menu_destroy_cb), w);
    -
    - /* The main view */
    - sw = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    - gtk_box_pack_start (GTK_BOX(ret), sw, TRUE, TRUE, 0);
    -
    - ls = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
    - ap_widget_set_data (w, QUOTATION_LIST_STORE, ls);
    -
    - treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(ls));
    - ap_widget_set_data (w, QUOTATION_TREE_VIEW, treeview);
    -
    - rend = gtk_cell_renderer_text_new ();
    - col = gtk_tree_view_column_new_with_attributes (_("Size"),
    - rend, "text", 2, NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), col);
    - g_object_set (G_OBJECT(rend),
    - "cell-background-set", TRUE,
    - "cell-background", "gray",
    - NULL);
    -
    - rend = gtk_cell_renderer_text_new ();
    - col = gtk_tree_view_column_new_with_attributes (_("Quotes"),
    - rend, "text", 0, NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), col);
    -
    - /* Enable CTRL+F searching */
    - gtk_tree_view_set_search_column (GTK_TREE_VIEW(treeview), 0);
    - gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW(treeview),
    - search_func, NULL, NULL);
    -
    - gtk_container_add (GTK_CONTAINER(sw), treeview);
    -
    - /* Add in the original quotes */
    - quotes_start = ap_prefs_get_string_list (w, "quotes");
    -
    - for (quotes = quotes_start; quotes != NULL; quotes = quotes->next) {
    - append_quote (w, ls, quotes->data);
    - }
    - free_string_list (quotes_start);
    -
    - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(treeview), TRUE);
    -
    - /* Bottom buttons */
    - hbox = gtk_hbutton_box_new ();
    - gtk_button_box_set_layout (GTK_BUTTON_BOX(hbox),
    - GTK_BUTTONBOX_SPREAD);
    -
    - gtk_box_pack_start (GTK_BOX(ret), hbox, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label (_("New quote"));
    - gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK(quotation_create), w);
    -
    - button = gtk_button_new_with_label (_("Edit"));
    - gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK(quotation_edit), w);
    -
    - button = gtk_button_new_with_label (_("Delete"));
    - gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK(quotation_delete), w);
    -
    - button = gtk_button_new_with_label (_("More..."));
    - gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK(quotation_more_menu), w);
    -
    - /* Separator */
    - gtk_box_pack_start (GTK_BOX(ret), gtk_hseparator_new (), FALSE, FALSE, 0);
    -
    - /* Behavior selection */
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX(ret), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new (_("Change quote every "));
    - gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - spinner = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
    - gtk_box_pack_start (GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON(spinner),
    - ap_prefs_get_int (w, "update_rate"));
    - g_signal_connect (G_OBJECT(spinner), "value-changed",
    - G_CALLBACK(quotation_rate_changed), w);
    -
    - label = gtk_label_new (_("hours (0: always show a new quote)"));
    - gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label (_("Change quote now"));
    - gtk_box_pack_end (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (quotation_force_change), w);
    -
    - return ret;
    -}
    -
    -
    -/*--------------------------------------------------------------------------*
    - * Core quotation things *
    - *--------------------------------------------------------------------------*/
    -static gchar *quotation_generate (struct widget *w)
    -{
    - GList *quotes;
    - gchar *ret;
    - int num_quotes, index;
    - time_t cur_time, old_time;
    - char *time_string;
    - struct tm *t;
    -
    - index = ap_prefs_get_int (w, "current_index");
    - quotes = ap_prefs_get_string_list (w, "quotes");
    -
    - /* Sanity check the quotes */
    - num_quotes = g_list_length (quotes);
    -
    - if (num_quotes == 0) {
    - return strdup (_("[ERROR: no quotes available]"));
    - }
    -
    - /* Increment index if time has elapsed */
    - old_time = purple_str_to_time (ap_prefs_get_string (w, "last_update"), TRUE,
    - NULL, NULL, NULL);
    - cur_time = time (NULL);
    -
    - if (difftime (cur_time, old_time) >
    - 60.0 * 60.0 * (double) ap_prefs_get_int (w, "update_rate"))
    - {
    - ap_debug ("quote", "time interval elapsed, moving to new quote");
    -
    - time_string = (char *)malloc(1000);
    - t = ap_gmtime (&cur_time);
    - strftime (time_string, 999, "%Y-%m-%dT%H:%M:%S+00:00", t);
    - free (t);
    - ap_prefs_set_string (w, "last_update", time_string);
    - free (time_string);
    -
    - index++;
    - ap_prefs_set_int (w, "current_index", index);
    - }
    -
    - /* Wrap around when last quote is reached */
    - if (index >= num_quotes) {
    - index = 0;
    - ap_prefs_set_int (w, "current_index", 0);
    - }
    -
    - /* Choose and output the quote */
    - ret = strdup((gchar *) g_list_nth_data (quotes, index));
    - free_string_list (quotes);
    - return ret;
    -}
    -
    -static void quotation_init (struct widget *w)
    -{
    - time_t the_time;
    - char *time_string;
    -
    - time_string = (char *)malloc(1000);
    - the_time = time(NULL);
    - strftime (time_string, 999, "%Y-%m-%dT%H:%M:%S+00:00", gmtime (&the_time));
    -
    - ap_prefs_add_string_list (w, "quotes", NULL);
    -
    - ap_prefs_add_int (w, "current_index", 0);
    - ap_prefs_add_int (w, "update_rate", 0);
    - ap_prefs_add_string (w, "last_update", time_string);
    -
    - free (time_string);
    -}
    -
    -
    -struct component quotation =
    -{
    - N_("Quotes"),
    - N_("Displays a quotation from a provided selection"),
    - "Quote",
    - &quotation_generate,
    - &quotation_init,
    - NULL,
    - NULL,
    - NULL,
    - &quotation_menu
    -};
    -
    --- a/autoprofile/comp_rss.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,477 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "comp_rss.h"
    -
    -#include "gtkimhtml.h"
    -
    -#include <ctype.h>
    -
    -static GtkWidget *entry_username = NULL;
    -static GtkWidget *entry_url = NULL;
    -
    -GHashTable *rss_entries = NULL;
    -static GHashTable *rss_timeouts = NULL;
    -GStaticMutex rss_mutex = G_STATIC_MUTEX_INIT;
    -
    -/* Core functions */
    -static char *get_rss_data (struct widget *w, const char *field, int index,
    - struct tm **time)
    -{
    - GList *tmp;
    - const struct rss_entry *e;
    - char *ret;
    - int max;
    -
    - g_static_mutex_lock (&rss_mutex);
    - tmp = (GList *) g_hash_table_lookup (rss_entries, w);
    -
    - if (index < 0 ) {
    - g_static_mutex_unlock (&rss_mutex);
    - return strdup (_("[ERROR: Invalid entry number]"));
    - }
    -
    - if (tmp == NULL) {
    - g_static_mutex_unlock (&rss_mutex);
    - return strdup (_("[ERROR: No data, invalid URL/account?]"));
    - }
    -
    - if (index != 0) {
    - while (index-- != 1) {
    - tmp = tmp->next;
    - if (tmp == NULL) {
    - g_static_mutex_unlock (&rss_mutex);
    - return strdup (_("[ERROR: Insufficient number of entries]"));
    - }
    - }
    - }
    -
    - e = (struct rss_entry *) tmp->data;
    -
    - if (!strcmp (field, "link")) {
    - if (e->url)
    - ret = strdup (e->url);
    - else
    - ret = NULL;
    - } else if (!strcmp (field, "title")) {
    - if (e->title)
    - ret = strdup (e->title);
    - else
    - ret = NULL;
    - } else if (!strcmp (field, "entry")) {
    - if (e->entry) {
    - max = ap_prefs_get_int (w, "entry_limit");
    - ret = strdup (e->entry);
    - if (max < g_utf8_strlen (ret, -1)) {
    - gchar *tmp = g_utf8_offset_to_pointer (ret, max);
    - *tmp = '\0';
    - }
    - } else {
    - ret = NULL;
    - }
    - } else if (!strcmp (field, "time")) {
    - *time = e->t;
    - ret = NULL;
    - } else {
    - ret = NULL;
    - }
    -
    - g_static_mutex_unlock (&rss_mutex);
    - return ret;
    -}
    -
    -static char *rss_generate (struct widget *w)
    -{
    - GString *output;
    - gchar *result;
    -
    - char *tmp, *time_tmp;
    - int state;
    - int count;
    - const char *format;
    - char fmt_char [3];
    - struct tm *time;
    -
    - fmt_char[0] = '%';
    - fmt_char[2] = '\0';
    -
    - format = ap_prefs_get_string (w, "format");
    - output = g_string_new ("");
    - time_tmp = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM);
    -
    - state = 0;
    - count = 0;
    -
    - while (*format) {
    - if (state == 1) {
    - if (isdigit (*format)) {
    - count = (count * 10) + (int) *format - 48;
    - format++;
    - } else {
    - switch (*format) {
    - case 'H':
    - case 'I':
    - case 'p':
    - case 'M':
    - case 'S':
    - case 'a':
    - case 'A':
    - case 'b':
    - case 'B':
    - case 'm':
    - case 'd':
    - case 'j':
    - case 'W':
    - case 'w':
    - case 'y':
    - case 'Y':
    - case 'z':
    - time = NULL;
    - tmp = get_rss_data (w, "time", count, &time);
    - if (time) {
    - fmt_char[1] = *format;
    - strftime (time_tmp, AP_SIZE_MAXIMUM, fmt_char, time);
    - g_string_append_printf (output, "%s", time_tmp);
    - }
    - break;
    - case 'l':
    - tmp = get_rss_data (w, "link", count, NULL);
    - if (tmp) {
    - g_string_append_printf (output, "%s", tmp);
    - free (tmp);
    - }
    - break;
    - case 't':
    - tmp = get_rss_data (w, "title", count, NULL);
    - if (tmp) {
    - g_string_append_printf (output, "%s", tmp);
    - free (tmp);
    - }
    - break;
    - case 'e':
    - tmp = get_rss_data (w, "entry", count, NULL);
    - if (tmp) {
    - g_string_append_printf (output, "%s", tmp);
    - free (tmp);
    - }
    - break;
    - case '%':
    - g_string_append_printf (output, "%c", *format);
    - break;
    - default:
    - g_string_append_unichar (output, g_utf8_get_char (format));
    - break;
    - }
    - format = g_utf8_next_char (format);
    - state = 0;
    - }
    - } else {
    - if (*format == '%') {
    - state = 1;
    - count = 0;
    - } else {
    - g_string_append_unichar (output, g_utf8_get_char (format));
    - }
    - format = g_utf8_next_char (format);
    - }
    - }
    -
    - result = output->str;
    - g_string_free (output, FALSE);
    - return result;
    -}
    -
    -static gboolean rss_update (gpointer data)
    -{
    - parse_rss ((struct widget *) data);
    - return TRUE;
    -}
    -
    -static void rss_load (struct widget *w)
    -{
    - gpointer rss_timeout;
    -
    - g_static_mutex_lock (&rss_mutex);
    - if (!rss_entries) {
    - rss_entries = g_hash_table_new (NULL, NULL);
    - }
    - if (!rss_timeouts) {
    - rss_timeouts = g_hash_table_new (NULL, NULL);
    - }
    -
    - rss_timeout = GINT_TO_POINTER (g_timeout_add (
    - ap_prefs_get_int (w, "update_rate") * 60 * 1000,
    - rss_update, w));
    - g_hash_table_insert (rss_timeouts, w, rss_timeout);
    -
    - g_static_mutex_unlock (&rss_mutex);
    -
    - rss_update (w);
    -}
    -
    -static void rss_unload (struct widget *w)
    -{
    - gpointer rss_timeout;
    -
    - g_static_mutex_lock (&rss_mutex);
    - rss_timeout = g_hash_table_lookup (rss_timeouts, w);
    - g_source_remove (GPOINTER_TO_INT (rss_timeout));
    - g_hash_table_remove (rss_timeouts, w);
    -
    - g_static_mutex_unlock (&rss_mutex);
    -}
    -
    -static void rss_init (struct widget *w)
    -{
    - ap_prefs_add_int (w, "type", RSS_XANGA);
    - ap_prefs_add_string (w, "location", "");
    - ap_prefs_add_string (w, "username", "");
    - ap_prefs_add_string (w, "format",
    - "My <a href=\"%l\">blog</a> was most recently updated on "
    - "%1B %1d at %I:%M %p");
    - ap_prefs_add_int (w, "update_rate", 5);
    - ap_prefs_add_int (w, "entry_limit", 1000);
    -}
    -
    -/* GUI functions */
    -static gboolean update_refresh_rate (GtkWidget *widget, GdkEventFocus *evt,
    - struct widget *w)
    -{
    - gpointer timeout;
    - int minutes;
    -
    - minutes = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
    - ap_prefs_set_int (w, "update_rate", minutes);
    -
    - // Kill the current timer and run a new one
    - g_static_mutex_lock (&rss_mutex);
    - timeout = g_hash_table_lookup (rss_timeouts, w);
    - g_source_remove (GPOINTER_TO_INT(timeout));
    - timeout = GINT_TO_POINTER (g_timeout_add (minutes * 60 * 1000,
    - rss_update, w));
    - g_hash_table_replace (rss_timeouts, w, timeout);
    - g_static_mutex_unlock (&rss_mutex);
    -
    - return FALSE;
    -}
    -
    -static void rss_data_update (GtkWidget *widget, struct widget *w)
    -{
    - rss_update (w);
    -}
    -
    -static void sensitivity_cb (const char *name, PurplePrefType type,
    - gconstpointer val, gpointer data)
    -{
    - int real_val = GPOINTER_TO_INT (val);
    -
    - if (real_val == RSS_XANGA || real_val == RSS_LIVEJOURNAL) {
    - gtk_widget_set_sensitive (entry_username, TRUE);
    - gtk_widget_set_sensitive (entry_url, FALSE);
    - } else {
    - gtk_widget_set_sensitive (entry_username, FALSE);
    - gtk_widget_set_sensitive (entry_url, TRUE);
    - }
    -}
    -
    -static GtkWidget *entry;
    -
    -static void event_cb (GtkWidget *widget, struct widget *w)
    -{
    - ap_prefs_set_string (w, "format",
    - gtk_imhtml_get_markup (GTK_IMHTML(entry)));
    -}
    -static void formatting_toggle_cb (GtkIMHtml *imhtml,
    - GtkIMHtmlButtons buttons, struct widget *w)
    -{
    - ap_prefs_set_string (w, "format",
    - gtk_imhtml_get_markup (GTK_IMHTML(entry)));
    -
    -}
    -
    -static void formatting_clear_cb (GtkIMHtml *imhtml,
    - struct widget *w)
    -{
    - ap_prefs_set_string (w, "format",
    - gtk_imhtml_get_markup (GTK_IMHTML(entry)));
    -}
    -
    -static GtkWidget *rss_menu (struct widget *w)
    -{
    - GtkWidget *ret;
    - GList *options;
    - GtkWidget *label, *hbox, *button, *spinner, *sw;
    - GtkWidget *entry_window, *toolbar;
    - GtkTextBuffer *text_buffer;
    -
    - int value;
    - gchar *pref;
    -
    - ret = gtk_vbox_new (FALSE, 5);
    -
    - /* Format string */
    - label = gtk_label_new (NULL);
    - gtk_label_set_markup (GTK_LABEL(label), "<b>Format string for output</b>");
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(ret), label, FALSE, FALSE, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX(ret), hbox, TRUE, TRUE, 0);
    -
    - entry_window = pidgin_create_imhtml (TRUE, &entry, &toolbar, &sw);
    - gtk_box_pack_start (GTK_BOX (hbox), entry_window, TRUE, TRUE, 0);
    - gtk_imhtml_append_text_with_images (GTK_IMHTML(entry),
    - ap_prefs_get_string (w, "format"),
    - 0, NULL);
    - text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
    - g_signal_connect (G_OBJECT (text_buffer), "changed",
    - G_CALLBACK (event_cb), w);
    - g_signal_connect_after(G_OBJECT(entry), "format_function_toggle",
    - G_CALLBACK(formatting_toggle_cb), w);
    - g_signal_connect_after(G_OBJECT(entry), "format_function_clear",
    - G_CALLBACK(formatting_clear_cb), w);
    -
    - label = gtk_label_new (_(
    - "The following options can be specified with a numerical modifier\n"
    - "(i.e. \"%e\" can also be written \"%1e\" or \"%2e\"). The optional\n"
    - "number specifies which entry to get the data for. \"1\" refers to the\n"
    - "most recent entry, \"2\" refers to the second-most recent entry, and so\n"
    - "forth. \"1\" is used if no number is specified.\n\n"
    - "%e\tStarting text of the entry.\n"
    - "%l\tLink to the specific entry.\n"
    - "%t\tTitle of entry (Xanga incompatible)\n"
    -
    - "\nTime of entry:\n"
    - "%H\thour of entry(24-hour clock)\n"
    - "%I\thour (12-hour clock)\n"
    - "%p\tAM or PM\n"
    - "%M\tminute\n"
    - "%S\tsecond\n"
    - "%a\tabbreviated weekday name\n"
    - "%A\tfull weekday name\n"
    - "%b\tabbreviated month name\n"
    - "%B\tfull month name\n"
    - "%m\tmonth (numerical)\n"
    - "%d\tday of the month\n"
    - "%j\tday of the year\n"
    - "%W\tweek number of the year\n"
    - "%w\tweekday (numerical)\n"
    - "%y\tyear without century\n"
    - "%Y\tyear with century\n"
    - "%z\ttime zone name, if any\n"
    - "%%\t%"));
    -
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - sw = gtk_scrolled_window_new (NULL,NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
    - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
    - gtk_box_pack_start (GTK_BOX (hbox), sw, TRUE, TRUE , 0);
    - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(sw), label);
    -
    - /* Type/URL/Username selection */
    - label = gtk_label_new (NULL);
    - gtk_label_set_markup (GTK_LABEL(label), "<b>RSS/Blog location</b>");
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(ret), label, FALSE, FALSE, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX(ret), hbox, FALSE, FALSE, 0);
    -
    - /* Dropdown */
    - options = g_list_append (NULL, (char *) _("Xanga"));
    - options = g_list_append (options, GINT_TO_POINTER(RSS_XANGA));
    - options = g_list_append (options, (char *) _("LiveJournal"));
    - options = g_list_append (options, GINT_TO_POINTER(RSS_LIVEJOURNAL));
    - options = g_list_append (options, (char *) _("RSS 2.0"));
    - options = g_list_append (options, GINT_TO_POINTER(RSS_2));
    - ap_prefs_dropdown_from_list (w, hbox, NULL, PURPLE_PREF_INT, "type", options);
    - g_list_free (options);
    -
    - pref = ap_prefs_get_pref_name (w, "type");
    - purple_prefs_connect_callback (ap_get_plugin_handle (), pref,
    - sensitivity_cb, w);
    - free (pref);
    -
    - /* Username/URL fields */
    - entry_username = ap_prefs_labeled_entry (w, hbox, _("Username:"),
    - "username", NULL);
    - entry_url = ap_prefs_labeled_entry (w, hbox, _("URL of feed:"),
    - "location", NULL);
    -
    - value = ap_prefs_get_int (w, "type");
    - if (value == RSS_XANGA || value == RSS_LIVEJOURNAL) {
    - gtk_widget_set_sensitive (entry_username, TRUE);
    - gtk_widget_set_sensitive (entry_url, FALSE);
    - } else {
    - gtk_widget_set_sensitive (entry_username, FALSE);
    - gtk_widget_set_sensitive (entry_url, TRUE);
    - }
    -
    - /* Other options */
    - label = gtk_label_new (NULL);
    - gtk_label_set_markup (GTK_LABEL(label), "<b>Other options</b>");
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(ret), label, FALSE, FALSE, 0);
    -
    - /* # of chars to display from description */
    - ap_prefs_labeled_spin_button (w, ret,
    - "Max characters to show in entry (%e)", "entry_limit", 1,
    - AP_SIZE_MAXIMUM - 1, NULL);
    -
    - /* Update rate selection */
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new (_("Minutes between checks for updates:"));
    - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    -
    - spinner = gtk_spin_button_new_with_range (1, 60, 1);
    - gtk_box_pack_start (GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
    - ap_prefs_get_int (w, "update_rate"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_refresh_rate), w);
    -
    - button = gtk_button_new_with_label ("Fetch data now!");
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (rss_data_update), w);
    - gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
    -
    - return ret;
    -}
    -
    -/* Le end */
    -struct component rss =
    -{
    - N_("RSS / Blogs"),
    - N_("Information taken from an RSS feed (Xanga and LiveJournal capable)"),
    - "RSS",
    - &rss_generate,
    - &rss_init,
    - &rss_load,
    - &rss_unload,
    - NULL,
    - &rss_menu
    -};
    -
    --- a/autoprofile/comp_rss.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,52 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "component.h"
    -#include "time.h"
    -
    -#define MAX_USERNAME_LENGTH 1000
    -
    -struct rss_entry {
    - struct tm *t;
    - char *title;
    - char *entry;
    - char *url;
    - char *comments;
    -};
    -
    -typedef enum
    -{
    - RSS_UNKNOWN = -1,
    - RSS_XANGA,
    - RSS_LIVEJOURNAL,
    - RSS_2
    -} RSS_TYPE;
    -
    -extern GHashTable *rss_entries;
    -extern GStaticMutex rss_mutex;
    -
    -void parse_rss (struct widget *);
    -void parse_xanga_rss (struct widget *, gchar *);
    -extern GMarkupParser rss_parser;
    -
    -
    --- a/autoprofile/comp_rss_parser.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,352 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "comp_rss.h"
    -#include "utility.h"
    -
    -#include <string.h>
    -
    -static gboolean parsing_rss = FALSE;
    -static gboolean parsing_item = FALSE;
    -
    -static gboolean item_title = FALSE;
    -static gboolean item_link = FALSE;
    -static gboolean item_description = FALSE;
    -static gboolean item_comments = FALSE;
    -static gboolean item_pubdate = FALSE;
    -
    -/* Get URL of RSS feed */
    -static char *get_url (struct widget *w)
    -{
    - int type;
    - char *ret;
    - const char *username;
    - GString *s;
    -
    - type = ap_prefs_get_int (w, "type");
    - s = g_string_new ("");
    -
    - switch (type) {
    - case RSS_LIVEJOURNAL:
    - username = ap_prefs_get_string (w, "username");
    - g_string_append_printf (s,
    - "http://www.livejournal.com/users/%s/data/rss", username);
    - break;
    - case RSS_XANGA:
    - username = ap_prefs_get_string (w, "username");
    - g_string_append_printf (s, "http://www.xanga.com/%s/rss", username);
    - break;
    - case RSS_2:
    - g_string_append_printf (s, "%s", ap_prefs_get_string (w, "location"));
    - break;
    - default:
    - break;
    - }
    -
    - ret = s->str;
    - g_string_free (s, FALSE);
    - return ret;
    -}
    -
    -/* Utility functions */
    -static void free_entries (struct widget *w) {
    - GList *tmp, *entries;
    - struct rss_entry *e;
    -
    - entries = (GList *) g_hash_table_lookup (rss_entries, w);
    -
    - while (entries) {
    - e = (struct rss_entry *) entries->data;
    - if (e->title)
    - free (e->title);
    - if (e->entry)
    - free (e->entry);
    - if (e->url)
    - free (e->url);
    - if (e->comments)
    - free (e->comments);
    - if (e->t)
    - free (e->t);
    -
    - free (e);
    - tmp = entries;
    - entries = entries->next;
    - g_list_free_1 (tmp);
    - }
    - g_hash_table_replace (rss_entries, w, NULL);
    -}
    -
    -/* Date parsing functions */
    -static struct tm *parse_date_rfc822 (const char *date_string)
    -{
    - time_t t, gmt_time, local_time;
    - struct tm *ret, *result;
    -
    - local_time = time(NULL);
    - gmt_time = time(NULL);
    - gmt_time = mktime(gmtime(&gmt_time));
    -
    - // TODO: Change this to GDate
    -
    - t = rfc_parse_date_time (date_string);
    - // if (rfc_parse_was_gmt ()) {
    - // FIXME: Handle time zones
    - // } else {
    - ret = (struct tm *) malloc (sizeof (struct tm));
    - result = localtime(&t);
    - ret->tm_sec = result->tm_sec;
    - ret->tm_min = result->tm_min;
    - ret->tm_hour = result->tm_hour;
    - ret->tm_mday = result->tm_mday;
    - ret->tm_mon = result->tm_mon;
    - ret->tm_year = result->tm_year;
    - //}
    -
    - return ret;
    -}
    -
    -/* XML Parser Callbacks */
    -static void start_element_handler (GMarkupParseContext *context,
    - const gchar *element_name,
    - const gchar **attribuate_names,
    - const gchar **attribute_values,
    - gpointer data, GError **error)
    -{
    - struct rss_entry *e;
    - GList *entries;
    - struct widget *w = (struct widget *) data;
    -
    - //printf ("start:%s\n", element_name);
    -
    - if (!parsing_rss && !strcmp (element_name, "rss"))
    - parsing_rss = TRUE;
    -
    - else if (parsing_rss && !parsing_item &&
    - !strcmp (element_name, "item")) {
    - parsing_item = TRUE;
    - e = (struct rss_entry *) malloc (sizeof(struct rss_entry));
    - entries = (GList *) g_hash_table_lookup (rss_entries, w);
    - entries = g_list_prepend (entries, e);
    - g_hash_table_replace (rss_entries, w, entries);
    - e->t = NULL;
    - e->title = NULL;
    - e->entry = NULL;
    - e->url = NULL;
    - e->comments = NULL;
    - }
    -
    - else if (parsing_item && !strcmp (element_name, "title"))
    - item_title = TRUE;
    - else if (parsing_item && !strcmp (element_name, "link"))
    - item_link = TRUE;
    - else if (parsing_item && !strcmp (element_name, "description"))
    - item_description = TRUE;
    - else if (parsing_item && !strcmp (element_name, "comments"))
    - item_comments = TRUE;
    - else if (parsing_item && !strcmp (element_name, "pubDate"))
    - item_pubdate = TRUE;
    -}
    -
    -static void end_element_handler (GMarkupParseContext *context,
    - const gchar *element_name,
    - gpointer data, GError **error)
    -{
    - struct widget *w = (struct widget *) w;
    - //printf ("end:%s\n", element_name);
    -
    - if (!strcmp (element_name, "rss"))
    - parsing_rss = FALSE;
    - else if (!strcmp (element_name, "item"))
    - parsing_item = FALSE;
    -
    - else if (!strcmp (element_name, "title"))
    - item_title = FALSE;
    - else if (!strcmp (element_name, "link"))
    - item_link = FALSE;
    - else if (!strcmp (element_name, "description"))
    - item_description = FALSE;
    - else if (!strcmp (element_name, "comments"))
    - item_comments = FALSE;
    - else if (!strcmp (element_name, "pubDate"))
    - item_pubdate = FALSE;
    -}
    -
    -static void text_handler (GMarkupParseContext *context,
    - const gchar *text, gsize text_len,
    - gpointer data, GError **error)
    -{
    - struct rss_entry *e;
    - GList *entries;
    - struct widget *w = (struct widget *) data;
    -
    - entries = (GList *) g_hash_table_lookup (rss_entries, w);
    -
    - if (entries == NULL) {
    - return;
    - }
    -
    - e = (struct rss_entry *) entries->data;
    -
    - if (item_link) {
    - if (e->url) {
    - free (e->url);
    - }
    - e->url = g_strdup (text);
    - }
    -
    - else if (item_description) {
    - if (e->entry) {
    - free (e->entry);
    - }
    - e->entry = purple_unescape_html (text);
    -
    - // If there is a standard format for Xanga titles (there really isn't)
    - // it will probably be devised from the actual content. Will be placed
    - // here if there is proven demand.
    - }
    -
    - else if (item_comments) {
    - if (e->comments) {
    - free (e->comments);
    - }
    - e->comments = g_strdup (text);
    - }
    -
    - else if (item_title) {
    - if (e->title) {
    - free (e->title);
    - }
    - e->title = g_strdup (text);
    - }
    -
    - else if (item_pubdate) {
    - if (e->t) {
    - free (e->t);
    - }
    - e->t = parse_date_rfc822 (text);
    - }
    -}
    -
    -/* Final parser variable */
    -GMarkupParser rss_parser =
    -{
    - start_element_handler,
    - end_element_handler,
    - text_handler,
    - NULL,
    - NULL
    -};
    -
    -/* Callback for HTTP data fetcher */
    -static void url_callback (PurpleUtilFetchUrlData *url_data, gpointer data,
    - const char *text, size_t size, const gchar *error_message)
    -{
    - GMarkupParseContext *context;
    - gchar *filtered_text = NULL, *convert = NULL, *next = NULL;
    - GError *err = NULL;
    - GList *entries;
    - struct widget *w = (struct widget *) data;
    -
    - /* Make sure URL exists/connected to Internet */
    - if (text == NULL) {
    - ap_debug ("rss", "error, unable to fetch page via internet");
    - return;
    - }
    -
    - parsing_rss = FALSE;
    - parsing_item = FALSE;
    -
    - item_title = FALSE;
    - item_link = FALSE;
    - item_description = FALSE;
    - item_comments = FALSE;
    - item_pubdate = FALSE;
    -
    - g_static_mutex_lock (&rss_mutex);
    -
    - free_entries (w);
    -
    - // Sanity checking
    - filtered_text = purple_utf8_salvage (text);
    -
    - *convert = purple_utf8_try_convert ("<");
    - *next = g_utf8_strchr (filtered_text, 10, g_utf8_get_char (convert));
    - free (convert);
    -
    - if (next == NULL) {
    - free (filtered_text);
    - // TODO: error out
    - g_static_mutex_unlock (&rss_mutex);
    - return;
    - }
    -
    - if (ap_prefs_get_int (w, "type") == RSS_XANGA) {
    - parse_xanga_rss (w, filtered_text);
    - entries = (GList *) g_hash_table_lookup (rss_entries, w);
    - entries = g_list_reverse (entries);
    - g_hash_table_replace (rss_entries, w, entries);
    - g_static_mutex_unlock (&rss_mutex);
    - free (filtered_text);
    - return;
    - }
    -
    - context = g_markup_parse_context_new (&rss_parser, 0, w, NULL);
    -
    - if (!g_markup_parse_context_parse (context, next, size, &err)) {
    - g_markup_parse_context_free (context);
    - ap_debug ("rss", "error, unable to start parser");
    - ap_debug ("rss", err->message);
    - free (filtered_text);
    - return;
    - }
    -
    - if (!g_markup_parse_context_end_parse (context, &err)) {
    - g_markup_parse_context_free (context);
    - ap_debug ("rss", "error, unable to end parser");
    - free (filtered_text);
    - return;
    - }
    -
    - g_markup_parse_context_free (context);
    -
    - entries = (GList *) g_hash_table_lookup (rss_entries, w);
    - entries = g_list_reverse (entries);
    - g_hash_table_replace (rss_entries, w, entries);
    - g_static_mutex_unlock (&rss_mutex);
    -
    - free (filtered_text);
    -}
    -
    -void parse_rss (struct widget *w)
    -{
    - char *url;
    -
    - url = get_url (w);
    - if (strcmp (url, "") != 0) {
    - purple_util_fetch_url (url, TRUE, NULL, FALSE, url_callback, w);
    - }
    - free (url);
    -}
    -
    -
    --- a/autoprofile/comp_rss_xanga.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,118 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include <stdlib.h>
    -
    -#include <time.h>
    -#include <string.h>
    -#include <stdio.h>
    -
    -#include "autoprofile.h"
    -#include "comp_rss.h"
    -#include <glib.h>
    -
    -static gchar *convert_char;
    -
    -
    -static gchar *find_next_tag (gchar *text) {
    - *convert_char = '<';
    - return g_utf8_strchr (text, -1, g_utf8_get_char (convert_char));
    -}
    -
    -static gchar *find_end_of_tag (gchar *text) {
    - *convert_char = '>';
    - return g_utf8_strchr (text, -1, g_utf8_get_char (convert_char));
    -}
    -
    -static gboolean starts_with (gchar *text, gchar letter) {
    - *convert_char = letter;
    - return (g_utf8_strchr (text, 1, g_utf8_get_char (convert_char)) == text);
    -}
    -
    -void parse_xanga_rss (struct widget *w, gchar *text) {
    - gchar *next_tag, *end_prev_tag;
    - gchar *tag_first_char, *tag_second_char;
    - gboolean is_item;
    -
    - convert_char = (gchar *) malloc (sizeof(gchar) * 2);
    - *(convert_char+1) = '\0';
    -
    - end_prev_tag = text;
    - is_item = FALSE;
    -
    - rss_parser.start_element (NULL, "rss", NULL, NULL, w, NULL);
    -
    - while ((next_tag = find_next_tag (end_prev_tag)) != NULL) {
    - tag_first_char = g_utf8_next_char (next_tag);
    - tag_second_char = g_utf8_next_char (tag_first_char);
    -
    - if (is_item) {
    - if (starts_with (tag_first_char, 't')) rss_parser.start_element (
    - NULL, "title", NULL, NULL, w, NULL);
    - else if (starts_with (tag_first_char, 'l')) rss_parser.start_element (
    - NULL, "link", NULL, NULL, w, NULL);
    - else if (starts_with (tag_first_char, 'p')) rss_parser.start_element (
    - NULL, "pubDate", NULL, NULL, w, NULL);
    - else if (starts_with (tag_first_char, 'd')) rss_parser.start_element (
    - NULL, "description", NULL, NULL, w, NULL);
    - else if (starts_with (tag_first_char, 'c')) rss_parser.start_element (
    - NULL, "comments", NULL, NULL, w, NULL);
    - else if (starts_with (tag_first_char, '/')) {
    - *next_tag = '\0';
    - rss_parser.text (NULL, end_prev_tag, -1, w, NULL);
    -
    - if (starts_with (tag_second_char, 't'))
    - rss_parser.end_element (NULL, "title", w, NULL);
    - else if (starts_with (tag_second_char, 'l'))
    - rss_parser.end_element (NULL, "link", w, NULL);
    - else if (starts_with (tag_second_char, 'p'))
    - rss_parser.end_element (NULL, "pubDate", w, NULL);
    - else if (starts_with (tag_second_char, 'd'))
    - rss_parser.end_element (NULL, "description", w, NULL);
    - else if (starts_with (tag_second_char, 'c'))
    - rss_parser.end_element (NULL, "comments", w, NULL);
    - else if (starts_with (tag_second_char, 'i')) {
    - rss_parser.end_element (NULL, "item", w, NULL);
    - is_item = FALSE;
    - } else {
    - // TODO: WARN USER IN THIS CASE
    - }
    - }
    - } else {
    - if (starts_with (tag_first_char, 'i') &&
    - starts_with (tag_second_char, 't')) {
    - is_item = TRUE;
    - rss_parser.start_element (NULL, "item", NULL, NULL, w, NULL);
    - }
    - }
    -
    - if ((next_tag = find_end_of_tag (tag_first_char)) == NULL) {
    - // TODO: NOTIFY USER THAT WE REACHED END
    - return;
    - }
    - end_prev_tag = g_utf8_next_char (next_tag);
    - }
    -
    - free (convert_char);
    -}
    -
    --- a/autoprofile/comp_textfile.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,268 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "../common/pp_internal.h"
    -
    -#include "component.h"
    -
    -#include <string.h>
    -
    -/*---------- TEXT FILE: Text from a file ----------*/
    -static GtkWidget *file_entry;
    -static GtkWidget *file_selector;
    -
    -/* Read file into string and return */
    -char *text_file_generate (struct widget *w)
    -{
    - gchar *text, *salvaged, *converted;
    - const gchar *filename;
    - int max = ap_prefs_get_int (w, "text_size");
    -
    - text = NULL;
    - filename = ap_prefs_get_string (w, "text_file");
    -
    - if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
    - return g_strdup (_("[ERROR: File does not exist]"));
    - }
    -
    - if (!g_file_get_contents (filename, &text, NULL, NULL)) {
    - return g_strdup (_("[ERROR: Unable to open file]"));
    - }
    -
    - converted = purple_utf8_try_convert (text);
    - if (converted != NULL) {
    - g_free (text);
    - text = converted;
    - }
    -
    - if (strlen (text) > max) {
    - *(text+max) = '\0';
    - }
    -
    - salvaged = purple_utf8_salvage (text);
    - g_free (text);
    -
    - return salvaged;
    -}
    -
    -static gboolean text_file_update (GtkWidget *widget, GdkEventFocus *evt,
    - struct widget *w)
    -{
    - ap_prefs_set_string (w, "text_file",
    - gtk_entry_get_text (GTK_ENTRY (file_entry)));
    - return FALSE;
    -}
    -
    -static void text_file_filename (GtkWidget *widget, gpointer user_data) {
    - const gchar *selected_filename;
    - struct widget *w = (struct widget *) user_data;
    -
    - selected_filename = gtk_file_selection_get_filename (
    - GTK_FILE_SELECTION (file_selector));
    -
    - ap_prefs_set_string (w, "text_file", selected_filename);
    - gtk_entry_set_text (GTK_ENTRY (file_entry), selected_filename);
    -}
    -
    -/* Creates and pops up file selection dialog for fortune file */
    -static void text_file_selection (GtkWidget *widget, gpointer user_data) {
    - const char *cur_file;
    - struct widget *w = (struct widget *) user_data;
    -
    - /* Create the selector */
    - file_selector = gtk_file_selection_new (
    - "Select a text file with content");
    -
    - cur_file = ap_prefs_get_string (w, "text_file");
    - if (cur_file && strlen (cur_file) > 1) {
    - gtk_file_selection_set_filename (
    - GTK_FILE_SELECTION (file_selector), cur_file);
    - }
    -
    - g_signal_connect (GTK_OBJECT(
    - GTK_FILE_SELECTION(file_selector)->ok_button),
    - "clicked", G_CALLBACK (text_file_filename), w);
    -
    - /* Destroy dialog box when the user clicks button. */
    - g_signal_connect_swapped (GTK_OBJECT(
    - GTK_FILE_SELECTION(file_selector)->ok_button),
    - "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector);
    -
    - g_signal_connect_swapped (GTK_OBJECT (
    - GTK_FILE_SELECTION (file_selector)->cancel_button),
    - "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector);
    -
    - /* Display dialog */
    - gtk_widget_show (file_selector);
    -}
    -
    -/* Pop up message with instructions */
    -static void text_file_info_button (GtkButton *button, gpointer data)
    -{
    - if (!strcmp ((char *) data, "itunes")) {
    - purple_notify_formatted (NULL, _("iTunes"), _("Current song in iTunes"), NULL,
    - _("Get TuneCam from <a href=\""
    - "http://www.soft-o-mat.com/productions.shtml\">"
    - "http://www.soft-o-mat.com/productions.shtml</a> and start it.<br>"
    - "Create a html file that contains the following text:<br><br>&lt;tc"
    - "&gt;artist&lt;/tc&gt; - &lt;tc&gt;title&lt;/tc&gt;<br><br>and "
    - "press the \"T\" button. Import the html file as a template for "
    - "the \"File Track\" and whatever else you see fit. Then select "
    - "the \"G\" button and choose the location of the output file which "
    - "will be used in this component"),
    - NULL, NULL);
    - } else if (!strcmp ((char *) data, "xmms")) {
    - purple_notify_formatted (NULL, _("XMMS"), _("Current song in XMMS"), NULL,
    - _("Included in the misc folder of AutoProfile is a script called \""
    - "xmms_currenttrack\". Install this script in your $PATH and give it "
    - "executable permissions, and specify the program using a pipe.<br><br>"
    - "Alternatively, in XMMS, go to Options->Preferences->Effects/General "
    - "Plugins.<br>Configure the \"Song Change\" plugin. In the song change"
    - " command box, put<br><br>echo \"%s\" > /path/to/output/file<br><br>"
    - "and be sure to enable the plugin. Select the file location in "
    - "AutoProfile and you should be done"),
    - NULL, NULL);
    - } else if (!strcmp ((char *) data, "wmp")) {
    - purple_notify_formatted (NULL, _("Windows Media Player"),
    - _("Current song in Windows Media Player"), NULL,
    - _("Download NowPlaying, a plugin for WMP from <a href=\""
    - "http://www.wmplugins.com/ItemDetail.aspx?ItemID=357\">"
    - "http://www.wmplugins.com/ItemDetail.aspx?ItemID=357</a> and follow "
    - "the included installation instructions.<br>Set the output filename "
    - "to the file you choose in this component"),
    - NULL, NULL);
    - } else if (!strcmp ((char *) data, "amip")) {
    - purple_notify_formatted (NULL, _("iTunes/Winamp/Foobar/Apollo/QCD"),
    - _("Current song in iTunes/Winamp/Foobar/Apollo/QCD"), NULL,
    - _("Get the version of AMIP associated with your player from <a href=\""
    - "http://amip.tools-for.net/\">"
    - "http://amip.tools-for.net/</a> and install/"
    - "enable it.<br>"
    - "Check the box \"Write song info to file\", play with the settings, "
    - "and set the file in this component to be the file in the AMIP "
    - "options."),
    - NULL, NULL);
    - }
    -}
    -
    -/* Create the menu */
    -GtkWidget *text_file_menu (struct widget *w)
    -{
    - GtkWidget *ret = gtk_vbox_new (FALSE, 5);
    - GtkWidget *hbox, *label, *button;
    -
    - label = gtk_label_new (_("Select text file with source content"));
    - gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    - /* Text entry to type in file name */
    - file_entry = gtk_entry_new ();
    - gtk_box_pack_start (GTK_BOX (hbox), file_entry, FALSE, FALSE, 0);
    - gtk_entry_set_text (GTK_ENTRY (file_entry),
    - ap_prefs_get_string (w, "text_file"));
    - g_signal_connect (G_OBJECT (file_entry), "focus-out-event",
    - G_CALLBACK (text_file_update), w);
    - /* Button to bring up file select dialog */
    - button = gtk_button_new_with_label ("Browse ...");
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (text_file_selection), w);
    -
    - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
    -
    - ap_prefs_labeled_spin_button (w, ret,
    - _("Max characters to read from file:"), "text_size",
    - 1, AP_SIZE_MAXIMUM - 1, NULL);
    -
    - gtk_box_pack_start (GTK_BOX (ret),
    - gtk_hseparator_new (), 0, 0, 0);
    -
    - /* Windows */
    - label = gtk_label_new (_("Windows users: Play the current song in:"));
    - gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label ("iTunes/Winamp/Foobar/Apollo/QCD");
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (text_file_info_button), "amip");
    - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label ("Windows Media Player");
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (text_file_info_button), "wmp");
    - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    -
    - /* *nix */
    - label = gtk_label_new (_("*nix users: Play the current song in:"));
    - gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label ("XMMS");
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (text_file_info_button), "xmms");
    - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
    -
    - /* OS X */
    - label = gtk_label_new (_("OS X users: Play the current song in:"));
    - gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label ("iTunes");
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (text_file_info_button), "itunes");
    - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
    -
    - return ret;
    -}
    -
    -void text_file_init (struct widget *w) {
    - ap_prefs_add_string (w, "text_file", "");
    - ap_prefs_add_int (w, "text_size", AP_SIZE_MAXIMUM - 1);
    -}
    -
    -struct component text =
    -{
    - N_("Text File / Songs"),
    - N_("Copies text from file that external programs "
    - "(e.g. XMMS, Winamp, iTunes) can modify on a regular basis"),
    - "File",
    - &text_file_generate,
    - &text_file_init,
    - NULL,
    - NULL,
    - NULL,
    - &text_file_menu
    -};
    -
    --- a/autoprofile/comp_timestamp.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,142 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "../common/pp_internal.h"
    -
    -#include "component.h"
    -#include "utility.h"
    -
    -#include "gtkimhtml.h"
    -
    -/*---------- TIMESTAMP: Display time at creation ---------------------------*/
    -static char *timestamp_generate (struct widget *w) {
    - struct tm *cur_time;
    - char *ret;
    -
    - time_t *general_time = (time_t *)malloc(sizeof(time_t));
    - time (general_time);
    - cur_time = ap_localtime (general_time);
    - free (general_time);
    -
    - ret = (char *)malloc (AP_SIZE_MAXIMUM);
    - *ret = '\0';
    -
    - strftime (ret, AP_SIZE_MAXIMUM - 1,
    - ap_prefs_get_string (w, "timestamp_format"),
    - cur_time);
    -
    - free (cur_time);
    - return ret;
    -}
    -
    -static void timestamp_init (struct widget *w) {
    - ap_prefs_add_string (w, "timestamp_format",
    - "Automatically created at %I:%M %p");
    -}
    -
    -static GtkWidget *entry;
    -
    -static void event_cb (GtkWidget *widget, struct widget *w)
    -{
    - ap_prefs_set_string (w, "timestamp_format",
    - gtk_imhtml_get_markup (GTK_IMHTML(entry)));
    -}
    -
    -static void formatting_toggle_cb (GtkIMHtml *imhtml,
    - GtkIMHtmlButtons buttons, struct widget *w)
    -{
    - ap_prefs_set_string (w, "timestamp_format",
    - gtk_imhtml_get_markup (GTK_IMHTML(entry)));
    -
    -}
    -
    -static void formatting_clear_cb (GtkIMHtml *imhtml,
    - struct widget *w)
    -{
    - ap_prefs_set_string (w, "timestamp_format",
    - gtk_imhtml_get_markup (GTK_IMHTML(entry)));
    -}
    -
    -
    -static GtkWidget *timestamp_menu (struct widget *w)
    -{
    - GtkWidget *ret = gtk_vbox_new (FALSE, 5);
    - GtkWidget *label, *sw;
    -
    - GtkWidget *entry_window, *toolbar;
    - GtkTextBuffer *text_buffer;
    -
    - entry_window = pidgin_create_imhtml (TRUE, &entry, &toolbar, &sw);
    - gtk_box_pack_start (GTK_BOX (ret), entry_window, FALSE, FALSE, 0); gtk_imhtml_append_text_with_images (GTK_IMHTML(entry),
    - ap_prefs_get_string (w, "timestamp_format"),
    - 0, NULL);
    - text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
    - g_signal_connect (G_OBJECT (text_buffer), "changed",
    - G_CALLBACK (event_cb), w);
    - g_signal_connect_after(G_OBJECT(entry), "format_function_toggle",
    - G_CALLBACK(formatting_toggle_cb), w);
    - g_signal_connect_after(G_OBJECT(entry), "format_function_clear",
    - G_CALLBACK(formatting_clear_cb), w);
    -
    - label = gtk_label_new (_(
    - "Insert the following characters where time is to be displayed:\n\n"
    - "%H\thour (24-hour clock)\n"
    - "%I\thour (12-hour clock)\n"
    - "%p\tAM or PM\n"
    - "%M\tminute\n"
    - "%S\tsecond\n"
    - "%a\tabbreviated weekday name\n"
    - "%A\tfull weekday name\n"
    - "%b\tabbreviated month name\n"
    - "%B\tfull month name\n"
    - "%m\tmonth (numerical)\n"
    - "%d\tday of the month\n"
    - "%j\tday of the year\n"
    - "%W\tweek number of the year\n"
    - "%w\tweekday (numerical)\n"
    - "%y\tyear without century\n"
    - "%Y\tyear with century\n"
    - "%z\ttime zone name, if any\n"
    - "%%\t%" ));
    - sw = gtk_scrolled_window_new (NULL,NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
    - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
    - gtk_box_pack_start (GTK_BOX (ret), sw, TRUE, TRUE , 0);
    - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(sw), label);
    -
    - return ret;
    -}
    -
    -struct component timestamp =
    -{
    - N_("Timestamp"),
    - N_("Displays custom text showing when message was created"),
    - "Timestamp",
    - &timestamp_generate,
    - &timestamp_init,
    - NULL,
    - NULL,
    - NULL,
    - &timestamp_menu
    -};
    -
    --- a/autoprofile/comp_uptime.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,100 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "../common/pp_internal.h"
    -
    -#include "component.h"
    -#include "utility.h"
    -
    -#include <string.h>
    -
    -/*---------- UPTIME: Display the computer uptime --*/
    -char *uptime_generate (struct widget *w) {
    - gboolean exec;
    - char *out, *line, *working;
    - char *p_character, *colon_character, *comma_character, *m_character;
    - GError *return_error;
    -
    - line = N_("uptime");
    -
    - exec = g_spawn_command_line_sync (line,
    - &out, NULL, NULL, &return_error);
    - /* Parse the output */
    - if (exec) {
    - /* Buffer length for safety */
    - working = (char *)malloc (strlen (out)+7+8+8+1);
    - strcpy (working, "Uptime:");
    - /* Break into minutes, hours, and everything else */
    - p_character = strchr (out, 'p');
    - m_character = strchr (p_character, 'm');
    -
    - /* Uptime format including "pm" */
    - if (m_character != NULL && m_character == p_character + 1) {
    - p_character = strchr (m_character, 'p');
    - m_character = strchr (p_character, 'm');
    - }
    -
    - /* Uptime if < 1 hour */
    - if (m_character != NULL && *(m_character+1) == 'i') {
    - *m_character = '\0';
    - p_character++;
    - strcat (working, p_character);
    - strcat (working, "minutes");
    -
    - /* General uptime */
    - } else {
    - colon_character = strchr (p_character, ':');
    - comma_character = strchr (colon_character, ',');
    - p_character++;
    - *colon_character++ = '\0';
    - *comma_character = '\0';
    - /* Yank it all together */
    - strcat (working, p_character);
    - strcat (working, " hours, ");
    - strcat (working, colon_character);
    - strcat (working, " minutes");
    - }
    -
    - free (out);
    - return working;
    - } else {
    - ap_debug ("uptime", "command failed to execute");
    - return g_strdup(_("[ERROR: failed to execute uptime command]"));
    - return NULL;
    - }
    -}
    -
    -struct component uptime =
    -{
    - N_("Uptime"),
    - N_("Show how long your computer has been running"),
    - "Uptime",
    - &uptime_generate,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -
    --- a/autoprofile/component.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,89 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "component.h"
    -
    -#include <string.h>
    -
    -static GList *components = NULL;
    -
    -static GList *get_components ();
    -
    -void ap_component_start () {
    - if (components) g_list_free (components);
    - components = get_components ();
    -
    - ap_widget_start ();
    -}
    -void ap_component_finish () {
    - ap_widget_finish ();
    -
    - g_list_free (components);
    - components = NULL;
    -}
    -
    -GList *ap_component_get_components () {
    - return components;
    -}
    -
    -struct component *ap_component_get_component (const gchar *identifier) {
    - GList *comps;
    - struct component *cur_comp;
    -
    - for (comps = components; comps != NULL; comps = comps->next) {
    - cur_comp = (struct component *) comps->data;
    -
    - if (!strcmp (cur_comp->identifier, identifier))
    - return cur_comp;
    - }
    -
    - return NULL;
    -}
    -
    -static GList *get_components ()
    -{
    - GList *ret = NULL;
    - /* Add each listed component */
    -
    - /*
    - XXX BEFORE YOU UNCOMMENT THIS, FIX THE IDENTIFIERS in logstats
    - ret = g_list_append (ret, &logstats);
    - */
    -
    - ret = g_list_append (ret, &text);
    - ret = g_list_append (ret, &quotation);
    - ret = g_list_append (ret, &rss);
    - ret = g_list_append (ret, &timestamp);
    - ret = g_list_append (ret, &http);
    - ret = g_list_append (ret, &count);
    -
    - #ifndef _WIN32
    - ret = g_list_append (ret, &executable);
    - ret = g_list_append (ret, &uptime);
    - #endif
    -
    - /* Return */
    - return ret;
    -}
    -
    --- a/autoprofile/component.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,77 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#ifndef _AP_COMPONENT_H_
    -#define _AP_COMPONENT_H_
    -
    -#ifdef HAVE_CONFIG_H
    -# include "../pp_config.h"
    -#endif
    -
    -#include "widget.h"
    -
    -#include "sizes.h"
    -
    -#include "prefs.h"
    -#include "notify.h"
    -#include "util.h"
    -
    -#include "pidgin.h"
    -#include "gtkutils.h"
    -
    -/* A component is composed of code that generates some sort of content,
    - and a widget is a specific _instance_ of a component */
    -struct widget;
    -
    -struct component {
    - char *name;
    - char *description;
    - char *identifier;
    - char *(*generate)(struct widget *);
    - void (*init_pref)(struct widget *);
    - void (*load)(struct widget *);
    - void (*unload)(struct widget *);
    - gboolean (*has_content_changed)(struct widget *);
    - GtkWidget *(*pref_menu)(struct widget *);
    -};
    -
    -void ap_component_start ();
    -void ap_component_finish ();
    -
    -GList *ap_component_get_components ();
    -struct component *ap_component_get_component (const gchar *);
    -
    -/* TEMP
    -extern struct component logstats;
    -*/
    -
    -extern struct component count;
    -extern struct component executable;
    -extern struct component http;
    -extern struct component quotation;
    -extern struct component rss;
    -extern struct component text;
    -extern struct component timestamp;
    -extern struct component uptime;
    -
    -#endif /* _AP_COMPONENT_H_ */
    --- a/autoprofile/gtk_actions.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,341 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "gtkimhtml.h"
    -#include "gtkimhtmltoolbar.h"
    -
    -static GtkWidget *current_profile = NULL;
    -static GtkWidget *accounts_dialog = NULL;
    -static GtkWidget *content_win = NULL;
    -
    -/*--------------------------------------------------------------------------*
    - * Accounts edit popup window *
    - *--------------------------------------------------------------------------*/
    -static void accounts_response_cb (GtkWidget *d, int response, gpointer data)
    -{
    - gtk_widget_destroy (accounts_dialog);
    - accounts_dialog = NULL;
    -}
    -
    -static void display_accounts_dialog () {
    - GtkWidget *label;
    -
    - if (accounts_dialog != NULL) {
    - gtk_window_present (GTK_WINDOW (accounts_dialog));
    - return;
    - }
    -
    - accounts_dialog = gtk_dialog_new_with_buttons (_("Edit Profile Accounts"),
    - NULL, GTK_DIALOG_NO_SEPARATOR, NULL, NULL);
    - gtk_dialog_set_has_separator (GTK_DIALOG(accounts_dialog), TRUE);
    -
    - gtk_dialog_add_button (GTK_DIALOG(accounts_dialog), GTK_STOCK_OK,
    - GTK_RESPONSE_OK);
    -
    - label = gtk_label_new ("");
    - gtk_label_set_markup (GTK_LABEL (label),
    - _("<b>No accounts currently enabled:</b> You have not yet specified\n "
    - "what accounts AutoProfile should set the profile for. Until you\n "
    - "check one of the boxes below, AutoProfile will effectively do\n "
    - "nothing."));
    - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(accounts_dialog)->vbox), label,
    - FALSE, FALSE, 0);
    - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(accounts_dialog)->vbox),
    - get_account_page (), TRUE, TRUE, 0);
    -
    - g_signal_connect (G_OBJECT(accounts_dialog), "response",
    - G_CALLBACK(accounts_response_cb), NULL);
    - gtk_window_set_default_size (GTK_WINDOW(accounts_dialog), 400, 450);
    - gtk_widget_show_all (accounts_dialog);
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Profile edit window *
    - *--------------------------------------------------------------------------*/
    -/* Callbacks for refreshing profile preview window */
    -static void refresh_preview (GtkWidget *preview) {
    - // TODO: See if a delay timeout is necessary here
    -
    - gchar *output, *input;
    -
    - if (preview == NULL || current_profile == NULL) return;
    -
    - gtk_imhtml_clear (GTK_IMHTML(preview));
    - input = gtk_imhtml_get_markup ((GtkIMHtml *) current_profile);
    - output = ap_generate (input, AP_SIZE_PROFILE_MAX);
    - gtk_imhtml_append_text (GTK_IMHTML(preview), output,
    - GTK_IMHTML_NO_SCROLL);
    - free (input);
    - free (output);
    -}
    -
    -static void refresh_cb (GtkWidget *widget, gpointer data)
    -{
    - refresh_preview ((GtkWidget *) data);
    -}
    -
    -static void event_cb (GtkWidget *widget, gpointer data)
    -{
    - refresh_preview ((GtkWidget *) data);
    -}
    -
    -static void formatting_toggle_cb (GtkIMHtml *imhtml,
    - GtkIMHtmlButtons buttons, gpointer data)
    -{
    - refresh_preview ((GtkWidget *) data);
    -}
    -
    -static void formatting_clear_cb (GtkIMHtml *imhtml, gpointer data)
    -{
    - refresh_preview ((GtkWidget *) data);
    -}
    -
    -static void revert_cb (GtkWidget *button, GtkWidget *imhtml)
    -{
    - gtk_imhtml_clear (GTK_IMHTML(imhtml));
    - gtk_imhtml_append_text_with_images (GTK_IMHTML(imhtml),
    - purple_prefs_get_string ("/plugins/gtk/autoprofile/profile"),
    - 0, NULL);
    -}
    -
    -static void save_cb (GtkWidget *button, GtkWidget *imhtml)
    -{
    - gchar *new_text;
    -
    - if (imhtml == NULL) return;
    -
    - new_text = gtk_imhtml_get_markup ((GtkIMHtml *) imhtml);
    - purple_prefs_set_string ("/plugins/gtk/autoprofile/profile", new_text);
    - free (new_text);
    -
    - if (NULL == purple_prefs_get_string_list (
    - "/plugins/gtk/autoprofile/profile_accounts")) {
    - // If no accounts set, ask for one!
    - display_accounts_dialog ();
    - }
    -}
    -
    -/* Profile edit window */
    -static GtkWidget *get_profile_page ()
    -{
    - GtkTreeSelection *sel;
    -
    - GtkWidget *ret;
    - GtkWidget *hbox, *vbox, *dialog_box, *preview, *edit_window;
    - GtkWidget *label, *sw, *toolbar, *bbox;
    - GtkWidget *refresh_button, *revert_button, *save_button;
    - GtkTextBuffer *text_buffer;
    -
    - ret = gtk_vbox_new (FALSE, 6);
    -
    - /* Preview window */
    - dialog_box = gtk_vbox_new (FALSE, 4);
    - gtk_container_set_border_width (GTK_CONTAINER(dialog_box), 6);
    - gtk_box_pack_start (GTK_BOX(ret), dialog_box, TRUE, TRUE, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 0);
    - gtk_box_pack_start (GTK_BOX(dialog_box), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new ("");
    - gtk_label_set_markup (GTK_LABEL(label), _("<b>Preview</b>"));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - refresh_button = gtk_button_new_with_label (_("Refresh"));
    - gtk_box_pack_end (GTK_BOX(hbox), refresh_button, FALSE, FALSE, 0);
    -
    - sw = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    - gtk_box_pack_start (GTK_BOX(dialog_box), sw, TRUE, TRUE, 0);
    -
    - preview = gtk_imhtml_new (NULL, NULL);
    - gtk_container_add (GTK_CONTAINER(sw), preview);
    - pidgin_setup_imhtml (preview);
    - gtk_imhtml_append_text (GTK_IMHTML(preview),
    - purple_prefs_get_string ("/plugins/gtk/autoprofile/profile"),
    - GTK_IMHTML_NO_SCROLL);
    -
    - /* Separator */
    - gtk_box_pack_start (GTK_BOX(ret), gtk_hseparator_new (), FALSE, FALSE, 0);
    -
    - /* Edit window */
    - dialog_box = gtk_vbox_new (FALSE, 6);
    - gtk_container_set_border_width (GTK_CONTAINER(dialog_box), 6);
    - gtk_box_pack_start (GTK_BOX(ret), dialog_box, TRUE, TRUE, 0);
    -
    - label = gtk_label_new ("");
    - gtk_label_set_markup (GTK_LABEL(label),
    - _("<b>Edit</b> (Drag widgets into profile / "
    - "Use shift+enter to insert a new line)"));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX(dialog_box), label, FALSE, FALSE, 0);
    -
    - /* Widget list */
    - hbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(dialog_box), hbox, TRUE, TRUE, 0);
    -
    - vbox = gtk_vbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
    - get_widget_list (vbox, &sel);
    -
    - /* Button bar */
    - bbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
    -
    - revert_button = gtk_button_new_with_label (_("Revert"));
    - gtk_box_pack_start (GTK_BOX(bbox), revert_button, TRUE, TRUE, 0);
    - save_button = gtk_button_new_with_label (_("Save profile"));
    - gtk_box_pack_start (GTK_BOX(bbox), save_button, TRUE, TRUE, 0);
    -
    - edit_window = pidgin_create_imhtml (TRUE, &current_profile, &toolbar,
    - &sw);
    - gtk_box_pack_start (GTK_BOX(hbox), edit_window, TRUE, TRUE, 0);
    -
    - /* Finish */
    - g_signal_connect (G_OBJECT(save_button), "clicked",
    - G_CALLBACK(save_cb), current_profile);
    - g_signal_connect (G_OBJECT(revert_button), "clicked",
    - G_CALLBACK(revert_cb), current_profile);
    - g_signal_connect (G_OBJECT (refresh_button), "clicked",
    - G_CALLBACK (refresh_cb), preview);
    -
    - text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (current_profile));
    - g_signal_connect (G_OBJECT (text_buffer), "changed",
    - G_CALLBACK (event_cb), preview);
    - g_signal_connect_after(G_OBJECT(current_profile), "format_function_toggle",
    - G_CALLBACK(formatting_toggle_cb), preview);
    - g_signal_connect_after(G_OBJECT(current_profile), "format_function_clear",
    - G_CALLBACK(formatting_clear_cb), preview);
    -
    - revert_cb (revert_button, current_profile);
    - refresh_cb (refresh_button, preview);
    -
    - return ret;
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * General edit window *
    - *--------------------------------------------------------------------------*/
    -static void
    -ap_edit_content_destroy (GtkWidget *button, GtkWidget *window)
    -{
    - if (content_win) {
    - gtk_widget_destroy (content_win);
    - done_with_widget_list ();
    - content_win = NULL;
    - current_profile = NULL;
    - }
    -}
    -
    -static void ap_edit_content_show ()
    -{
    - GtkWidget *vbox;
    - GtkWidget *bbox;
    - GtkWidget *notebook;
    - GtkWidget *button;
    -
    - if (content_win) {
    - gtk_window_present (GTK_WINDOW(content_win));
    - return;
    - }
    -
    - /* Create the window */
    - content_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    - gtk_window_set_role (GTK_WINDOW(content_win), "ap_edit_content");
    - gtk_window_set_title (GTK_WINDOW(content_win), _("Edit Content"));
    - gtk_window_set_default_size (GTK_WINDOW(content_win), 700, 550);
    - gtk_container_set_border_width (GTK_CONTAINER(content_win), 6);
    -
    - g_signal_connect (G_OBJECT(content_win), "destroy",
    - G_CALLBACK(ap_edit_content_destroy), NULL);
    -
    - vbox = gtk_vbox_new (FALSE, 6);
    - gtk_container_add (GTK_CONTAINER(content_win), vbox);
    -
    - /* The notebook */
    - notebook = gtk_notebook_new ();
    - gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
    -
    - gtk_notebook_append_page (GTK_NOTEBOOK(notebook),
    - ap_widget_get_config_page (), gtk_label_new (_("Widgets")));
    - gtk_notebook_append_page (GTK_NOTEBOOK(notebook), get_profile_page (),
    - gtk_label_new (_("Info/profile")));
    -
    - /* The buttons to press! */
    - bbox = gtk_hbutton_box_new ();
    - gtk_box_set_spacing (GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
    - gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
    - gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
    - g_signal_connect (G_OBJECT(button), "clicked",
    - G_CALLBACK(ap_edit_content_destroy), NULL);
    - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
    -
    - gtk_widget_show_all (content_win);
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * The actions themselves *
    - *--------------------------------------------------------------------------*/
    -static void edit_content (PurplePluginAction *action)
    -{
    - ap_edit_content_show ();
    -}
    -
    -static void edit_preferences (PurplePluginAction *action)
    -{
    - ap_preferences_display ();
    -}
    -
    -static void make_visible (PurplePluginAction *action)
    -{
    - ap_gtk_make_visible ();
    -}
    -
    -/* Return the actions */
    -GList *actions (PurplePlugin *plugin, gpointer context)
    -{
    - PurplePluginAction *act;
    - GList *list = NULL;
    -
    - act = purple_plugin_action_new (_("Edit Content"), edit_content);
    - list = g_list_append (list, act);
    - act = purple_plugin_action_new (_("Preferences"), edit_preferences);
    - list = g_list_append (list, act);
    - act = purple_plugin_action_new (_("Show summary"), make_visible);
    - list = g_list_append (list, act);
    -
    - return list;
    -}
    -
    -void ap_actions_finish ()
    -{
    - if (content_win) ap_edit_content_destroy (NULL, NULL);
    - if (accounts_dialog) accounts_response_cb (NULL, 0, NULL);
    -}
    -
    --- a/autoprofile/gtk_away_msgs.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,487 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -
    -#include "gtkdialogs.h"
    -#include "gtkimhtml.h"
    -#include "gtkprefs.h"
    -
    -/* VARIABLE DEFINITIONS */
    -static guint queue_pref_cb = 0;
    -static guint sound_pref_cb = 0;
    -static gboolean ap_previously_away = FALSE;
    -
    -/* The list containing data on generated profiles / status messages */
    -static GtkListStore *message_list = NULL;
    -
    -/* The general window */
    -static GtkWidget *dialog = NULL;
    -
    -/* Progress bars */
    -typedef struct _ap_progress_bar {
    - APUpdateType type;
    - GtkWidget *bar;
    - guint timeout;
    -} APProgressBar;
    -static GHashTable *progress_bars = NULL;
    -
    -/*--------------------------------------------------------------------------*
    - * Callback functions *
    - *--------------------------------------------------------------------------*/
    -static void hide_cb (GtkButton *button, gpointer data) {
    - gtk_widget_hide_all (dialog);
    -}
    -
    -static void queue_cb (
    - const char *name, PurplePrefType type, gconstpointer val, gpointer data)
    -{
    - ap_update_queueing ();
    -}
    -
    -static void sound_cb (
    - const char *name, PurplePrefType type, gconstpointer val, gpointer data)
    -{
    - GtkWidget *button;
    - gboolean value;
    -
    - button = (GtkWidget *) data;
    - value = purple_prefs_get_bool ("/purple/sound/while_away");
    -
    - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), value);
    -}
    -
    -static void update_summary_visibility ()
    -{
    - const gchar *summary_pref;
    -
    - // Decide whether or not to show window
    - summary_pref = purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/show_summary");
    -
    - if (!strcmp (summary_pref, "always")) {
    - gtk_widget_show_all (dialog);
    - } else if (!strcmp (summary_pref, "away") && ap_is_currently_away ()) {
    - gtk_widget_show_all (dialog);
    - } else {
    - gtk_widget_hide_all (dialog);
    - }
    -
    - ap_previously_away = ap_is_currently_away ();
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Displayed message stuff *
    - *--------------------------------------------------------------------------*/
    -static void display_diff_msg (GtkTreeSelection *select, gpointer data)
    -{
    - GtkTreeModel *model;
    - GtkTreeIter iter;
    - const gchar *string;
    - GtkWidget *imhtml = (GtkWidget *) data;
    -
    - if (gtk_tree_selection_get_selected (select, &model, &iter))
    - {
    - gtk_tree_model_get (model, &iter, 3, &string, -1);
    - gtk_imhtml_clear (GTK_IMHTML(imhtml));
    - if (string != NULL) {
    - gtk_imhtml_append_text (GTK_IMHTML(imhtml), string,
    - GTK_IMHTML_NO_SCROLL);
    - gtk_imhtml_append_text (GTK_IMHTML(imhtml), "<BR>",
    - GTK_IMHTML_NO_SCROLL);
    - }
    - }
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Progress bar stuff *
    - *--------------------------------------------------------------------------*/
    -static APProgressBar *progress_create (APUpdateType type,
    - GtkWidget *container)
    -{
    - APProgressBar *progress_bar;
    -
    - progress_bar = (APProgressBar *) malloc (sizeof (APProgressBar));
    - progress_bar->timeout = 0;
    - progress_bar->type = type;
    - progress_bar->bar = gtk_progress_bar_new ();
    - gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR(progress_bar->bar), GTK_PROGRESS_CONTINUOUS);
    - gtk_box_pack_start (GTK_BOX(container), progress_bar->bar, FALSE, FALSE, 0);
    - if (type == AP_UPDATE_PROFILE) {
    - gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar),
    - _("no updates made to profile"));
    - } else if (type == AP_UPDATE_STATUS) {
    - gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar),
    - _("no updates made to status"));
    - }
    -
    - g_hash_table_insert (progress_bars, GINT_TO_POINTER(type), progress_bar);
    -
    - return progress_bar;
    -}
    -
    -static void progress_update_stop (APProgressBar *progress_bar) {
    - if (progress_bar->timeout) {
    - purple_timeout_remove (progress_bar->timeout);
    - progress_bar->timeout = 0;
    - }
    - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar->bar), 1.0);
    - if (progress_bar->type == AP_UPDATE_PROFILE) {
    - gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar),
    - _("waiting for new profile content"));
    - } else if (progress_bar->type == AP_UPDATE_STATUS) {
    - gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar),
    - _("waiting for new status content"));
    - }
    -}
    -
    -#define BAH 500
    -
    -static gboolean progress_update (gpointer data) {
    - int total_milliseconds;
    - int seconds_remaining;
    - double fraction_increment;
    - double cur_fraction;
    - double result;
    - GString *text;
    - APProgressBar *progress_bar = (APProgressBar *) data;
    -
    - // Update fraction on bar
    - total_milliseconds =
    - purple_prefs_get_int ("/plugins/gtk/autoprofile/delay_update") * 1000;
    - fraction_increment = BAH / ((double) total_milliseconds);
    - cur_fraction = gtk_progress_bar_get_fraction (
    - GTK_PROGRESS_BAR(progress_bar->bar));
    - result = cur_fraction + fraction_increment;
    - if (result >= 1) {
    - progress_update_stop (progress_bar);
    - return FALSE;
    - }
    - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar->bar), result);
    -
    - // Update text on bar
    - seconds_remaining = (int)
    - (((double) total_milliseconds / 1000) -
    - (cur_fraction * (double) total_milliseconds / 1000));
    - text = g_string_new ("");
    - if (progress_bar->type == AP_UPDATE_PROFILE) {
    - g_string_printf (text, _("next profile update in %d seconds"),
    - seconds_remaining);
    - } else if (progress_bar->type == AP_UPDATE_STATUS) {
    - g_string_printf (text, _("next status update in %d seconds"),
    - seconds_remaining);
    - }
    - gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar), text->str);
    - g_string_free (text, TRUE);
    -
    - return TRUE;
    -}
    -
    -static void ap_gtk_timeout_start (APUpdateType type) {
    - APProgressBar *progress_bar;
    -
    - progress_bar = g_hash_table_lookup (progress_bars, GINT_TO_POINTER(type));
    - if (progress_bar->timeout) {
    - purple_timeout_remove (progress_bar->timeout);
    - }
    - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar->bar), 0);
    - progress_bar->timeout =
    - purple_timeout_add (BAH, progress_update, progress_bar);
    - progress_update (progress_bar);
    -}
    -
    -void ap_gtk_set_progress_visible (APUpdateType type, gboolean visible)
    -{
    - APProgressBar *progress_bar;
    -
    - progress_bar = g_hash_table_lookup (progress_bars, GINT_TO_POINTER(type));
    - if (visible) gtk_widget_show (progress_bar->bar);
    - else gtk_widget_hide (progress_bar->bar);
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * Create the main window *
    - *--------------------------------------------------------------------------*/
    -static void create_dialog () {
    - GtkTreeViewColumn *column;
    - GtkCellRenderer *renderer;
    - GtkTreeSelection *selection;
    - GtkWidget *message_list_view;
    -
    - GtkWidget *vbox, *vpane, *hbox, *config_vbox;
    - GtkWidget *sw, *imhtml, *msg_window, *button;
    -
    - imhtml = gtk_imhtml_new (NULL, NULL);
    -
    - /* Create main display window */
    - dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    - gtk_window_set_type_hint (GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
    - gtk_window_set_title (GTK_WINDOW(dialog), _("AutoProfile Summary"));
    - gtk_widget_realize (dialog);
    -
    - vbox = gtk_vbox_new (FALSE, 5);
    - gtk_container_add (GTK_CONTAINER (dialog), vbox);
    - gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
    -
    - /* Set up progress bar container */
    - progress_create (AP_UPDATE_PROFILE, vbox);
    - progress_create (AP_UPDATE_STATUS, vbox);
    -
    - /* Set up list of past away messages */
    - vpane = gtk_vpaned_new ();
    - gtk_box_pack_start (GTK_BOX(vbox), vpane, TRUE, TRUE, 0);
    -
    - message_list = gtk_list_store_new (4,
    - G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
    - message_list_view = gtk_tree_view_new_with_model (
    - GTK_TREE_MODEL (message_list));
    - renderer = gtk_cell_renderer_text_new ();
    -
    - column = gtk_tree_view_column_new_with_attributes (
    - _("Time"), renderer, "markup", 0, NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW (message_list_view), column);
    - gtk_tree_view_column_set_sort_column_id (column, 0);
    -
    - column = gtk_tree_view_column_new_with_attributes (
    - _("Type"), renderer, "markup", 1, NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW (message_list_view), column);
    - gtk_tree_view_column_set_sort_column_id (column, 1);
    -
    - renderer = gtk_cell_renderer_text_new ();
    - g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    -
    - column = gtk_tree_view_column_new_with_attributes (
    - _("Text"), renderer, "markup", 2, NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW (message_list_view), column);
    - gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
    -
    - sw = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    -
    - gtk_container_add (GTK_CONTAINER (sw), message_list_view);
    - gtk_paned_add1 (GTK_PANED(vpane), sw);
    -
    - selection = gtk_tree_view_get_selection (
    - GTK_TREE_VIEW (message_list_view));
    - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
    - g_signal_connect (G_OBJECT (selection), "changed",
    - G_CALLBACK (display_diff_msg), imhtml);
    -
    - /* Set up the window to display away message in */
    - msg_window = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(msg_window),
    - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(msg_window),
    - GTK_SHADOW_IN);
    - gtk_paned_add2 (GTK_PANED(vpane), msg_window);
    -
    - gtk_container_add (GTK_CONTAINER(msg_window), imhtml);
    - pidgin_setup_imhtml (imhtml);
    -
    - /* Bottom area */
    - hbox = gtk_hbox_new (FALSE, 6);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - config_vbox = gtk_vbox_new (FALSE, 4);
    - gtk_box_pack_start (GTK_BOX(hbox), config_vbox, TRUE, TRUE, 0);
    -
    - pidgin_prefs_checkbox (
    - _("Queue new messages while away"),
    - "/plugins/gtk/autoprofile/queue_messages_when_away",
    - config_vbox);
    -
    - button = pidgin_prefs_checkbox (
    - _("Play sounds while away"),
    - "/purple/sound/while_away",
    - config_vbox);
    - sound_pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (),
    - "/purple/sound/while_away", sound_cb, button);
    -
    - gtk_box_pack_start (GTK_BOX(hbox), gtk_vseparator_new (), FALSE, FALSE, 0);
    -
    - config_vbox = gtk_vbox_new (FALSE, 4);
    - gtk_box_pack_start (GTK_BOX(hbox), config_vbox, TRUE, TRUE, 0);
    -
    - ap_gtk_prefs_add_summary_option (config_vbox);
    -
    - button = gtk_button_new_with_label (_("Hide summary now"));
    - gtk_box_pack_start (GTK_BOX(config_vbox), button, FALSE, FALSE, 0);
    - g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (hide_cb), NULL);
    -
    - /* Finish up */
    - g_signal_connect (G_OBJECT(dialog), "delete-event",
    - G_CALLBACK(gtk_widget_hide_on_delete), NULL);
    - gtk_paned_set_position (GTK_PANED(vpane), 250);
    - gtk_window_set_default_size (GTK_WINDOW(dialog), 430, 430);
    -}
    -
    -/*--------------------------------------------------------------------------*
    - * PUBLIC FUNCTIONS *
    - *--------------------------------------------------------------------------*/
    -void ap_gtk_add_message (APUpdateType update_type, APMessageType type,
    - const gchar *text)
    -{
    - GtkTreeIter iter;
    - struct tm *cur_time;
    - char *time_string, *simple_text, *s;
    - time_t *general_time;
    - gchar *type_string;
    -
    - // Create the time string
    - general_time = (time_t *) malloc (sizeof(time_t));
    - time (general_time);
    - cur_time = ap_localtime (general_time);
    - free (general_time);
    -
    - time_string = (char *) malloc (sizeof(char[32]));
    - *time_string = '\0';
    -
    - strftime (time_string, 31, "<b>%I:%M %p</b>", cur_time);
    - free (cur_time);
    -
    - // Create the type string
    - type_string = strdup("<b>Status</b>");
    - switch (type) {
    - case AP_MESSAGE_TYPE_PROFILE:
    - type_string = strdup (_("<b>User profile</b>"));
    - break;
    - case AP_MESSAGE_TYPE_AWAY:
    - type_string = strdup (_("<b>Away message</b>"));
    - break;
    - case AP_MESSAGE_TYPE_AVAILABLE:
    - type_string = strdup (_("<b>Available message</b>"));
    - break;
    - case AP_MESSAGE_TYPE_STATUS:
    - type_string = strdup (_("<b>Status message</b>"));
    - break;
    - default:
    - type_string = strdup (_("<b>Other</b>"));
    - break;
    - }
    -
    - // Simplify the text
    - if (text != NULL) {
    - simple_text = strdup (text);
    -
    - // Only show the first line
    - s = (gchar *) purple_strcasestr (simple_text, "<br>");
    - if (s != NULL) {
    - *s++ = '.';
    - *s++ = '.';
    - *s++ = '.';
    - *s = '\0';
    - }
    -
    - // Strip HTML
    - s = simple_text;
    - simple_text = purple_markup_strip_html (simple_text);
    - free (s);
    -
    - } else {
    - simple_text = NULL;
    - }
    -
    - // Add it
    - gtk_list_store_prepend (message_list, &iter);
    - gtk_list_store_set (message_list, &iter,
    - 0, time_string,
    - 1, type_string,
    - 2, simple_text,
    - 3, text,
    - -1);
    - free (type_string);
    - free (time_string);
    - if (simple_text) free (simple_text);
    -
    - // Delete if too many
    - if (gtk_tree_model_iter_nth_child
    - (GTK_TREE_MODEL(message_list), &iter, NULL,
    - AP_GTK_MAX_MESSAGES)) {
    - gtk_list_store_remove (message_list, &iter);
    - }
    -
    - // Move the timeout bar
    - ap_gtk_timeout_start (update_type);
    -
    - // Check if it needs to be visible or not
    - if (type != AP_MESSAGE_TYPE_PROFILE &&
    - ap_is_currently_away () != ap_previously_away) {
    - update_summary_visibility ();
    - }
    -}
    -
    -void ap_gtk_make_visible ()
    -{
    - gtk_widget_show_all (dialog);
    - gtk_window_present (GTK_WINDOW(dialog));
    -}
    -
    -void ap_gtk_start () {
    - progress_bars = g_hash_table_new (NULL, NULL);
    -
    - // Message queueing
    - queue_pref_cb = purple_prefs_connect_callback (
    - ap_get_plugin_handle (),
    - "/plugins/gtk/autoprofile/queue_messages_when_away", queue_cb, NULL);
    -
    - // Create window
    - create_dialog ();
    - update_summary_visibility ();
    -}
    -
    -static void ap_gtk_finish_progress_bar (APUpdateType type)
    -{
    - APProgressBar *progress_bar;
    -
    - progress_bar = g_hash_table_lookup (progress_bars, GINT_TO_POINTER(type));
    - if (progress_bar) {
    - if (progress_bar->timeout) {
    - purple_timeout_remove (progress_bar->timeout);
    - }
    - free (progress_bar);
    - g_hash_table_insert (progress_bars, GINT_TO_POINTER(type), NULL);
    - }
    -}
    -
    -void ap_gtk_finish () {
    - // Kill the window and associated variables
    - gtk_widget_destroy (dialog);
    - dialog = NULL;
    - message_list = NULL;
    -
    - ap_gtk_finish_progress_bar (AP_UPDATE_PROFILE);
    - ap_gtk_finish_progress_bar (AP_UPDATE_STATUS);
    -
    - // Disconnect queue message
    - purple_prefs_disconnect_callback (queue_pref_cb);
    - queue_pref_cb = 0;
    - purple_prefs_disconnect_callback (sound_pref_cb);
    - sound_pref_cb = 0;
    -
    - g_hash_table_destroy (progress_bars);
    - progress_bars = NULL;
    -}
    -
    --- a/autoprofile/gtk_widget.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,778 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -#include "widget.h"
    -#include "request.h"
    -
    -#include "gtkprefs.h"
    -#include "gtkimhtml.h"
    -
    -#define AP_RESPONSE_CHOOSE 98125
    -
    -static GtkWidget *dialog_box = NULL;
    -static GtkWidget *dialog_box_contents = NULL;
    -static GtkWidget *dialog_box_preview = NULL;
    -static struct widget *dialog_box_widget = NULL;
    -static GStaticMutex preview_mutex = G_STATIC_MUTEX_INIT;
    -
    -static GtkWidget *component_dialog = NULL;
    -static GtkWidget *choose_button = NULL;
    -
    -static GtkWidget *widget_dialog = NULL;
    -static GtkWidget *delete_button = NULL;
    -static GtkWidget *rename_button = NULL;
    -static GtkListStore *tree_list = NULL;
    -
    -static GHashTable *pref_names = NULL;
    -
    -static void component_dialog_show ();
    -
    -void ap_widget_prefs_updated (struct widget *w) {
    - gchar *output;
    -
    - if (dialog_box_preview == NULL) return;
    - if (w != dialog_box_widget) return;
    -
    - // TODO: Investigate how laggy this is, possibly add a timeout
    - output = w->component->generate (w);
    - g_static_mutex_lock (&preview_mutex);
    - gtk_imhtml_clear (GTK_IMHTML(dialog_box_preview));
    - gtk_imhtml_append_text (GTK_IMHTML(dialog_box_preview), output,
    - GTK_IMHTML_NO_SCROLL);
    - g_static_mutex_unlock (&preview_mutex);
    - free (output);
    -}
    -
    -static void update_widget_list (GtkListStore *ls) {
    - GtkTreeIter iter;
    - GList *widgets, *widgets_start;
    - struct widget *w;
    - GString *s;
    -
    - s = g_string_new ("");
    -
    - gtk_list_store_clear (ls);
    -
    - widgets_start = widgets = ap_widget_get_widgets ();
    -
    - for (widgets = widgets_start; widgets != NULL; widgets = widgets->next) {
    - gtk_list_store_append (ls, &iter);
    - w = (struct widget *) widgets->data;
    - g_string_printf (s, "<b>%s</b>", w->alias);
    -
    - gtk_list_store_set (ls, &iter,
    - 0, s->str,
    - 1, w,
    - -1);
    - }
    - g_list_free (widgets_start);
    - g_string_free (s, TRUE);
    -}
    -
    -static void refresh_cb (GtkWidget *widget, gpointer data) {
    - struct widget *w;
    -
    - w = (struct widget *) data;
    - ap_widget_prefs_updated (w);
    -}
    -
    -/* Widget configuration menu */
    -static GtkWidget *get_widget_configuration (struct widget *w) {
    - GtkWidget *config, *hbox, *vbox, *sw;
    - GtkWidget *label, *button;
    - GtkWidget *menu;
    - GString *s;
    - gchar *output;
    -
    - config = gtk_vbox_new (FALSE, 0);
    -
    - /* Title/Description */
    - hbox = gtk_hbox_new (FALSE, 8);
    - gtk_container_set_border_width (GTK_CONTAINER(hbox), 6);
    - gtk_box_pack_start (GTK_BOX(config), hbox, FALSE, FALSE, 0);
    -
    - s = g_string_new ("");
    - g_string_printf (s, "<b>%s:</b> %s", w->component->name,
    - w->component->description);
    - label = gtk_label_new ("");
    - gtk_label_set_markup (GTK_LABEL(label), s->str);
    - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - g_string_free (s, TRUE);
    -
    - /* Separator */
    - gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (),
    - FALSE, FALSE, 0);
    -
    - /* Preview window */
    - vbox = gtk_vbox_new (FALSE, 6);
    - gtk_container_set_border_width (GTK_CONTAINER(vbox), 6);
    - gtk_box_pack_start (GTK_BOX(config), vbox, FALSE, FALSE, 0);
    -
    - hbox = gtk_hbox_new (FALSE, 8);
    - gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new ("");
    - gtk_label_set_markup (GTK_LABEL(label), _("<b>Preview</b>"));
    - gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_with_label (_("Refresh"));
    - gtk_box_pack_end (GTK_BOX(hbox), button, FALSE, FALSE, 0);
    - g_signal_connect (G_OBJECT (button), "clicked",
    - G_CALLBACK (refresh_cb), w);
    -
    - sw = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    - gtk_box_pack_start (GTK_BOX(vbox), sw, TRUE, TRUE, 0);
    -
    - dialog_box_preview = gtk_imhtml_new (NULL, NULL);
    - gtk_container_add (GTK_CONTAINER(sw), dialog_box_preview);
    - pidgin_setup_imhtml (dialog_box_preview);
    - output = w->component->generate (w);
    - gtk_imhtml_append_text (GTK_IMHTML(dialog_box_preview),
    - output, GTK_IMHTML_NO_SCROLL);
    - free (output);
    - dialog_box_widget = w;
    -
    - /* Separator */
    - gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (),
    - FALSE, FALSE, 0);
    -
    - /* Configuration stuff */
    - vbox = gtk_vbox_new (FALSE, 8);
    - gtk_container_set_border_width (GTK_CONTAINER(vbox), 6);
    - gtk_box_pack_start (GTK_BOX(config), vbox, TRUE, TRUE, 0);
    -
    - label = gtk_label_new ("");
    - gtk_label_set_markup (GTK_LABEL(label), _("<b>Configuration</b>"));
    - gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    -
    - if (w->component->pref_menu == NULL ||
    - (menu = (w->component->pref_menu) (w)) == NULL) {
    - label = gtk_label_new (_("No options available for this component"));
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
    - } else {
    - gtk_box_pack_start (GTK_BOX (vbox), menu, TRUE, TRUE, 0);
    - }
    -
    - return config;
    -}
    -
    -/* Info message */
    -static GtkWidget *get_info_message () {
    - GtkWidget *page;
    - GtkWidget *aboutwin;
    - GtkWidget *text;
    -
    - /* Make the box */
    - page = gtk_vbox_new (FALSE, 8);
    - gtk_container_set_border_width (GTK_CONTAINER (page), 12);
    -
    - /* Window with info */
    - aboutwin = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(aboutwin),
    - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(aboutwin),
    - GTK_SHADOW_IN);
    - gtk_box_pack_start (GTK_BOX(page), aboutwin, TRUE, TRUE, 0);
    -
    - text = gtk_imhtml_new (NULL, NULL);
    - gtk_container_add (GTK_CONTAINER(aboutwin), text);
    - pidgin_setup_imhtml (text);
    -
    - /* Info text */
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("<b><u>Basic info</u></b><br>"), GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("A <b>widget</b> is a little piece/snippet of automatically "
    - "generated text. There are all sorts of widgets; each type has "
    - "different content (i.e. a random quote, text from a blog, the "
    - "song currently playing, etc).<br><br>"), GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("To use a widget, simply drag it from the list on the left and "
    - "drop it into a profile or status message. <i>It's that easy!</i>"
    - "<br><br>"), GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("<b>To edit your profile:</b> "
    - "Use the \"Info/profile\" tab in this window.<br>"),
    - GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("<b>To edit your available/away/status message:</b> "
    - "Use the regular Purple interface built into the bottom of the buddy "
    - "list.<br><br>"), GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("<b><u>Advanced Tips</u></b><br>"), GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("You can insert a widget into a profile or status by typing its name. "
    - "To do this, just type \"[widget-name]\" wherever you want to "
    - "place a widget (names of widgets are listed on the left). <br><br>"
    - "<b>You type:</b> The song I am playing now is [iTunesInfo].<br>"
    - "<b>AutoProfile result:</b> The song I am playing now is "
    - "The Beatles - Yellow Submarine.<br><br>"), GTK_IMHTML_NO_SCROLL);
    -
    - return page;
    -}
    -
    -/* Dialog window actions */
    -static void widget_popup_rename_cb (
    - struct widget *w, const char *new_text) {
    -
    - GtkTreeIter iter;
    - GValue val;
    - struct widget *cur_widget;
    - GString *s;
    -
    - gtk_tree_model_get_iter_first (GTK_TREE_MODEL(tree_list), &iter);
    -
    - while (TRUE) {
    - val.g_type = 0;
    - gtk_tree_model_get_value (GTK_TREE_MODEL(tree_list), &iter, 1, &val);
    - cur_widget = g_value_get_pointer(&val);
    -
    - if (cur_widget == w) break;
    -
    - if (!gtk_tree_model_iter_next (GTK_TREE_MODEL(tree_list), &iter)) {
    - purple_notify_error (NULL, NULL,
    - N_("Unable to change name"),
    - N_("The specified widget no longer exists."));
    - return;
    - }
    - }
    -
    - if (ap_widget_rename (w, new_text)) {
    - s = g_string_new ("");
    - g_string_printf (s, "<b>%s</b>", w->alias);
    - // Set value in ls
    - gtk_list_store_set (tree_list, &iter,
    - 0, s->str,
    - 1, w,
    - -1);
    - g_string_free (s, TRUE);
    - } else {
    - purple_notify_error (NULL, NULL,
    - N_("Unable to change name"),
    - N_("The widget name you have specified is already in use."));
    - }
    -}
    -
    -static void delete_cb (GtkWidget *button, GtkTreeSelection *sel)
    -{
    - GtkTreeModel *model;
    - GtkTreeIter iter;
    - GValue val;
    - struct widget *w;
    -
    - gtk_tree_selection_get_selected (sel, &model, &iter);
    - val.g_type = 0;
    - gtk_tree_model_get_value (model, &iter, 1, &val);
    - w = g_value_get_pointer(&val);
    - ap_widget_delete (w);
    - gtk_list_store_remove (GTK_LIST_STORE(model), &iter);
    -}
    -
    -static void rename_cb (GtkWidget *button, GtkTreeSelection *sel)
    -{
    - GtkTreeModel *model;
    - GtkTreeIter iter;
    - GValue val;
    - struct widget *w;
    -
    - gtk_tree_selection_get_selected (sel, &model, &iter);
    - val.g_type = 0;
    - gtk_tree_model_get_value (model, &iter, 1, &val);
    - w = g_value_get_pointer(&val);
    -
    - purple_request_input(NULL,
    - _("Rename Widget"), NULL,
    - _("Enter a new name for this widget."), w->alias,
    - FALSE, FALSE, NULL,
    - _("Rename"), G_CALLBACK(widget_popup_rename_cb),
    - _("Cancel"), NULL, NULL, NULL, NULL, w);
    -}
    -
    -static void add_cb (GtkWidget *button, GtkTreeSelection *sel)
    -{
    - component_dialog_show ();
    -}
    -
    -void ap_widget_gtk_start () {
    - pref_names = g_hash_table_new (g_str_hash, g_str_equal);
    -}
    -
    -void ap_widget_gtk_finish () {
    - done_with_widget_list ();
    - g_hash_table_destroy (pref_names);
    - pref_names = NULL;
    -}
    -
    -static void widget_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) {
    - GtkTreeIter iter;
    - struct widget *w;
    - GValue val;
    -
    - gtk_widget_destroy (dialog_box_contents);
    -
    - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - gtk_widget_set_sensitive(rename_button, FALSE);
    - gtk_widget_set_sensitive(delete_button, FALSE);
    - dialog_box_contents = get_info_message ();
    - dialog_box_preview = NULL;
    - dialog_box_widget = NULL;
    - } else {
    - gtk_widget_set_sensitive(rename_button, TRUE);
    - gtk_widget_set_sensitive(delete_button, TRUE);
    -
    - val.g_type = 0;
    - gtk_tree_model_get_value (GTK_TREE_MODEL(tree_list), &iter, 1, &val);
    - w = g_value_get_pointer(&val);
    -
    - dialog_box_contents = get_widget_configuration (w);
    - }
    -
    - gtk_box_pack_start (GTK_BOX(dialog_box), dialog_box_contents,
    - TRUE, TRUE, 0);
    - gtk_widget_show_all (dialog_box);
    -}
    -
    -GtkWidget *ap_widget_get_config_page ()
    -{
    - GtkTreeSelection *sel;
    - GtkWidget *vbox;
    - GtkWidget *add_button;
    -
    - /* Arrange main parts of window */
    - dialog_box = gtk_hbox_new (FALSE, 0);
    -
    - vbox = gtk_vbox_new (FALSE, 0);
    - gtk_box_pack_start (GTK_BOX(dialog_box), vbox, FALSE, FALSE, 0);
    -
    - get_widget_list (vbox, &sel);
    - g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (widget_sel_cb),
    - NULL);
    -
    - add_button = gtk_button_new_with_label (_("New Widget"));
    - g_signal_connect (G_OBJECT(add_button), "clicked",
    - G_CALLBACK(add_cb), sel);
    - gtk_box_pack_start (GTK_BOX(vbox), add_button, FALSE, FALSE, 0);
    -
    - rename_button = gtk_button_new_with_label (_("Rename"));
    - gtk_widget_set_sensitive(rename_button, FALSE);
    - g_signal_connect (G_OBJECT(rename_button), "clicked",
    - G_CALLBACK(rename_cb), sel);
    - gtk_box_pack_start (GTK_BOX(vbox), rename_button, FALSE, FALSE, 0);
    -
    - delete_button = gtk_button_new_with_label (_("Delete"));
    - gtk_widget_set_sensitive(delete_button, FALSE);
    - g_signal_connect (G_OBJECT(delete_button), "clicked",
    - G_CALLBACK(delete_cb), sel);
    - gtk_box_pack_start (GTK_BOX(vbox), delete_button, FALSE, FALSE, 0);
    -
    - dialog_box_contents = get_info_message ();
    - gtk_box_pack_start (GTK_BOX(dialog_box), dialog_box_contents,
    - TRUE, TRUE, 0);
    -
    - return dialog_box;
    -}
    -
    -/* DND */
    -static void
    -drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx,
    - GtkSelectionData *data, guint info, guint time,
    - gpointer user_data)
    -{
    - GtkListStore *ls = (GtkListStore *) user_data;
    -
    - if (ls == NULL) return;
    -
    - if (data->target == gdk_atom_intern ("STRING", FALSE)) {
    - GtkTreeRowReference *ref;
    - GtkTreePath *source_row;
    - GtkTreeIter iter;
    - GString *s;
    - struct widget *w;
    - GValue val = {0};
    -
    - ref = g_object_get_data (G_OBJECT(ctx), "gtk-tree-view-source-row");
    - source_row = gtk_tree_row_reference_get_path (ref);
    -
    - if (source_row == NULL) return;
    -
    - gtk_tree_model_get_iter(GTK_TREE_MODEL(ls), &iter, source_row);
    - gtk_tree_model_get_value(GTK_TREE_MODEL(ls), &iter,
    - 1, &val);
    -
    - w = g_value_get_pointer (&val);
    - s = g_string_new ("");
    - g_string_printf (s, "[%s]", w->alias);
    - gtk_selection_data_set (data, gdk_atom_intern ("STRING", FALSE),
    - 8, (guchar *)s->str, strlen(s->str)+1);
    -
    - g_string_free (s, TRUE);
    - gtk_tree_path_free (source_row);
    - }
    -}
    -
    -void done_with_widget_list () {
    - if (tree_list) {
    - g_object_unref (tree_list);
    - tree_list = NULL;
    - }
    -
    - widget_dialog = NULL;
    - delete_button = NULL;
    - dialog_box = NULL;
    - dialog_box_contents = NULL;
    - dialog_box_preview = NULL;
    - dialog_box_widget = NULL;
    - if (component_dialog != NULL) {
    - gtk_widget_destroy (component_dialog);
    - component_dialog = NULL;
    - choose_button = NULL;
    - }
    -}
    -
    -GtkWidget *get_widget_list (GtkWidget *box, GtkTreeSelection **sel)
    -{
    - GtkWidget *sw;
    - GtkWidget *event_view;
    - GtkCellRenderer *rend;
    - GtkTreeViewColumn *col;
    - GtkTargetEntry gte [] = {{"STRING", 0, GTK_IMHTML_DRAG_STRING}};
    -
    - if (tree_list == NULL) {
    - tree_list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
    - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(tree_list),
    - 0, GTK_SORT_ASCENDING);
    - update_widget_list (tree_list);
    - g_object_ref (G_OBJECT(tree_list));
    - }
    -
    - /* List of widgets */
    - sw = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    - gtk_box_pack_start (GTK_BOX(box), sw, TRUE, TRUE, 0);
    -
    - event_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_list));
    - *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
    -
    - rend = gtk_cell_renderer_text_new ();
    - col = gtk_tree_view_column_new_with_attributes (_("Widget"), rend,
    - "markup", 0, NULL);
    -
    - gtk_tree_view_append_column (GTK_TREE_VIEW (event_view), col);
    - gtk_tree_view_column_set_sort_column_id (col, 0);
    - gtk_container_add (GTK_CONTAINER(sw), event_view);
    -
    - /* Drag and Drop */
    - gtk_tree_view_enable_model_drag_source(
    - GTK_TREE_VIEW(event_view), GDK_BUTTON1_MASK, gte,
    - 1, GDK_ACTION_COPY);
    - g_signal_connect(G_OBJECT(event_view), "drag-data-get",
    - G_CALLBACK(drag_data_get_cb), tree_list);
    -
    - return event_view;
    -}
    -
    -/*********************************************************
    - Component selection window
    -**********************************************************/
    -
    -static void add_component (struct component *c) {
    - struct widget *w;
    - GtkTreeIter iter;
    - GString *s;
    -
    - w = ap_widget_create (c);
    -
    - if (w == NULL) return;
    -
    - s = g_string_new ("");
    -
    - gtk_list_store_append (tree_list, &iter);
    - g_string_printf (s, "<b>%s</b>", w->alias);
    -
    - gtk_list_store_set (tree_list, &iter,
    - 0, s->str,
    - 1, w,
    - -1);
    - g_string_free (s, TRUE);
    -}
    -
    -static void component_row_activate_cb (GtkTreeView *view, GtkTreePath *path,
    - GtkTreeViewColumn *column, gpointer null)
    -{
    - GtkTreeSelection *sel;
    - GtkTreeIter iter;
    - struct component *c;
    - GtkTreeModel *model;
    -
    - sel = gtk_tree_view_get_selection (view);
    -
    - if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - gtk_tree_model_get (model, &iter, 1, &c, -1);
    - add_component (c);
    - }
    -
    - gtk_widget_destroy (component_dialog);
    - component_dialog = NULL;
    - choose_button = NULL;
    -}
    -
    -static void component_response_cb(GtkWidget *d, int response,
    - GtkTreeSelection *sel)
    -{
    - GtkTreeModel *model;
    - GtkTreeIter iter;
    - GValue val;
    - struct component *c;
    -
    - switch (response) {
    - case AP_RESPONSE_CHOOSE:
    - gtk_tree_selection_get_selected (sel, &model, &iter);
    - val.g_type = 0;
    - gtk_tree_model_get_value (model, &iter, 1, &val);
    - c = g_value_get_pointer(&val);
    - add_component (c);
    - case GTK_RESPONSE_CLOSE:
    - case GTK_RESPONSE_CANCEL:
    - case GTK_RESPONSE_DELETE_EVENT:
    - gtk_widget_destroy(d);
    - component_dialog = NULL;
    - choose_button = NULL;
    - break;
    - }
    -}
    -
    -static void component_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) {
    - GtkTreeIter iter;
    -
    - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - gtk_widget_set_sensitive(choose_button, FALSE);
    - } else {
    - gtk_widget_set_sensitive(choose_button, TRUE);
    - }
    -}
    -
    -static void update_component_list (GtkListStore *ls) {
    - GtkTreeIter iter;
    - GList *components;
    - struct component *c;
    - GString *s;
    - gchar *name, *description;
    -
    - gtk_list_store_clear (ls);
    -
    - s = g_string_new ("");
    -
    - for (components = ap_component_get_components ();
    - components != NULL;
    - components = components->next) {
    - gtk_list_store_append (ls, &iter);
    - c = (struct component *) components->data;
    -
    - name = g_markup_escape_text (c->name, -1);
    - description = g_markup_escape_text (c->description, -1);
    -
    - g_string_printf (s, "<b>%s</b>\n%s", name, description);
    -
    - gtk_list_store_set (ls, &iter,
    - 0, s->str,
    - 1, c,
    - -1);
    - free (name);
    - free (description);
    - }
    -
    - g_string_free (s, TRUE);
    -}
    -
    -static void component_dialog_show ()
    -{
    - GtkWidget *sw;
    - GtkWidget *event_view;
    - GtkListStore *ls;
    - GtkCellRenderer *rendt;
    - GtkTreeViewColumn *col;
    - GtkTreeSelection *sel;
    -
    - if (component_dialog != NULL) {
    - gtk_window_present(GTK_WINDOW(component_dialog));
    - return;
    - }
    -
    - component_dialog = gtk_dialog_new_with_buttons (_("Select a widget type"),
    - NULL,
    - GTK_DIALOG_NO_SEPARATOR,
    - NULL);
    -
    - choose_button = gtk_dialog_add_button (GTK_DIALOG(component_dialog),
    - _("Create widget"), AP_RESPONSE_CHOOSE);
    - gtk_dialog_add_button (GTK_DIALOG(component_dialog),
    - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
    - gtk_widget_set_sensitive (choose_button, FALSE);
    -
    - sw = gtk_scrolled_window_new (NULL,NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    -
    - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(component_dialog)->vbox), sw,
    - TRUE, TRUE, 0);
    -
    - ls = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
    - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(ls),
    - 0, GTK_SORT_ASCENDING);
    -
    - update_component_list (ls);
    -
    - event_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(ls));
    -
    - g_signal_connect(G_OBJECT(event_view), "row-activated",
    - G_CALLBACK(component_row_activate_cb), event_view);
    -
    - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
    -
    - rendt = gtk_cell_renderer_text_new ();
    - col = gtk_tree_view_column_new_with_attributes (_("Widget type"),
    - rendt,
    - "markup", 0,
    - NULL);
    -
    -#if GTK_CHECK_VERSION(2,6,0)
    - gtk_tree_view_column_set_expand (col, TRUE);
    - g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    -#endif
    - gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
    - gtk_tree_view_column_set_sort_column_id (col, 0);
    - g_object_unref (G_OBJECT(ls));
    - gtk_container_add (GTK_CONTAINER(sw), event_view);
    -
    - g_signal_connect (G_OBJECT (sel), "changed",
    - G_CALLBACK (component_sel_cb), NULL);
    - g_signal_connect(G_OBJECT(component_dialog), "response",
    - G_CALLBACK(component_response_cb), sel);
    - gtk_window_set_default_size(GTK_WINDOW(component_dialog), 550, 430);
    - gtk_widget_show_all(component_dialog);
    -}
    -
    -/* Preferences stuff */
    -static void pref_callback (const char *name, PurplePrefType type,
    - gconstpointer val, gpointer data) {
    - struct widget *w = (struct widget *) data;
    - ap_widget_prefs_updated (w);
    -}
    -
    -static const gchar *get_const_pref (struct widget *w, const char *key) {
    - gchar *pref, *result;
    - // This is here to prevent memory leaks
    -
    - pref = ap_prefs_get_pref_name (w, key);
    - if (pref_names == NULL) {
    - pref_names = g_hash_table_new (g_str_hash, g_str_equal);
    - }
    -
    - result = g_hash_table_lookup (pref_names, pref);
    -
    - if (!result) {
    - g_hash_table_insert (pref_names, pref, pref);
    - return pref;
    - } else {
    - free (pref);
    - return result;
    - }
    -}
    -
    -GtkWidget *ap_prefs_checkbox (struct widget *w, const char *title,
    - const char *key, GtkWidget *page)
    -{
    - GtkWidget *result;
    - const gchar *pref;
    -
    - pref = get_const_pref (w, key);
    - result = pidgin_prefs_checkbox (title, pref, page);
    - purple_prefs_connect_callback (ap_get_plugin_handle (), pref,
    - pref_callback, w);
    -
    - return result;
    -}
    -
    -GtkWidget *ap_prefs_dropdown_from_list (struct widget *w, GtkWidget *page,
    - const gchar *title, PurplePrefType type, const char *key, GList *menuitems)
    -{
    - GtkWidget *result;
    - const gchar *pref;
    -
    - pref = get_const_pref (w, key);
    - result = pidgin_prefs_dropdown_from_list (
    - page, title, type, pref, menuitems);
    - purple_prefs_connect_callback (ap_get_plugin_handle (), pref,
    - pref_callback, w);
    -
    - return result;
    -}
    -
    -GtkWidget *ap_prefs_labeled_entry (struct widget *w, GtkWidget *page,
    - const gchar *title, const char *key, GtkSizeGroup *sg) {
    - GtkWidget *result;
    - const gchar *pref;
    -
    - pref = get_const_pref (w, key);
    - result = pidgin_prefs_labeled_entry (page, title, pref, sg);
    - purple_prefs_connect_callback (ap_get_plugin_handle (), pref,
    - pref_callback, w);
    -
    - return result;
    -}
    -
    -GtkWidget *ap_prefs_labeled_spin_button (struct widget *w,
    - GtkWidget *page, const gchar *title, const char *key, int min,
    - int max, GtkSizeGroup *sg)
    -{
    - GtkWidget *result;
    - const gchar *pref;
    -
    - pref = get_const_pref (w, key);
    - result = pidgin_prefs_labeled_spin_button (page, title, pref,
    - min, max, sg);
    - purple_prefs_connect_callback (ap_get_plugin_handle (), pref,
    - pref_callback, w);
    -
    - return result;
    -}
    -
    --- a/autoprofile/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,38 +0,0 @@
    -if TYPES.contains('incomplete') and PIDGIN.found()
    - autoprofile = shared_module('autoprofile',
    - 'autoaway.c',
    - 'autoprofile.c',
    - 'autoprofile.h',
    - 'autoreply.c',
    - 'comp_countdownup.c',
    - 'comp_executable.c',
    - 'comp_http.c',
    - 'comp_logstats.c',
    - 'comp_logstats_gtk.c',
    - 'comp_logstats.h',
    - 'component.c',
    - 'component.h',
    - 'comp_quotation.c',
    - 'comp_rss.c',
    - 'comp_rss.h',
    - 'comp_rss_parser.c',
    - 'comp_rss_xanga.c',
    - 'comp_textfile.c',
    - 'comp_timestamp.c',
    - 'comp_uptime.c',
    - 'gtk_actions.c',
    - 'gtk_away_msgs.c',
    - 'gtk_widget.c',
    - 'preferences.c',
    - 'sizes.h',
    - 'utility.c',
    - 'utility.h',
    - 'utility_rfc822.c',
    - 'widget.c',
    - 'widget.h',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'autoprofile'
    -endif
    --- a/autoprofile/preferences.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,750 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -
    -#include "gtkimhtml.h"
    -#include "gtksavedstatuses.h"
    -#include "gtkprefs.h"
    -
    -/*--------------------------------------------------------------------------*
    - * Info Tab *
    - *--------------------------------------------------------------------------*/
    -static GtkWidget *get_info_page () {
    - GtkWidget *page;
    - GtkWidget *label;
    - GtkWidget *aboutwin;
    - GtkWidget *text;
    -
    - gchar *labeltext, *str;
    -
    - /* Make the box */
    - page = gtk_vbox_new (FALSE, 5);
    - gtk_container_set_border_width (GTK_CONTAINER (page), 5);
    -
    - /* AutoProfile title */
    - labeltext = g_strdup_printf (
    - _("<span weight=\"bold\" size=\"larger\">AutoProfile %s</span>"),
    - PP_VERSION);
    - label = gtk_label_new (NULL);
    - gtk_label_set_markup (GTK_LABEL(label), labeltext);
    - gtk_label_set_line_wrap (GTK_LABEL(label), TRUE);
    - gtk_misc_set_alignment (GTK_MISC(label), 0.5, 0);
    - gtk_box_pack_start (GTK_BOX(page), label, FALSE, FALSE, 0);
    - g_free(labeltext);
    -
    - /* Window with info */
    - aboutwin = gtk_scrolled_window_new (NULL, NULL);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(aboutwin),
    - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(aboutwin),
    - GTK_SHADOW_IN);
    - gtk_box_pack_start (GTK_BOX(page), aboutwin, TRUE, TRUE, 0);
    -
    - text = gtk_imhtml_new (NULL, NULL);
    - gtk_container_add (GTK_CONTAINER(aboutwin), text);
    - pidgin_setup_imhtml (text);
    -
    - /* Info text */
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("Use the <b>Autoprofile</b> portion of the <b>Tools</b> "
    - "menu in the <b>"
    - "buddy list</b> to configure the actual content that will go in your "
    - "status messages and profiles and set options.<br><br>"),
    - GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("<u>DOCUMENTATION / HELP</u><br>"), GTK_IMHTML_NO_SCROLL);
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("Complete documentation can be found at:<br> <a href="
    - "\"http://hkn.eecs.berkeley.edu/~casey/autoprofile/documentation.php\">"
    - "hkn.eecs.berkeley.edu/~casey/autoprofile/documentation.php</a><br>"),
    - GTK_IMHTML_NO_SCROLL);
    -
    - gtk_imhtml_append_text (GTK_IMHTML(text),
    - _("<br><u>ABOUT</u><br>"), GTK_IMHTML_NO_SCROLL);
    -
    - str = g_strconcat(
    - "<font size=\"3\"><b>", _("Developers"), ":</b></font><br>"
    - " Casey Ho (Lead Developer)<br>"
    - " Mitchell Harwell<br>", NULL);
    - gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
    - g_free(str);
    -
    - str = g_strconcat(
    - "<font size=\"3\"><b>", _("Contributors/Patchers"), ":</b></font><br>"
    - " Onime Clement<br>"
    - " Michael Milligan<br>"
    - " Mark Painter<br>", NULL);
    - gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
    - g_free(str);
    -
    - str = g_strconcat(
    - "<font size=\"3\"><b>", _("Website"), ":</b></font><br>"
    - " <a href=\"http://autoprofile.sourceforge.net\">"
    - "autoprofile.sourceforge.net<br>", NULL);
    - gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
    - g_free(str);
    -
    - return page;
    -}
    -
    -/*----------------------------------------------------------------------------
    - * Accounts Tab
    - *--------------------------------------------------------------------------*/
    -/* PRIMARILY RIPPED FROM GAIM GTKACCOUNT.C */
    -enum
    -{
    - COLUMN_ICON,
    - COLUMN_SCREENNAME,
    - COLUMN_ENABLED,
    - COLUMN_PROTOCOL,
    - COLUMN_DATA,
    - COLUMN_PULSE_DATA,
    - NUM_COLUMNS
    -};
    -
    -typedef struct
    -{
    - PurpleAccount *account;
    - char *username;
    - char *alias;
    -} PidginAccountAddUserData;
    -
    -typedef struct
    -{
    - GtkWidget *treeview;
    -
    - GtkListStore *model;
    - GtkTreeIter drag_iter;
    -
    - GtkTreeViewColumn *screenname_col;
    -} AccountsWindow;
    -
    -static void add_account_to_liststore(PurpleAccount *, gpointer);
    -static void set_account(GtkListStore *, GtkTreeIter *, PurpleAccount *);
    -
    -static gboolean is_profile_settable (PurpleAccount *a) {
    - const gchar *id = purple_account_get_protocol_id (a);
    - if (!strcmp (id, "prpl-yahoo") ||
    - !strcmp (id, "prpl-msn") ||
    - !strcmp (id, "prpl-jabber")) {
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -static void
    -drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx,
    - GtkSelectionData *data, guint info, guint time,
    - AccountsWindow *dialog)
    -{
    - if (data->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE)) {
    - GtkTreeRowReference *ref;
    - GtkTreePath *source_row;
    - GtkTreeIter iter;
    - PurpleAccount *account = NULL;
    - GValue val = {0};
    -
    - ref = g_object_get_data(G_OBJECT(ctx), "gtk-tree-view-source-row");
    - source_row = gtk_tree_row_reference_get_path(ref);
    -
    - if (source_row == NULL) return;
    -
    - gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, source_row);
    - gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    - COLUMN_DATA, &val);
    -
    - dialog->drag_iter = iter;
    -
    - account = g_value_get_pointer(&val);
    -
    - gtk_selection_data_set(data, gdk_atom_intern("PURPLE_ACCOUNT", FALSE),
    - 8, (void *)&account, sizeof(account));
    -
    - gtk_tree_path_free(source_row);
    - }
    -}
    -
    -static void
    -move_account_after(GtkListStore *store, GtkTreeIter *iter,
    - GtkTreeIter *position)
    -{
    - GtkTreeIter new_iter;
    - PurpleAccount *account;
    -
    - gtk_tree_model_get(GTK_TREE_MODEL(store), iter, COLUMN_DATA, &account, -1);
    - gtk_list_store_insert_after(store, &new_iter, position);
    -
    - set_account(store, &new_iter, account);
    -
    - gtk_list_store_remove(store, iter);
    -}
    -
    -static void
    -move_account_before(GtkListStore *store, GtkTreeIter *iter,
    - GtkTreeIter *position)
    -{
    - GtkTreeIter new_iter;
    - PurpleAccount *account;
    -
    - gtk_tree_model_get(GTK_TREE_MODEL(store), iter, COLUMN_DATA, &account, -1);
    - gtk_list_store_insert_before(store, &new_iter, position);
    -
    - set_account(store, &new_iter, account);
    -
    - gtk_list_store_remove(store, iter);
    -}
    -
    -static void
    -drag_data_received_cb(GtkWidget *widget, GdkDragContext *ctx,
    - guint x, guint y, GtkSelectionData *sd,
    - guint info, guint t, AccountsWindow *dialog)
    -{
    - if (sd->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE) && sd->data) {
    - gint dest_index;
    - PurpleAccount *a = NULL;
    - GtkTreePath *path = NULL;
    - GtkTreeViewDropPosition position;
    -
    - memcpy(&a, sd->data, sizeof(a));
    -
    - if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y,
    - &path, &position)) {
    -
    - GtkTreeIter iter;
    - PurpleAccount *account;
    - GValue val = {0};
    -
    - gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
    - gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    - COLUMN_DATA, &val);
    -
    - account = g_value_get_pointer(&val);
    -
    - switch (position) {
    - case GTK_TREE_VIEW_DROP_AFTER:
    - case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
    - move_account_after(dialog->model, &dialog->drag_iter, &iter);
    - dest_index = g_list_index(purple_accounts_get_all(), account) + 1;
    - break;
    -
    - case GTK_TREE_VIEW_DROP_BEFORE:
    - case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
    - dest_index = g_list_index(purple_accounts_get_all(), account);
    - move_account_before(dialog->model, &dialog->drag_iter, &iter);
    - break;
    -
    - default:
    - return;
    - }
    -
    - purple_accounts_reorder(a, dest_index);
    - }
    - }
    -}
    -
    -static void
    -enabled_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data)
    -{
    - AccountsWindow *dialog = (AccountsWindow *)data;
    - PurpleAccount *account;
    - GtkTreeModel *model = GTK_TREE_MODEL(dialog->model);
    - GtkTreeIter iter;
    - gboolean enabled;
    -
    - gtk_tree_model_get_iter_from_string(model, &iter, path_str);
    - gtk_tree_model_get(model, &iter,
    - COLUMN_DATA, &account,
    - COLUMN_ENABLED, &enabled,
    - -1);
    -
    - /* Change profile settings */
    - ap_account_enable_profile (account, !enabled);
    - set_account (dialog->model, &iter, account);
    -}
    -
    -static void
    -add_columns(GtkWidget *treeview, AccountsWindow *dialog)
    -{
    - GtkCellRenderer *renderer;
    - GtkTreeViewColumn *column;
    -
    - /* Screen Name column */
    - column = gtk_tree_view_column_new();
    - gtk_tree_view_column_set_title(column, _("Screen Name"));
    - gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);
    - gtk_tree_view_column_set_resizable(column, TRUE);
    -
    - /* Icon */
    - renderer = gtk_cell_renderer_pixbuf_new();
    - gtk_tree_view_column_pack_start(column, renderer, FALSE);
    - gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", COLUMN_ICON);
    -
    - /* Screen Name */
    - renderer = gtk_cell_renderer_text_new();
    - gtk_tree_view_column_pack_start(column, renderer, TRUE);
    - gtk_tree_view_column_add_attribute(column, renderer,
    - "text", COLUMN_SCREENNAME);
    - dialog->screenname_col = column;
    -
    - /* Enabled */
    - renderer = gtk_cell_renderer_toggle_new();
    -
    - g_signal_connect(G_OBJECT(renderer), "toggled",
    - G_CALLBACK(enabled_cb), dialog);
    -
    - column =
    - gtk_tree_view_column_new_with_attributes(_("AutoProfile sets user info"),
    - renderer, "active", COLUMN_ENABLED, NULL);
    -
    - gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);
    - gtk_tree_view_column_set_resizable(column, TRUE);
    -
    - /* Protocol name */
    - column = gtk_tree_view_column_new();
    - gtk_tree_view_column_set_title(column, _("Protocol"));
    - gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);
    - 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_add_attribute(column, renderer,
    - "text", COLUMN_PROTOCOL);
    -}
    -
    -static void
    -set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account)
    -{
    - GdkPixbuf *pixbuf;
    - GdkPixbuf *scale;
    -
    - scale = NULL;
    -
    - pixbuf = pidgin_create_prpl_icon(account, 0.5);
    -
    - if (pixbuf != NULL)
    - {
    - scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR);
    -
    - if (purple_account_is_disconnected(account))
    - gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE);
    - }
    -
    - gtk_list_store_set(store, iter,
    - COLUMN_ICON, scale,
    - COLUMN_SCREENNAME, purple_account_get_username(account),
    - COLUMN_ENABLED, ap_account_has_profile_enabled(account),
    - COLUMN_PROTOCOL, purple_account_get_protocol_name(account),
    - COLUMN_DATA, account,
    - -1);
    -
    - if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf));
    - if (scale != NULL) g_object_unref(G_OBJECT(scale));
    -}
    -
    -static void
    -add_account_to_liststore(PurpleAccount *account, gpointer user_data)
    -{
    - GtkTreeIter iter;
    - AccountsWindow *dialog = (AccountsWindow *) user_data;
    -
    - if (dialog == NULL) return;
    -
    - if (!is_profile_settable (account)) return;
    -
    - gtk_list_store_append(dialog->model, &iter);
    - set_account(dialog->model, &iter, account);
    -}
    -
    -static void
    -populate_accounts_list(AccountsWindow *dialog)
    -{
    - GList *l;
    -
    - gtk_list_store_clear(dialog->model);
    -
    - for (l = purple_accounts_get_all(); l != NULL; l = l->next)
    - add_account_to_liststore((PurpleAccount *)l->data, dialog);
    - }
    -
    -#if !GTK_CHECK_VERSION(2,2,0)
    -static void
    -get_selected_helper(GtkTreeModel *model, GtkTreePath *path,
    - GtkTreeIter *iter, gpointer user_data)
    -{
    - *((gboolean *)user_data) = TRUE;
    -}
    -#endif
    -
    -static void
    -account_selected_cb(GtkTreeSelection *sel, AccountsWindow *dialog)
    -{
    - gboolean selected = FALSE;
    -
    -#if GTK_CHECK_VERSION(2,2,0)
    - selected = (gtk_tree_selection_count_selected_rows(sel) > 0);
    -#else
    - gtk_tree_selection_selected_foreach(sel, get_selected_helper, &selected);
    -#endif
    -}
    -
    -static GtkWidget *
    -create_accounts_list(AccountsWindow *dialog)
    -{
    - GtkWidget *sw;
    - GtkWidget *treeview;
    - GtkTreeSelection *sel;
    - GtkTargetEntry gte[] = {{"PURPLE_ACCOUNT", GTK_TARGET_SAME_APP, 0}};
    -
    - /* Create the scrolled window. */
    - sw = gtk_scrolled_window_new(0, 0);
    - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_AUTOMATIC,
    - GTK_POLICY_ALWAYS);
    - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
    - gtk_widget_show(sw);
    -
    - /* Create the list model. */
    - dialog->model = gtk_list_store_new(NUM_COLUMNS,
    - GDK_TYPE_PIXBUF, G_TYPE_STRING,
    - G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER,
    - G_TYPE_POINTER);
    -
    - /* And now the actual treeview */
    - treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
    - dialog->treeview = treeview;
    - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
    -
    - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
    - gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
    - g_signal_connect(G_OBJECT(sel), "changed",
    - G_CALLBACK(account_selected_cb), dialog);
    -
    - gtk_container_add(GTK_CONTAINER(sw), treeview);
    - gtk_widget_show(treeview);
    -
    - add_columns(treeview, dialog);
    -
    - populate_accounts_list(dialog);
    -
    - /* Setup DND. I wanna be an orc! */
    - gtk_tree_view_enable_model_drag_source(
    - GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK, gte,
    - 1, GDK_ACTION_COPY);
    - gtk_tree_view_enable_model_drag_dest(
    - GTK_TREE_VIEW(treeview), gte, 1,
    - GDK_ACTION_COPY | GDK_ACTION_MOVE);
    -
    - g_signal_connect(G_OBJECT(treeview), "drag-data-received",
    - G_CALLBACK(drag_data_received_cb), dialog);
    - g_signal_connect(G_OBJECT(treeview), "drag-data-get",
    - G_CALLBACK(drag_data_get_cb), dialog);
    -
    - return sw;
    -}
    -
    -static void account_page_delete_cb (GtkObject *object, gpointer data)
    -{
    - g_free (data);
    -}
    -
    -GtkWidget *get_account_page () {
    - GtkWidget *page;
    - GtkWidget *sw;
    - GtkWidget *label;
    - AccountsWindow *accounts_window;
    -
    - /* Make the box */
    - page = gtk_vbox_new (FALSE, 8);
    - gtk_container_set_border_width (GTK_CONTAINER (page), 12);
    -
    - accounts_window = g_new0(AccountsWindow, 1);
    -
    - /* Setup the scrolled window that will contain the list of accounts. */
    - sw = create_accounts_list(accounts_window);
    - gtk_box_pack_start(GTK_BOX(page), sw, TRUE, TRUE, 0);
    -
    - label = gtk_label_new (
    - _("Accounts that do not support user-specified profiles are not shown"));
    - gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
    -
    - g_signal_connect (G_OBJECT (page), "destroy",
    - G_CALLBACK (account_page_delete_cb), accounts_window);
    -
    - return page;
    -}
    -
    -/*----------------------------------------------------------------------------
    - * Behavior Tab
    - *--------------------------------------------------------------------------*/
    -void ap_gtk_prefs_add_summary_option (GtkWidget *widget) {
    - pidgin_prefs_dropdown (widget,
    - "Show AutoProfile summary window",
    - PURPLE_PREF_STRING,
    - "/plugins/gtk/autoprofile/show_summary",
    - "Always", "always", "When away", "away", "Never", "never", NULL);
    -}
    -
    -static void
    -set_idle_away(PurpleSavedStatus *status)
    -{
    - purple_prefs_set_int("/core/savedstatus/idleaway",
    - purple_savedstatus_get_creation_time(status));
    -}
    -
    -static GtkWidget *get_behavior_page () {
    - GtkWidget *page;
    - GtkWidget *label;
    - GtkWidget *frame, *vbox, *hbox;
    - GtkWidget *button, *select, *menu;
    - GtkSizeGroup *sg;
    - gchar *markup;
    -
    - /* Make the box */
    - page = gtk_vbox_new (FALSE, 8);
    - gtk_container_set_border_width (GTK_CONTAINER (page), 12);
    -
    - /*---------- Update frequency ----------*/
    - frame = pidgin_make_frame (page, _("Update frequency"));
    - vbox = gtk_vbox_new (FALSE, 0);
    - gtk_container_add (GTK_CONTAINER (frame), vbox);
    -
    - pidgin_prefs_labeled_spin_button (vbox,
    - _("Minimum number of seconds between updates"),
    - "/plugins/gtk/autoprofile/delay_update",
    - 15, 1000, NULL);
    -
    - label = gtk_label_new ("");
    - markup = g_markup_printf_escaped ("<span style=\"italic\">%s</span>",
    - _("WARNING: Using values below 60 seconds may increase the frequency\n"
    - "of rate limiting errors"));
    - gtk_label_set_markup (GTK_LABEL (label), markup);
    - g_free (markup);
    - gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
    -
    - /*----------- Auto-away stuff ------------*/
    - frame = pidgin_make_frame(page, _("Auto-away"));
    -
    - button = pidgin_prefs_checkbox(_("Change status when idle"),
    - "/plugins/gtk/autoprofile/away_when_idle", frame);
    -
    - sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
    - select = pidgin_prefs_labeled_spin_button(frame,
    - _("Minutes before changing status:"), "/core/away/mins_before_away",
    - 1, 24 * 60, sg);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(pidgin_toggle_sensitive), select);
    -
    - hbox = gtk_hbox_new(FALSE, 0);
    - gtk_container_add(GTK_CONTAINER(frame), hbox);
    -
    - label = gtk_label_new_with_mnemonic(_("Change status to:"));
    - gtk_size_group_add_widget(sg, label);
    - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(pidgin_toggle_sensitive), label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - /* TODO: Show something useful if we don't have any saved statuses. */
    - menu = pidgin_status_menu(purple_savedstatus_get_idleaway(),
    - G_CALLBACK(set_idle_away));
    - gtk_box_pack_start(GTK_BOX(frame), menu, FALSE, FALSE, 0);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(pidgin_toggle_sensitive), menu);
    - gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu);
    -
    - if (!purple_prefs_get_bool("/plugins/gtk/autoprofile/away_when_idle")) {
    - gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE);
    - gtk_widget_set_sensitive(GTK_WIDGET(select), FALSE);
    - gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE);
    - }
    -
    - return page;
    -}
    -
    -/*----------------------------------------------------------------------------
    - * Auto-reply Tab
    - *--------------------------------------------------------------------------*/
    -/* Update string arguments */
    -static gboolean update_behavior_string (GtkWidget *widget, GdkEventFocus *evt,
    - gpointer data)
    -{
    - ap_debug ("preferences", "behavior string preference modified");
    -
    - if (!strcmp (data, "text_trigger")) {
    - purple_prefs_set_string ("/plugins/gtk/autoprofile/autorespond/trigger",
    - gtk_entry_get_text (GTK_ENTRY (widget)));
    - } else if (!strcmp (data, "text_respond")) {
    - purple_prefs_set_string ("/plugins/gtk/autoprofile/autorespond/text",
    - gtk_entry_get_text (GTK_ENTRY (widget)));
    - } else {
    - ap_debug_error ("preferences", "invalid data argument to string update");
    - }
    -
    - return FALSE;
    -}
    -
    -/* Update value returned from spinner for auto-respond delay */
    -static gboolean update_delay_respond (GtkWidget *widget, GdkEventFocus *evt,
    - gpointer data)
    -{
    - purple_prefs_set_int ("/plugins/gtk/autoprofile/delay_respond",
    - gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)));
    - return FALSE;
    -}
    -
    -static GtkWidget *get_autoreply_page () {
    - GtkWidget *page;
    - GtkWidget *label, *checkbox, *spinner, *entry;
    - GtkWidget *frame, *vbox, *large_vbox, *hbox;
    - GtkWidget *dd;
    - GtkSizeGroup *sg;
    -
    - /* Make the box */
    - page = gtk_vbox_new (FALSE, 8);
    - gtk_container_set_border_width (GTK_CONTAINER (page), 12);
    -
    - frame = pidgin_make_frame(page, _("General"));
    -
    - dd = pidgin_prefs_dropdown(frame, _("Auto-reply:"),
    - PURPLE_PREF_STRING, "/plugins/gtk/autoprofile/autorespond/auto_reply",
    - _("Never"), "never",
    - _("When away"), "away",
    - _("When both away and idle"), "awayidle",
    - NULL);
    - sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
    - gtk_size_group_add_widget(sg, dd);
    - gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
    -
    - /*---------- Auto-responses ----------*/
    - frame = pidgin_make_frame (page, _("Dynamic auto-responses"));
    - vbox = gtk_vbox_new (FALSE, 5);
    - gtk_container_add (GTK_CONTAINER (frame), vbox);
    -
    - /* Auto-response activated */
    - checkbox = pidgin_prefs_checkbox (
    - _("Allow users to request more auto-responses"),
    - "/plugins/gtk/autoprofile/autorespond/enable", vbox);
    - large_vbox = gtk_vbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (vbox), large_vbox, FALSE, FALSE, 0);
    -
    - /* Auto-response delay */
    - hbox = gtk_hbox_new (FALSE, 5);
    - gtk_box_pack_start (GTK_BOX (large_vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new (_("Delay"));
    - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    - spinner = gtk_spin_button_new_with_range (1, G_MAXINT, 1);
    - gtk_box_pack_start (GTK_BOX (hbox), spinner, TRUE, TRUE, 0);
    - label = gtk_label_new (_("seconds between auto-responses"));
    - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    - gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), purple_prefs_get_int (
    - "/plugins/gtk/autoprofile/autorespond/delay"));
    - g_signal_connect (G_OBJECT (spinner), "value-changed",
    - G_CALLBACK (update_delay_respond), NULL);
    -
    - /* Auto-response message string */
    - label = gtk_label_new (_("Message sent with first autoresponse:"));
    - gtk_box_pack_start (GTK_BOX (large_vbox), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - entry = gtk_entry_new ();
    - gtk_box_pack_start (GTK_BOX (large_vbox), entry, FALSE, FALSE, 0);
    - gtk_entry_set_max_length (GTK_ENTRY (entry), 100);
    - gtk_entry_set_text (GTK_ENTRY (entry), purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/autorespond/text"));
    - g_signal_connect (G_OBJECT (entry), "focus-out-event",
    - G_CALLBACK (update_behavior_string), "text_respond");
    -
    - label = gtk_label_new (_("Request trigger message:"));
    - gtk_box_pack_start (GTK_BOX (large_vbox), label, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
    - entry = gtk_entry_new ();
    - gtk_box_pack_start (GTK_BOX (large_vbox), entry, FALSE, FALSE, 0);
    - gtk_entry_set_max_length (GTK_ENTRY (entry), 50);
    - gtk_entry_set_text (GTK_ENTRY (entry), purple_prefs_get_string (
    - "/plugins/gtk/autoprofile/autorespond/trigger"));
    - g_signal_connect (G_OBJECT (entry), "focus-out-event",
    - G_CALLBACK (update_behavior_string), "text_trigger");
    -
    - /* Sensitivity signals */
    - g_signal_connect(G_OBJECT(checkbox), "clicked",
    - G_CALLBACK(pidgin_toggle_sensitive), large_vbox);
    - if (!purple_prefs_get_bool ("/plugins/gtk/autoprofile/autorespond/enable")) {
    - gtk_widget_set_sensitive (large_vbox, FALSE);
    - } else {
    - gtk_widget_set_sensitive (large_vbox, TRUE);
    - }
    -
    - return page;
    -}
    -
    -/*----------------------------------------------------------------------------
    - * Menu as a whole
    - *--------------------------------------------------------------------------*/
    -static GtkWidget *get_config_frame (PurplePlugin *plugin)
    -{
    - GtkWidget *info = get_info_page ();
    - gtk_widget_set_size_request (info, 350, 400);
    - return info;
    -}
    -
    -static void dialog_cb (GtkDialog *dialog, gint arg1, gpointer user_data)
    -{
    - gtk_widget_destroy ((GtkWidget *)dialog);
    -}
    -
    -void ap_preferences_display ()
    -{
    - GtkWidget *dialog, *notebook;
    -
    - notebook = gtk_notebook_new ();
    -
    - gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
    - get_behavior_page (), gtk_label_new (_("General")));
    - gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
    - get_account_page (), gtk_label_new (_("User info/profiles")));
    - gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
    - get_autoreply_page (), gtk_label_new (_("Auto-reply")));
    -
    - g_object_set (notebook, "homogeneous", TRUE, NULL);
    -
    - dialog = gtk_dialog_new_with_buttons(PIDGIN_ALERT_TITLE, NULL,
    - GTK_DIALOG_NO_SEPARATOR,
    - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
    - NULL);
    -
    - gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), notebook);
    - gtk_window_set_default_size (GTK_WINDOW(dialog), 400, 400);
    - gtk_widget_show_all (dialog);
    -
    - g_signal_connect (G_OBJECT(dialog), "response",
    - G_CALLBACK(dialog_cb), dialog);
    -}
    -
    -/*--------------- Generate the preference widget once ----------------*/
    -PidginPluginUiInfo ui_info =
    -{
    - get_config_frame
    -};
    -
    --- a/autoprofile/sizes.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,49 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -/* Size definitions (0 indicates not compatible) */
    -#define AP_SIZE_MAXIMUM 2048
    -
    -/* Profile sizes */
    -#define AP_SIZE_PROFILE_MAX 2048
    -#define AP_SIZE_PROFILE_AIM 2048
    -#define AP_SIZE_PROFILE_ICQ 0
    -#define AP_SIZE_PROFILE_JABBER 0
    -#define AP_SIZE_PROFILE_MSN 0
    -#define AP_SIZE_PROFILE_YAHOO 0
    -
    -/* Away message sizes */
    -#define AP_SIZE_AWAY_MAX 2048
    -#define AP_SIZE_AWAY_AIM 2048
    -
    -
    -/* Available message sizes */
    -#define AP_SIZE_AVAILABLE_MAX 512
    -#define AP_SIZE_AVAILABLE_AIM 60
    -#define AP_SIZE_AVAILABLE_ICQ 0
    -// jabber
    -#define AP_SIZE_AVAILABLE_MSN 0
    -#define AP_SIZE_AVAILABLE_YAHOO 512
    -
    -/* End size definitions */
    -
    --- a/autoprofile/utility.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,221 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "autoprofile.h"
    -
    -#include "debug.h"
    -
    -/* TODO: get rid of this and port to glib time */
    -static GStaticMutex time_mutex = G_STATIC_MUTEX_INIT;
    -
    -static struct tm *ap_tm_copy (const struct tm *t) {
    - struct tm *r;
    - r = (struct tm *) malloc (sizeof (struct tm));
    - r->tm_sec = t->tm_sec;
    - r->tm_min = t->tm_min;
    - r->tm_hour = t->tm_hour;
    - r->tm_mday = t->tm_mday;
    - r->tm_mon = t->tm_mon;
    - r->tm_year = t->tm_year;
    - r->tm_wday = t->tm_wday;
    - r->tm_yday = t->tm_yday;
    - r->tm_isdst = t->tm_isdst;
    - return r;
    -}
    -
    -struct tm *ap_localtime (const time_t *tp) {
    - struct tm *result;
    - g_static_mutex_lock (&time_mutex);
    - result = ap_tm_copy (localtime (tp));
    - g_static_mutex_unlock (&time_mutex);
    - return result;
    -}
    -
    -struct tm *ap_gmtime (const time_t *tp) {
    - struct tm *result;
    - g_static_mutex_lock (&time_mutex);
    - result = ap_tm_copy (gmtime (tp));
    - g_static_mutex_unlock (&time_mutex);
    - return result;
    -}
    -
    -/* Reads from fortune-style file and returns GList of each quote */
    -static void fortune_helper (GString *s, gchar *data, gboolean escape_html) {
    - if (*data == '\n') {
    - g_string_append_printf (s, "<br>");
    - return;
    - }
    -
    - if (escape_html) {
    - switch (*data) {
    - case '"': g_string_append_printf (s, "&quot;"); return;
    - case '&': g_string_append_printf (s, "&amp;"); return;
    - case '<': g_string_append_printf (s, "&lt;"); return;
    - case '>': g_string_append_printf (s, "&gt;"); return;
    - }
    - }
    -
    - g_string_append_unichar (s, g_utf8_get_char (data));
    -}
    -
    -GList *read_fortune_file (const char *filename, gboolean escape_html)
    -{
    - int state;
    - gchar *raw_data, *raw_data_start;
    - gchar *converted, *text;
    - GList *quotes = NULL;
    - GString *cur_quote;
    -
    - if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
    - return NULL;
    - }
    -
    - if (!g_file_get_contents (filename, &text, NULL, NULL)) {
    - return NULL;
    - }
    -
    - converted = purple_utf8_try_convert (text);
    - if (converted != NULL) {
    - g_free (text);
    - text = converted;
    - }
    -
    - raw_data_start = raw_data = purple_utf8_salvage (text);
    - g_free (text);
    -
    - purple_str_strip_char (raw_data, '\r');
    -
    - /* Modeling the parser as a finite state machine */
    - state = 0;
    - cur_quote = g_string_new ("");
    - while (*raw_data) {
    - switch (state) {
    - /* State after newline (potential quote) */
    - case 1:
    - if (*raw_data == '%') { // Found it
    - quotes = g_list_append (quotes, strdup (cur_quote->str));
    - g_string_truncate (cur_quote, 0);
    - state = 2;
    - } else {
    - state = 0;
    - g_string_append_printf (cur_quote, "<br>");
    - fortune_helper (cur_quote, raw_data, escape_html);
    - }
    - break;
    -
    - /* State after end of a quote */
    - case 2:
    - if (*raw_data != '\n' && *raw_data != '%') {
    - state = 0;
    - fortune_helper (cur_quote, raw_data, escape_html);
    - }
    - break;
    - /* General state */
    - default:
    - if (*raw_data == '\n') {
    - state = 1;
    - } else {
    - fortune_helper (cur_quote, raw_data, escape_html);
    - }
    - break;
    - }
    -
    - raw_data = g_utf8_next_char (raw_data);
    - }
    -
    - if (strlen (cur_quote->str) > 0) {
    - quotes = g_list_append (quotes, strdup (cur_quote->str));
    - }
    -
    - g_string_free (cur_quote, TRUE);
    - free (raw_data_start);
    - return quotes;
    -}
    -
    -/* Returns 1 if a pattern is found at the start of a string */
    -int match_start (const char *text, const char *pattern)
    -{
    - while (*pattern) {
    - if (!*text || *pattern++ != *text++)
    - return 0;
    - }
    - return 1;
    -}
    -
    -/* Free's a GList as well as the internal contents */
    -void free_string_list (GList *list)
    -{
    - GList *node = list;
    -
    - while (node) {
    - free (node->data);
    - node = node->next;
    - }
    -
    - g_list_free (list);
    -}
    -
    -/* Check if string is in GList */
    -gboolean string_list_find (GList *lst, const char *data)
    -{
    - while (lst) {
    - if (!strcmp (data, (char *) lst->data)) {
    - return TRUE;
    - }
    - lst = lst->next;
    - }
    -
    - return FALSE;
    -}
    -
    -/* Prints out debug messages with repetitive formatting completed */
    -static void auto_debug_helper (
    - PurpleDebugLevel level, const char *category, const char *message)
    -{
    - GString *s;
    -
    - if (message == NULL)
    - message = "NULL";
    -
    - s = g_string_new ("");
    - g_string_printf (s, "%s: %s\n", category, message);
    - purple_debug (level, "autoprofile", "%s", s->str);
    - g_string_free (s, TRUE);
    -}
    -
    -void ap_debug (const char *category, const char *message) {
    - auto_debug_helper (PURPLE_DEBUG_INFO, category, message);
    -}
    -
    -void ap_debug_misc (const char *category, const char *message) {
    - auto_debug_helper (PURPLE_DEBUG_MISC, category, message);
    -}
    -
    -void ap_debug_warn (const char *category, const char *message) {
    - auto_debug_helper (PURPLE_DEBUG_WARNING, category, message);
    -}
    -
    -void ap_debug_error (const char *category, const char *message) {
    - auto_debug_helper (PURPLE_DEBUG_ERROR, category, message);
    -}
    -
    --- a/autoprofile/utility.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,41 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -struct tm *ap_localtime (const time_t *);
    -struct tm *ap_gmtime (const time_t *);
    -
    -GList *read_fortune_file (const char *, gboolean);
    -int match_start (const char *, const char *);
    -void free_string_list (GList *);
    -gboolean string_list_find (GList *, const char *);
    -
    -void ap_debug (const char *, const char *);
    -void ap_debug_misc (const char *, const char *);
    -void ap_debug_warn (const char *, const char *);
    -void ap_debug_error (const char *, const char *);
    -
    -/* RFC 822 Date/Time */
    -#include <time.h>
    -time_t rfc_parse_date_time (const char *data);
    -int rfc_parse_was_gmt ();
    -
    --- a/autoprofile/utility_rfc822.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,191 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include <time.h>
    -#include <string.h>
    -#include <ctype.h>
    -#include <stdio.h>
    -
    -#include "autoprofile.h"
    -
    -static struct tm parsed_datetime;
    -static int parsed_gmttime = 0;
    -
    -/* Strip leading whitespace */
    -static const char* rfc_parse_whitespace (const char *data) {
    - while (*data && isspace (*data))
    - data++;
    - return data;
    -}
    -
    -/* Strip leading digits */
    -static const char* rfc_parse_num (const char *data) {
    - while (*data && isdigit (*data))
    - data++;
    - return data;
    -}
    -
    -/* Strip leading whitespace and digits */
    -static const char* rfc_parse_whitespace_num (const char *data) {
    - while (*data && (isspace (*data) || isdigit (*data)))
    - data++;
    - return data;
    -}
    -
    -
    -static const char* rfc_parse_date (const char *data) {
    - char month[4];
    - int day = 0;
    - int year = 0;
    - int monthnum = 0;
    -
    - sscanf (data, "%d", &day);
    - data = rfc_parse_whitespace_num (data);
    - sscanf (data, "%s", month);
    -
    - if (!strcmp (month, "Jan")) { monthnum = 0; } else
    - if (!strcmp (month, "Feb")) { monthnum = 1; } else
    - if (!strcmp (month, "Mar")) { monthnum = 2; } else
    - if (!strcmp (month, "Apr")) { monthnum = 3; } else
    - if (!strcmp (month, "May")) { monthnum = 4; } else
    - if (!strcmp (month, "Jun")) { monthnum = 5; } else
    - if (!strcmp (month, "Jul")) { monthnum = 6; } else
    - if (!strcmp (month, "Aug")) { monthnum = 7; } else
    - if (!strcmp (month, "Sep")) { monthnum = 8; } else
    - if (!strcmp (month, "Oct")) { monthnum = 9; } else
    - if (!strcmp (month, "Nov")) { monthnum = 10; } else
    - if (!strcmp (month, "Dec")) { monthnum = 11; }
    -
    - data += 3;
    - sscanf (data, "%d", &year);
    - data = rfc_parse_whitespace (data);
    - data = rfc_parse_num (data);
    -
    - if (year < 50) {
    - year += 100;
    - } else if (year > 100) {
    - year -= 1900;
    - }
    -
    - /* Set the values */
    - parsed_datetime.tm_mday = day;
    - parsed_datetime.tm_mon = monthnum;
    - parsed_datetime.tm_year = year;
    -
    - return data;
    -}
    -
    -static const char* rfc_parse_hour (const char *data) {
    - int hour = 0;
    - int minutes = 0;
    - int seconds = 0;
    -
    - sscanf (data, "%d", &hour);
    - data = strchr (data, ':');
    - sscanf (++data, "%d", &minutes);
    -
    - if (strchr (data, ':')) {
    - data = strchr (data, ':') + 1;
    - sscanf (data, "%d", &seconds);
    - data = rfc_parse_whitespace_num (data);
    - }
    -
    - parsed_datetime.tm_hour = hour;
    - parsed_datetime.tm_min = minutes;
    - parsed_datetime.tm_sec = seconds;
    -
    - return data;
    -}
    -
    -static const char *rfc_parse_zone (const char *data) {
    - if (strstr (data, "GMT"))
    - parsed_gmttime = 1;
    - else
    - parsed_gmttime = 0;
    -
    - return data;
    -}
    -
    -static const char* rfc_parse_time (const char *data) {
    - data = rfc_parse_hour (data);
    - data = rfc_parse_zone (data);
    - return data;
    -}
    -
    -static const char* rfc_parse_day (const char *data) {
    - return strchr (data, ',') + 1;
    -}
    -
    -int rfc_parse_was_gmt () {
    - return parsed_gmttime;
    -}
    -
    -time_t rfc_parse_date_time (const char *data) {
    - time_t result;
    - /* Initialize values */
    - parsed_datetime.tm_sec = 0;
    - parsed_datetime.tm_min = 0;
    - parsed_datetime.tm_hour = 0;
    - parsed_datetime.tm_mday = 0;
    - parsed_datetime.tm_mon = 0;
    - parsed_datetime.tm_year = 0;
    - parsed_datetime.tm_isdst = -1;
    -
    - data = rfc_parse_whitespace (data);
    - if (isalpha (*data)) {
    - data = rfc_parse_day (data);
    - }
    -
    - data = rfc_parse_date (data);
    - data = rfc_parse_time (data);
    -
    - result = mktime(&parsed_datetime);
    -
    -#ifndef __BSD_VISIBLE
    - if (rfc_parse_was_gmt ())
    - result -= timezone;
    -#endif
    -
    - return result;
    -}
    -
    -/* DEBUGGING
    -
    -int main () {
    -
    - struct tm *x = rfc_parse_date_time ("Mon, 06 Jun 2005 20:24:18 GMT");
    -
    - printf ("Sec: %d\n", x->tm_sec);
    - printf ("Min: %d\n", x->tm_min);
    - printf ("Hour: %d\n", x->tm_hour);
    - printf ("Day: %d\n", x->tm_mday);
    - printf ("Month: %d\n", x->tm_mon);
    - printf ("Year: %d\n", x->tm_year);
    -
    - printf ("GMT: %d\n", parsed_gmttime);
    -
    - return 0;
    -}
    -
    -*/
    -
    --- a/autoprofile/widget.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,607 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#include "../common/pp_internal.h"
    -
    -#include "widget.h"
    -#include "utility.h"
    -
    -#include <ctype.h>
    -#include <string.h>
    -
    -static GStaticMutex widget_mutex = G_STATIC_MUTEX_INIT;
    -static GList *widgets = NULL;
    -static GHashTable *identifiers = NULL;
    -static GRand *r = NULL;
    -
    -static char *widget_pref = "/plugins/gtk/autoprofile/widgets/widget_ids";
    -
    -static void ap_widget_init_default_statuses ()
    -{
    - // Make sure we don't keep on readding the default statuses if a user
    - // deleted them
    - if (!purple_prefs_exists (widget_pref)) {
    - purple_prefs_add_none ("/plugins/gtk/autoprofile/widgets/42");
    - purple_prefs_add_string (
    - "/plugins/gtk/autoprofile/widgets/42/component", "Timestamp");
    - purple_prefs_add_string (
    - "/plugins/gtk/autoprofile/widgets/42/alias", "Timestamp");
    - purple_prefs_add_string (
    - "/plugins/gtk/autoprofile/widgets/42/timestamp_format",
    - "Automatically created at %I:%M %p");
    - }
    -}
    -
    -void ap_widget_init () {
    - GList *node;
    -
    - ap_widget_init_default_statuses ();
    -
    - node = g_list_append (NULL, g_strdup ("42"));
    - purple_prefs_add_string_list (widget_pref, node);
    - free_string_list (node);
    -}
    -
    -/* Basic functions */
    -static gchar *strip_whitespace (const gchar *text) {
    - gchar *result, *end, *search;
    -
    - while (isspace (*text)) {
    - text++;
    - }
    -
    - end = NULL;
    - search = result = g_strdup (text);
    -
    - while (*search) {
    - if (end == NULL && isspace (*search)) {
    - end = search;
    - }
    - if (!isspace (*search)) {
    - end = NULL;
    - }
    - search++;
    - }
    -
    - if (end != NULL) *end = '\0';
    - return result;
    -}
    -
    -static void update_widget_ids () {
    - GList *cur_node, *ids;
    - struct widget *cur_widget;
    -
    - ids = NULL;
    - for (cur_node = widgets; cur_node != NULL; cur_node = cur_node->next) {
    - cur_widget = (struct widget *) cur_node->data;
    - ids = g_list_append (ids, cur_widget->wid);
    - }
    -
    - purple_prefs_set_string_list (widget_pref, ids);
    - g_list_free (ids);
    -}
    -
    -// Mutex is ALREADY HELD when this function is called
    -static struct widget *ap_widget_find_internal (const gchar *search_text) {
    - GList *cur_node;
    - struct widget *cur_widget;
    - gchar *alias;
    -
    - alias = strip_whitespace (search_text);
    -
    - cur_node = widgets;
    -
    - while (cur_node) {
    - cur_widget = (struct widget *) cur_node->data;
    - if (!purple_utf8_strcasecmp (alias, cur_widget->alias)) {
    - free (alias);
    - return cur_widget;
    - }
    - cur_node = cur_node->next;
    - }
    -
    - free (alias);
    - return NULL;
    -}
    -
    -struct widget *ap_widget_find (const gchar *search_text) {
    - struct widget *w;
    -
    - g_static_mutex_lock (&widget_mutex);
    - w = ap_widget_find_internal (search_text);
    - g_static_mutex_unlock (&widget_mutex);
    - return w;
    -}
    -
    -struct widget *ap_widget_find_by_identifier (const gchar *search_text) {
    - struct widget *w;
    -
    - g_static_mutex_lock (&widget_mutex);
    - w = (struct widget *) g_hash_table_lookup (identifiers, search_text);
    - g_static_mutex_unlock (&widget_mutex);
    - return w;
    -}
    -
    -void ap_widget_start () {
    - GList *widget_identifiers, *widget_identifiers_start;
    - GString *pref_name;
    - const gchar *identifier, *component_identifier;
    - struct component *comp;
    - struct widget *w;
    -
    - g_static_mutex_lock (&widget_mutex);
    -
    - r = g_rand_new ();
    -
    - widgets = NULL;
    - identifiers = g_hash_table_new (g_str_hash, g_str_equal);
    -
    - pref_name = g_string_new ("");
    - widget_identifiers_start = purple_prefs_get_string_list (widget_pref);
    -
    - for (widget_identifiers = widget_identifiers_start;
    - widget_identifiers != NULL;
    - widget_identifiers = widget_identifiers->next) {
    - g_string_printf (pref_name,
    - "/plugins/gtk/autoprofile/widgets/%s/component",
    - (gchar *) widget_identifiers->data);
    -
    - component_identifier = purple_prefs_get_string (pref_name->str);
    - if (component_identifier == NULL) {
    - ap_debug_error ("widget", "widget does not have component information");
    - continue;
    - }
    -
    - comp = ap_component_get_component (component_identifier);
    -
    - if (comp == NULL) {
    - ap_debug_error ("widget", "no component matches widget identifier");
    - continue;
    - }
    -
    - g_string_printf (pref_name,
    - "/plugins/gtk/autoprofile/widgets/%s/alias",
    - (gchar *) widget_identifiers->data);
    -
    - identifier = purple_prefs_get_string (pref_name->str);
    - if (identifier == NULL) {
    - ap_debug_error ("widget", "widget does not have alias information");
    - continue;
    - }
    -
    - w = ap_widget_find_internal (identifier);
    - if (w != NULL) {
    - ap_debug_error ("widget", "widget alias already in use");
    - continue;
    - }
    -
    - w = (struct widget *) malloc (sizeof (struct widget));
    - w->alias = g_strdup (identifier);
    - w->wid = g_strdup ((gchar *) widget_identifiers->data);
    - w->component = comp;
    - w->data = g_hash_table_new (NULL, NULL);
    -
    - widgets = g_list_append (widgets, w);
    - g_hash_table_insert (identifiers, w->wid, w);
    -
    - if (w->component->load) {
    - w->component->load (w);
    - }
    -
    - g_string_printf (pref_name,
    - "loaded saved widget with alias %s and identifier %s",
    - w->alias,
    - w->wid);
    - ap_debug_misc ("widget", pref_name->str);
    - }
    -
    - free_string_list (widget_identifiers_start);
    - g_string_free (pref_name, TRUE);
    -
    - g_static_mutex_unlock (&widget_mutex);
    -
    - ap_widget_gtk_start ();
    -}
    -
    -void ap_widget_finish () {
    - GList *tmp;
    - struct widget *w;
    -
    - g_static_mutex_lock (&widget_mutex);
    -
    - ap_widget_gtk_finish ();
    -
    - g_hash_table_destroy (identifiers);
    - identifiers = NULL;
    -
    - while (widgets) {
    - w = (struct widget *) widgets->data;
    -
    - if (w->component->unload) {
    - w->component->unload (w);
    - }
    -
    - g_hash_table_destroy (w->data);
    - free (w->alias);
    - free (w->wid);
    - free (w);
    -
    - tmp = widgets->next;
    - g_list_free_1 (widgets);
    - widgets = tmp;
    - }
    -
    - g_rand_free (r);
    - r = NULL;
    -
    - g_static_mutex_unlock (&widget_mutex);
    -}
    -
    -gboolean ap_widget_has_content_changed () {
    - GList *node;
    - struct widget *w;
    - gboolean changed = FALSE;
    -
    - g_static_mutex_lock (&widget_mutex);
    - for (node = widgets; node != NULL; node = node->next) {
    - w = (struct widget *) node->data;
    - if (w->component->has_content_changed == NULL ||
    - w->component->has_content_changed (w)) {
    - changed = TRUE;
    - break;
    - }
    - }
    -
    - g_static_mutex_unlock (&widget_mutex);
    - return changed;
    -}
    -
    -GList *ap_widget_get_widgets () {
    - GList *result;
    - g_static_mutex_lock (&widget_mutex);
    - result = g_list_copy (widgets);
    - g_static_mutex_unlock (&widget_mutex);
    - return result;
    -}
    -
    -struct widget *ap_widget_create (struct component *comp)
    -{
    - struct widget *w;
    - GString *s;
    - gchar *identifier, *alias;
    - int i;
    - GList *node;
    -
    - g_static_mutex_lock (&widget_mutex);
    -
    - // Sanity check to make sure we dont "delete" old widgets by
    - // overriding old pref
    - if (identifiers == NULL) {
    - ap_debug_warn ("widget",
    - "tried to create widget when variables unitialized");
    - g_static_mutex_unlock (&widget_mutex);
    - return NULL;
    - }
    -
    - ap_debug ("widget", "instantiating new widget from component");
    -
    - s = g_string_new ("");
    -
    - // Get alias
    - w = ap_widget_find_internal (comp->identifier);
    - alias = NULL; // Stupid compiler
    -
    - if (w == NULL) {
    - alias = g_strdup (comp->identifier);
    - } else {
    - for (i = 1; i < 10000; i++) {
    - g_string_printf (s, "%s%d", comp->identifier, i);
    - w = ap_widget_find_internal (s->str);
    - if (w == NULL) {
    - alias = g_strdup (s->str);
    - break;
    - }
    - }
    -
    - if (i == 10000) {
    - // This would happen....very very rarely...
    - ap_debug_error ("widget", "ran out of aliases for component");
    - g_string_free (s, TRUE);
    - g_static_mutex_unlock (&widget_mutex);
    - return NULL;
    - }
    - }
    -
    - // Get identifier
    - while (TRUE) {
    - i = g_rand_int (r);
    - g_string_printf (s, "%d", i);
    -
    - node = widgets;
    -
    - while (node) {
    - w = (struct widget *) node->data;
    - if (!strcmp (s->str, w->wid)) {
    - break;
    - }
    - node = node->next;
    - }
    -
    - if (node == NULL) {
    - identifier = g_strdup (s->str);
    - break;
    - }
    - }
    -
    - w = (struct widget *) malloc (sizeof (struct widget));
    - w->alias = alias;
    - w->wid = identifier;
    - w->component = comp;
    - w->data = g_hash_table_new (NULL, NULL);
    -
    - widgets = g_list_append (widgets, w);
    - g_hash_table_insert (identifiers, w->wid, w);
    -
    - // Modify Purple prefs
    - update_widget_ids ();
    -
    - g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s", w->wid);
    - purple_prefs_add_none (s->str);
    -
    - g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s/component",
    - w->wid);
    - purple_prefs_add_string (s->str, w->component->identifier);
    -
    - g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s/alias", w->wid);
    - purple_prefs_add_string (s->str, w->alias);
    -
    - // Initialize widget
    - if (w->component->init_pref) {
    - w->component->init_pref (w);
    - }
    - if (w->component->load) {
    - w->component->load (w);
    - }
    -
    - // Cleanup
    - g_string_printf (s, "Created widget with alias %s and identifier %s",
    - alias, identifier);
    - ap_debug ("widget", s->str);
    -
    - g_string_free (s, TRUE);
    -
    -
    - g_static_mutex_unlock (&widget_mutex);
    -
    - return w;
    -}
    -
    -void ap_widget_delete (struct widget *w) {
    - GString *s;
    -
    - if (w == NULL) {
    - ap_debug_error ("widget", "attempt to delete NULL widget");
    - return;
    - }
    -
    - g_static_mutex_lock (&widget_mutex);
    -
    - // Sanity check to make sure we dont "delete" old widgets by
    - // overriding old pref
    - if (identifiers == NULL) {
    - ap_debug_warn ("widget",
    - "tried to delete widget when variables unitialized");
    - g_static_mutex_unlock (&widget_mutex);
    - return;
    - }
    -
    - s = g_string_new ("");
    -
    - g_string_printf (s, "Deleting widget with alias %s and identifier %s",
    - w->alias, w->wid);
    - ap_debug ("widget", s->str);
    -
    - widgets = g_list_remove (widgets, w);
    - g_hash_table_remove (identifiers, w->wid);
    -
    - update_widget_ids ();
    -
    - g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s", w->wid);
    - purple_prefs_remove (s->str);
    -
    - g_string_free (s, TRUE);
    -
    - if (w->component->unload) {
    - w->component->unload (w);
    - }
    -
    - g_hash_table_destroy (w->data);
    - free (w->wid);
    - free (w->alias);
    - free (w);
    -
    - g_static_mutex_unlock (&widget_mutex);
    -}
    -
    -// TRUE if rename succeeds, FALSE otherwise
    -gboolean ap_widget_rename (struct widget *orig, const gchar *new_alias) {
    - struct widget *w;
    - GString *s;
    - gchar *orig_alias;
    -
    - g_static_mutex_lock (&widget_mutex);
    -
    - w = ap_widget_find_internal (new_alias);
    - if (w != NULL && w != orig) {
    - g_static_mutex_unlock (&widget_mutex);
    - return FALSE;
    - }
    -
    - orig_alias = orig->alias;
    - orig->alias = g_strdup (new_alias);
    -
    - s = g_string_new ("");
    -
    - g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s/alias", orig->wid);
    - purple_prefs_set_string (s->str, new_alias);
    -
    - g_string_printf (s, "Changed alias of widget from %s to %s",
    - orig_alias, new_alias);
    - ap_debug ("widget", s->str);
    -
    - free (orig_alias);
    - g_string_free (s, TRUE);
    -
    - g_static_mutex_unlock (&widget_mutex);
    - return TRUE;
    -}
    -
    -/* Widget data galore! */
    -void ap_widget_set_data (struct widget *w, int id, gpointer data) {
    - g_static_mutex_lock (&widget_mutex);
    - g_hash_table_insert (w->data, GINT_TO_POINTER(id), data);
    - g_static_mutex_unlock (&widget_mutex);
    -}
    -
    -gpointer ap_widget_get_data (struct widget *w, int id) {
    - gpointer result;
    -
    - g_static_mutex_lock (&widget_mutex);
    - result = g_hash_table_lookup (w->data, GINT_TO_POINTER(id));
    - g_static_mutex_unlock (&widget_mutex);
    -
    - return result;
    -}
    -
    -/* Widget preferences galore! */
    -gchar *ap_prefs_get_pref_name (struct widget *w, const char *name) {
    - GString *s;
    - gchar *result;
    -
    - s = g_string_new ("");
    - g_string_append (s, "/plugins/gtk/autoprofile/widgets/");
    - g_string_append_printf (s, "%s/%s", w->wid, name);
    -
    - result = s->str;
    - g_string_free (s, FALSE);
    - return result;
    -}
    -
    -void ap_prefs_add_bool (struct widget *w, const char *name, gboolean value) {
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_add_bool (pref, value);
    - free (pref);
    -}
    -
    -void ap_prefs_add_int (struct widget *w, const char *name, int value) {
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_add_int (pref, value);
    - free (pref);
    -}
    -
    -void ap_prefs_add_none (struct widget *w, const char *name) {
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_add_none (pref);
    - free (pref);
    -}
    -
    -void ap_prefs_add_string (struct widget *w, const char *name,
    - const char *value)
    -{
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_add_string (pref, value);
    - free (pref);
    -}
    -
    -void ap_prefs_add_string_list (struct widget *w, const char *name,
    - GList *value)
    -{
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_add_string_list (pref, value);
    - free (pref);
    -}
    -
    -gboolean ap_prefs_get_bool (struct widget *w, const char *name) {
    - gboolean result;
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - result = purple_prefs_get_bool (pref);
    - free (pref);
    - return result;
    -}
    -
    -int ap_prefs_get_int (struct widget *w, const char *name) {
    - int result;
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - result = purple_prefs_get_int (pref);
    - free (pref);
    - return result;
    -}
    -
    -const char *ap_prefs_get_string (struct widget *w, const char *name) {
    - const char *result;
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - result = purple_prefs_get_string (pref);
    - free (pref);
    - return result;
    -}
    -
    -GList *ap_prefs_get_string_list (struct widget *w, const char *name) {
    - GList *result;
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - result = purple_prefs_get_string_list (pref);
    - free (pref);
    - return result;
    -}
    -
    -void ap_prefs_set_bool (struct widget *w, const char *name, gboolean value) {
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_set_bool (pref, value);
    - free (pref);
    - ap_widget_prefs_updated (w);
    -}
    -
    -void ap_prefs_set_int (struct widget *w, const char *name, int value) {
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_set_int (pref, value);
    - free (pref);
    - ap_widget_prefs_updated (w);
    -}
    -
    -void ap_prefs_set_string (struct widget *w, const char *name,
    - const char *value)
    -{
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_set_string (pref, value);
    - free (pref);
    - ap_widget_prefs_updated (w);
    -}
    -
    -void ap_prefs_set_string_list (struct widget *w, const char *name,
    - GList *value)
    -{
    - gchar *pref = ap_prefs_get_pref_name (w, name);
    - purple_prefs_set_string_list (pref, value);
    - free (pref);
    - ap_widget_prefs_updated (w);
    -}
    -
    --- a/autoprofile/widget.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,96 +0,0 @@
    -/*--------------------------------------------------------------------------*
    - * AUTOPROFILE *
    - * *
    - * A Purple away message and profile manager that supports dynamic text *
    - * *
    - * AutoProfile is the legal property of its developers. Please refer to *
    - * the COPYRIGHT file distributed with this source distribution. *
    - * *
    - * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
    - *--------------------------------------------------------------------------*/
    -
    -#ifndef _AP_WIDGET_H_
    -#define _AP_WIDGET_H_
    -
    -#include "component.h"
    -
    -#include "pidgin.h"
    -#include "gtkutils.h"
    -
    -/* The heart of everything */
    -struct widget {
    - char *wid;
    - char *alias;
    - struct component *component;
    - GHashTable *data;
    -};
    -
    -void ap_widget_init ();
    -void ap_widget_start ();
    -void ap_widget_finish ();
    -void ap_widget_gtk_start ();
    -void ap_widget_gtk_finish ();
    -
    -/* Basic functions */
    -gboolean ap_widget_has_content_changed ();
    -GList *ap_widget_get_widgets ();
    -
    -struct widget *ap_widget_find (const gchar *);
    -struct widget *ap_widget_find_by_identifier (const gchar *);
    -
    -struct widget *ap_widget_create (struct component *);
    -void ap_widget_delete (struct widget *);
    -
    -// TRUE if rename succeeds, FALSE otherwise
    -gboolean ap_widget_rename (struct widget *, const gchar *);
    -
    -/* GUI functions */
    -GtkWidget *ap_widget_get_config_page ();
    -void ap_widget_prefs_updated (struct widget *);
    -GtkWidget *get_widget_list (GtkWidget *, GtkTreeSelection **);
    -void done_with_widget_list ();
    -
    -/* Widget data galore! */
    -void ap_widget_set_data (struct widget *, int, gpointer);
    -gpointer ap_widget_get_data (struct widget *, int);
    -
    -/* Widget preferences galore! */
    -gchar *ap_prefs_get_pref_name (struct widget *, const char *);
    -GtkWidget *ap_prefs_checkbox (struct widget *, const char *, const char *,
    - GtkWidget *);
    -GtkWidget *ap_prefs_dropdown_from_list (struct widget *, GtkWidget *,
    - const gchar *, PurplePrefType, const char *, GList *);
    -GtkWidget *ap_prefs_labeled_entry (struct widget *, GtkWidget *page,
    - const gchar *, const char *, GtkSizeGroup *);
    -GtkWidget *ap_prefs_labeled_spin_button (struct widget *, GtkWidget *,
    - const gchar *, const char *, int, int, GtkSizeGroup *);
    -
    -void ap_prefs_add_bool (struct widget *, const char *name, gboolean value);
    -void ap_prefs_add_int (struct widget *, const char *name, int value);
    -void ap_prefs_add_none (struct widget *, const char *name);
    -void ap_prefs_add_string (struct widget *, const char *, const char *);
    -void ap_prefs_add_string_list (struct widget *, const char *, GList *);
    -
    -gboolean ap_prefs_get_bool (struct widget *, const char *name);
    -int ap_prefs_get_int (struct widget *, const char *name);
    -const char * ap_prefs_get_string (struct widget *, const char *name);
    -GList * ap_prefs_get_string_list (struct widget *, const char *name);
    -
    -void ap_prefs_set_bool (struct widget *, const char *name, gboolean value);
    -void ap_prefs_set_int (struct widget *, const char *name, int value);
    -void ap_prefs_set_string (struct widget *, const char *name, const char *);
    -void ap_prefs_set_string_list (struct widget *, const char *, GList *);
    -
    -#endif /* _AP_WIDGET_H_ */
    --- a/awaynotify/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# Away State Notification
    -
    -status: incomplete
    -dependencies: libpurple
    -authors: Matt Perry
    -introduced: 1.0beta6
    -
    -Shows when someone goes away/back in a conversation.
    -
    --- a/awaynotify/awaynotify.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,268 +0,0 @@
    -/*
    - * awaynotify - show notices when status changes
    - * Copyright (C) 2005-2008 Matt Perry <guy@somewhere.fscked.org>
    - *
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include <blist.h>
    -#include <conversation.h>
    -#include <debug.h>
    -#include <signals.h>
    -
    -#include <plugin.h>
    -#include <pluginpref.h>
    -#include <prefs.h>
    -
    -#include <string.h>
    -
    -#define PLUGIN_ID "core-plugin_pack-awaynotify"
    -#define CHECK_AWAY_MESSAGE_TIME_MS 1000
    -
    -typedef struct _Infochecker Infochecker;
    -
    -struct _Infochecker {
    - PurpleAccount *account;
    - char *buddy;
    - guint timeout_id;
    -};
    -
    -GList* infochecker_list = NULL;
    -
    -static gint infocheck_timeout(gpointer data);
    -
    -static Infochecker* infocheck_new(PurpleAccount* account, char* buddy)
    -{
    - Infochecker* checker = g_new0(Infochecker, 1);
    - checker->account = account;
    - checker->buddy = g_strdup(buddy);
    -
    - return checker;
    -}
    -
    -static void infocheck_delete(Infochecker* checker)
    -{
    - g_free(checker->buddy);
    - g_free(checker);
    -}
    -
    -static void infocheck_remove(GList* node)
    -{
    - Infochecker* checker = (Infochecker*)node->data;
    -
    - g_source_remove(checker->timeout_id);
    - infochecker_list = g_list_remove_link(infochecker_list, node);
    -
    - infocheck_delete(checker);
    -}
    -
    -static gint infocheck_compare(gconstpointer pa, gconstpointer pb)
    -{
    - Infochecker* a = (Infochecker*)pa;
    - Infochecker* b = (Infochecker*)pb;
    -
    - return (a->account == b->account) ? strcmp(a->buddy, b->buddy) : 1;
    -}
    -
    -
    -static void write_status(PurpleBuddy *buddy, const char *message, const char* status)
    -{
    - PurpleConversation *conv;
    - const char *who;
    - char buf[256];
    - char *escaped;
    -
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, buddy->account);
    -
    - if (conv == NULL)
    - return;
    -
    - who = purple_buddy_get_alias(buddy);
    - escaped = g_markup_escape_text(who, -1);
    -
    - g_snprintf(buf, sizeof(buf), message, escaped, status);
    - g_free(escaped);
    -
    - purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
    -}
    -
    -static char* parse_away_message(char* statustext)
    -{
    - char* away_ptr = strstr(statustext, "Away Message:");
    -
    - if (away_ptr == NULL)
    - return g_strdup("");
    -
    - away_ptr += 4 + 1 + 7 + 1;
    - if (*away_ptr == '<') {
    - char* tmp = strchr(away_ptr, '>');
    - if (tmp) away_ptr = tmp + 1;
    - }
    -
    - while (*away_ptr == ' ') away_ptr++;
    -
    - return g_strdup(away_ptr);
    -}
    -
    -static char* get_away_message(PurpleBuddy* buddy)
    -{
    - PurplePlugin *prpl;
    - PurplePluginProtocolInfo *prpl_info = NULL;
    - char* statustext = NULL;
    -
    - if (buddy == NULL)
    - return NULL;
    -
    - prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
    - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
    -
    - if (prpl_info && prpl_info->tooltip_text) {
    - const char *end;
    - char *statustext = NULL;
    - PurpleNotifyUserInfo *info = purple_notify_user_info_new();
    - prpl_info->tooltip_text(buddy, info, TRUE);
    - statustext = purple_notify_user_info_get_text_with_newline(info, "\n");
    - purple_notify_user_info_destroy(info);
    -
    - if (statustext && !g_utf8_validate(statustext, -1, &end)) {
    - char *new = g_strndup(statustext, g_utf8_pointer_to_offset(statustext, end));
    - g_free(statustext);
    - statustext = new;
    - }
    - }
    -
    - if (statustext) {
    - char* away_message = parse_away_message(statustext);
    - g_free(statustext);
    - return away_message;
    - } else
    - return NULL;
    -}
    -
    -static gint infocheck_timeout(gpointer data)
    -{
    - GList* node = (GList*)data;
    - Infochecker* checker = node ? (Infochecker*)node->data : NULL;
    - PurpleBuddy* buddy;
    - char* away_message;
    -
    - if (node == NULL || checker == NULL) {
    - purple_debug_warning("awaynotify", "checker called without being active!\n");
    - return FALSE;
    - }
    -
    - buddy = purple_find_buddy(checker->account, checker->buddy);
    - away_message = get_away_message(buddy);
    -
    - if (away_message == NULL) {
    - /* He must have signed off or there was some other error. Give up. */
    - infocheck_remove(node);
    - return FALSE;
    - }
    -
    - if (away_message[0] == 0) {
    - /* Not away yet. Return true to try again. */
    - g_free(away_message);
    - return TRUE;
    - }
    -
    - write_status(buddy, _("%s is away: %s"), away_message);
    -
    - g_free(away_message);
    - infocheck_remove(node);
    -
    - return FALSE;
    -}
    -
    -static void infocheck_add(Infochecker* checker)
    -{
    - infochecker_list = g_list_prepend(infochecker_list, checker);
    - checker->timeout_id = g_timeout_add(CHECK_AWAY_MESSAGE_TIME_MS,
    - infocheck_timeout, g_list_first(infochecker_list));
    -}
    -
    -static void buddy_away_cb(PurpleBuddy *buddy, void *data)
    -{
    - if (purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, buddy->account) == NULL)
    - return; /* Ignore if there's no conv open. */
    -
    - infocheck_add(infocheck_new(buddy->account, buddy->name));
    -}
    -
    -static void buddy_unaway_cb(PurpleBuddy *buddy, void *data)
    -{
    - GList* node = g_list_find_custom(infochecker_list, buddy->name, infocheck_compare);
    -
    - if (node)
    - infocheck_remove(node);
    -
    - write_status(buddy, _("%s is no longer away."), NULL);
    -}
    -
    -static gboolean plugin_load(PurplePlugin *plugin)
    -{
    - void *blist_handle = purple_blist_get_handle();
    -
    - purple_signal_connect(blist_handle, "buddy-away",
    - plugin, PURPLE_CALLBACK(buddy_away_cb), NULL);
    - purple_signal_connect(blist_handle, "buddy-back",
    - plugin, PURPLE_CALLBACK(buddy_unaway_cb), NULL);
    -
    - return TRUE;
    -}
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD, /**< type */
    - NULL, /**< ui_requirement */
    - 0, /**< flags */
    - NULL, /**< dependencies */
    - PURPLE_PRIORITY_DEFAULT, /**< priority */
    -
    - PLUGIN_ID, /**< id */
    - NULL, /**< name */
    - PP_VERSION, /**< version */
    - NULL, /** summary */
    - NULL, /** description */
    - "Matt Perry <guy@somewhere.fscked.org>", /**< author */
    - PP_WEBSITE, /**< homepage */
    -
    - plugin_load, /**< load */
    - NULL, /**< unload */
    - NULL, /**< destroy */
    -
    - NULL, /**< ui_info */
    - NULL, /**< extra_info */
    - NULL, /**< prefs_info */
    - NULL
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin)
    -{
    - info.name = _("Away State Notification");
    - info.summary =
    - _("Notifies in a conversation window when a buddy goes or returns from away");
    - info.description = info.summary;
    -}
    -
    -PURPLE_INIT_PLUGIN(statenotify, init_plugin, info)
    -
    --- a/awaynotify/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('incomplete') and PURPLE.found()
    - awaynotify = shared_library('awaynotify',
    - 'awaynotify.c',
    - dependencies : [PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'awaynotify'
    -endif
    --- a/bit/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -# Buddy Icon Tools
    -
    -status: incomplete
    -dependencies: pidgin
    -authors: Peter Lawler
    -introduced: 1.0beta1
    -
    -Tools to manipulate buddy icons *DANGEROUS*.
    -
    -Whilst working on Purple 2.0.0, I found a need to destroy all my buddies' buddy icons. There's nothing to do these functions in Purple, so here they are. Completely, thoroughly untested.
    -
    --- a/bit/bit.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,240 +0,0 @@
    -/*
    - * Copyright (C) 2005-2008 Peter Lawler <bleeter from users.sf.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 the Free Software Foundation; either version 2
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include <buddyicon.h>
    -#include <debug.h>
    -#include <notify.h>
    -#include <plugin.h>
    -#include <request.h>
    -
    -PurplePlugin *bit = NULL; /* the request api prefers this for a plugin */
    -static PurpleBuddyList *buddies = NULL;
    -
    -/* TODO: Add a function to clear unused icons */
    -/* TODO: Ensure all this stuff I have at the moment is safe for others to use */
    -
    -static void
    -blist_iterate_action(gboolean remove)
    -{
    - PurpleBlistNode *node = NULL;
    - PurpleConversation *conv = NULL;
    -
    - /* this grabs the purple buddy list, which will be walked through */
    - buddies = purple_get_blist();
    -
    - /* Use the utility function to loop over the nodes of the tree */
    - for (node = buddies->root; node && PURPLE_BLIST_NODE_IS_BUDDY(node);
    - node = purple_blist_node_next(node, TRUE)) {
    - PurpleBuddy *buddy = (PurpleBuddy *)node;
    - const char *tmpname = purple_buddy_get_name(buddy);
    - PurpleBuddyIcon *icon = purple_buddy_get_icon(buddy);
    - if (icon != NULL) {
    -#if 0
    - purple_debug_info("bit", "Processing %s (%p)\n", tmpname, icon);
    - if (!icon->ref_count > 0 && remove == TRUE) {
    - for ( n = icon->ref_count; n !=0; n-- ) {
    - purple_debug_info("bit", "ref_count: %d\n", n);
    - purple_buddy_icon_unref(icon);
    - }
    - }
    -#endif
    - /* XXX: This *may* cause a segfault. - Sadrul */
    - if (remove == TRUE) {
    - purple_debug_info("bit", "Uncaching icon for %s\n", tmpname);
    -#if 0
    - /* XXX: The new buddy icon API doesn't have purple_buddy_icon_uncache() */
    - purple_buddy_icon_uncache(buddy);
    -#endif
    - /* XXX: This *definately* causes a segfault. From reading the
    - * source, I may not need to unref but just straight destroy it
    - * haven't played/investigated enough to decide if I want to
    - * keep/move/delete this - Bleeter
    -
    - purple_debug_info("bit", "Destroying icon for %s\n", tmpname);
    - purple_buddy_icon_destroy(icon);*/
    - }
    - } else {
    - if (remove == TRUE)
    - purple_debug_info("bit", "No icon to flush for %s\n", tmpname);
    - }
    -
    - if (purple_account_is_connected(purple_buddy_get_account(buddy))) {
    - purple_debug_info("bit", "Updating icon for %s\n",
    - tmpname);
    - purple_blist_update_buddy_icon(buddy);
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
    - tmpname,purple_buddy_get_account(buddy));
    - if (conv != NULL)
    - purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON);
    - }
    - }
    -}
    -
    -static void
    -flush_buddy_icon_action(PurplePluginAction *action)
    -{
    - blist_iterate_action(TRUE);
    -}
    -
    -static void
    -refresh_buddy_icon_action(PurplePluginAction *action)
    -{
    - blist_iterate_action(FALSE);
    -}
    -
    -#if 0
    -static void
    -destroy_unused_icons_action(PurplePluginAction *action)
    -{
    - GDir *dir;
    - GList *l;
    - const char *path, *filename;
    - const char *type;
    -
    - path = purple_buddy_icons_get_cache_dir();
    - if (path == NULL) {
    - gchar *str;
    - str = g_strdup_printf(_("Unable to locate the buddy icon cache directory %s"), path);
    - purple_debug_error("bit", str);
    - purple_notify_error(bit, _("Destroy Unused Icons"), _("Unable to locate"),
    - str);
    - return;
    - }
    -
    - if (!(dir = g_dir_open(path, 0, NULL))) {
    - gchar *str;
    - str = g_strdup_printf(_("Unable to read the buddy icon cache directory %s"), path);
    - purple_debug_error("bit", str);
    - purple_notify_error(bit, _("Destroy Unused Icons"), _("Unable to read"),
    - str);
    - return;
    - }
    -
    - while ((filename = g_dir_read_name(dir))) {
    -
    - PurpleBlistNode *cur_node = NULL;
    - PurpleConversation *conv = NULL;
    - gint n;
    -
    - buddies = purple_get_blist();
    - for (cur_node = buddies->root; cur_node;
    - cur_node = purple_blist_node_next(cur_node, TRUE)) {
    - if(PURPLE_BLIST_NODE_IS_BUDDY(cur_node)) {
    - PurpleBuddy *buddy = (PurpleBuddy *)cur_node;
    - const char *tmpname = purple_buddy_get_name(buddy);
    - PurpleBuddyIcon *icon = purple_buddy_get_icon(buddy);
    - if (icon != NULL) {
    - /* store each found icon FILENAME into *l */
    - /* checksums are done in prpl, so don't bother trying */
    - }
    - }
    - }
    - /* remove files not in SOMWHERE*/
    - purple_debug_info("bit", "Filename %s\n", filename);
    - type = purple_buddy_icon_get_type(filename);
    - purple_debug_info("bit", "Type %s\n", type);
    - }
    -}
    -#endif
    -
    -static GList *
    -bit_actions(PurplePlugin *plugin, gpointer context)
    -{
    - GList *list = NULL;
    - PurplePluginAction *act = NULL;
    -
    -#if 0
    -/* buddy icon structs currently suck, I think
    - it's impossible to tell from a filename which buddy it's associated with
    - without going through every file, and the blist...
    - ... a huge hash type table *may help*, but I'd consider it highly inefficient
    - then again, some of the stuff in here ain't exactly a TGV either */
    -
    - act = purple_plugin_action_new(_("Destroy Unused Icons"),
    - destroy_unused_icons_action);
    - list = g_list_append(list, act);
    -#endif
    - act = purple_plugin_action_new(_("Flush Buddy Icons"),
    - flush_buddy_icon_action);
    - list = g_list_append(list, act);
    -
    - act = purple_plugin_action_new(_("Refresh Buddy Icons"),
    - refresh_buddy_icon_action);
    - list = g_list_append(list, act);
    -
    - purple_debug_info("bit", "Action list created\n");
    -
    - return list;
    -}
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC, /**< magic */
    - PURPLE_MAJOR_VERSION, /**< major version */
    - PURPLE_MINOR_VERSION, /**< minor version */
    - PURPLE_PLUGIN_STANDARD, /**< type */
    - NULL, /**< ui_requirement */
    - 0, /**< flags */
    - NULL, /**< dependencies */
    - PURPLE_PRIORITY_DEFAULT, /**< priority */
    -
    - "core-plugin_pack-bit", /**< id */
    - NULL, /**< name */
    - PP_VERSION, /**< version */
    - NULL, /** summary */
    - NULL, /** description */
    - "Peter Lawler <bleeter from users.sf.net>",
    - /**< authors */
    - PP_WEBSITE, /**< homepage */
    -
    - NULL, /**< load */
    - NULL, /**< unload */
    - NULL, /**< destroy */
    -
    - NULL, /**< ui_info */
    - NULL, /**< extra_info */
    - NULL, /**< prefs_info */
    - bit_actions, /**< actions */
    - NULL, /**< reserved 1 */
    - NULL, /**< reserved 2 */
    - NULL, /**< reserved 3 */
    - NULL /**< reserved 4 */
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin)
    -{
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif
    - info.name = _("Buddy Icon Tools");
    - info.summary = _("Tools to manipulate buddy icons. *DANGEROUS*");
    - info.description = _("Whilst working on Purple 2.0.0, I found a need to "
    - "destroy all my buddies' buddy icons. There's nothing to do "
    - "these functions in Purple, so here they are. Completely, "
    - "thoroughly untested.");
    -
    - bit = plugin; /* handle needed for request API file selector */
    -}
    -
    -PURPLE_INIT_PLUGIN(bit, init_plugin, info)
    -
    --- a/bit/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('incomplete') and PIDGIN.found()
    - bit = shared_module('bit',
    - 'bit.c',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'bit'
    -endif
    --- a/buddytime/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -# Buddy Time
    -
    -status: incomplete
    -dependencies: libpurple, pidgin (optional)
    -authors: Gary Kramlich, Richard Laager
    -introduced: 2.2.0
    -
    -Quickly see the local time of a buddy.
    -
    -Status: incomplete
    -
    --- a/buddytime/buddyedit.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,412 +0,0 @@
    -/*************************************************************************
    - * Buddy Edit Module
    - *
    - * A Purple plugin that adds an edit to to buddies allowing you to change
    - * various details you can't normally change. It also provides a mechanism
    - * for subsequent plugins to add themselves to that dialog.
    - *
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Some code copyright (C) 2006, Richard Laager <rlaager@users.sf.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 the Free Software Foundation; either version 2 of the
    - * License, 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., 59 Temple Place - Suite 330, Boston, MA
    - * 02111-1307, USA.
    - *************************************************************************/
    -
    -#define PURPLE_PLUGINS
    -#define PLUGIN "core-kleptog-buddyedit"
    -
    -#include <glib.h>
    -#include <string.h>
    -
    -#include "notify.h"
    -#include "plugin.h"
    -#include "util.h"
    -#include "version.h"
    -
    -#include "debug.h" /* Debug output functions */
    -#include "request.h" /* Requests stuff */
    -
    -static PurplePlugin *plugin_self;
    -
    -static void
    -buddyedit_editcomplete_cb(PurpleBlistNode * data, PurpleRequestFields * fields)
    -{
    - gboolean blist_destroy = FALSE;
    -
    - PurpleBlistNode *olddata = data; /* Keep pointer in case we need to destroy it */
    -
    - /* Account detail stuff */
    - switch (data->type)
    - {
    - case PURPLE_BLIST_BUDDY_NODE:
    - {
    - PurpleBuddy *buddy = (PurpleBuddy *) data;
    - PurpleAccount *account = purple_request_fields_get_account(fields, "account");
    - const char *name = purple_request_fields_get_string(fields, "name");
    - const char *alias = purple_request_fields_get_string(fields, "alias");
    -
    - /* If any details changes, create the buddy */
    - if((account != buddy->account) || strcmp(name, buddy->name))
    - {
    - GHashTable *tmp;
    - PurpleBuddy *newbuddy = purple_buddy_new(account, name, alias);
    - purple_blist_add_buddy(newbuddy, NULL, NULL, data); /* Copy it to correct location */
    -
    - /* Now this is ugly, but we want to copy the settings and avoid issues with memory management */
    - tmp = ((PurpleBlistNode *) buddy)->settings;
    - ((PurpleBlistNode *) buddy)->settings = ((PurpleBlistNode *) newbuddy)->settings;
    - ((PurpleBlistNode *) newbuddy)->settings = tmp;
    -
    - blist_destroy = TRUE;
    - data = (PurpleBlistNode *) newbuddy;
    - }
    - else
    - purple_blist_alias_buddy(buddy, alias);
    - break;
    - }
    - case PURPLE_BLIST_CONTACT_NODE:
    - {
    - PurpleContact *contact = (PurpleContact *) data;
    - const char *alias = purple_request_fields_get_string(fields, "alias");
    - purple_contact_set_alias(contact, alias);
    - break;
    - }
    - case PURPLE_BLIST_GROUP_NODE:
    - {
    - PurpleGroup *group = (PurpleGroup *) data;
    - const char *alias = purple_request_fields_get_string(fields, "alias");
    - purple_blist_rename_group(group, alias);
    - break;
    - }
    - case PURPLE_BLIST_CHAT_NODE:
    - {
    - PurpleChat *chat = (PurpleChat *) data;
    - gboolean new_chat = FALSE;
    -
    - PurpleConnection *gc;
    - GList *list = NULL, *tmp;
    - gc = purple_account_get_connection(chat->account);
    - if(PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
    - list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc);
    -
    - PurpleAccount *newaccount = purple_request_fields_get_account(fields, "account");
    -
    - /* In Purple2 each prot_chat_entry has a field "required". We use
    - * this to determine if a field is important enough to recreate
    - * the chat if it changes. Non-required fields we just change
    - * in-situ. */
    - if(newaccount != chat->account)
    - new_chat = TRUE;
    - else
    - {
    - const char *oldvalue, *newvalue;
    -
    - for (tmp = g_list_first(list); tmp && !new_chat; tmp = g_list_next(tmp))
    - {
    - struct proto_chat_entry *pce = tmp->data;
    - if(!pce->required) /* Only checking required fields at this point */
    - continue;
    - if(pce->is_int)
    - continue; /* Not yet */
    - oldvalue = g_hash_table_lookup(chat->components, pce->identifier);
    - newvalue = purple_request_fields_get_string(fields, pce->identifier);
    -
    - if(oldvalue == NULL)
    - oldvalue = "";
    - if(newvalue == NULL)
    - newvalue = "";
    - if(strcmp(oldvalue, newvalue) != 0)
    - new_chat = TRUE;
    - }
    - }
    -
    - if(new_chat)
    - {
    - const char *oldvalue, *newvalue;
    - GHashTable *components =
    - g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    -
    - for (tmp = g_list_first(list); tmp; tmp = g_list_next(tmp))
    - {
    - struct proto_chat_entry *pce = tmp->data;
    -
    - if(pce->is_int)
    - {
    - oldvalue = g_hash_table_lookup(chat->components, pce->identifier);
    - g_hash_table_replace(components, g_strdup(pce->identifier),
    - g_strdup(oldvalue));
    - }
    - else
    - {
    - newvalue = purple_request_fields_get_string(fields, pce->identifier);
    - g_hash_table_replace(components, g_strdup(pce->identifier),
    - g_strdup(newvalue));
    - }
    - }
    - PurpleChat *newchat = purple_chat_new(newaccount, NULL, components);
    - purple_blist_add_chat(newchat, NULL, data); /* Copy it to correct location */
    - data = (PurpleBlistNode *) newchat;
    - blist_destroy = TRUE;
    - }
    - else /* Just updating values in old chat */
    - {
    - const char *newvalue;
    - for (tmp = g_list_first(list); tmp; tmp = g_list_next(tmp))
    - {
    - struct proto_chat_entry *pce = tmp->data;
    - if(pce->required)
    - continue;
    - if(pce->is_int)
    - {
    - /* Do nothing, yet */
    - }
    - else
    - {
    - newvalue = purple_request_fields_get_string(fields, pce->identifier);
    - g_hash_table_replace(chat->components, g_strdup(pce->identifier),
    - g_strdup(newvalue));
    - }
    - }
    - }
    -
    - const char *alias = purple_request_fields_get_string(fields, "alias");
    - purple_blist_alias_chat(chat, alias);
    - break;
    - }
    - case PURPLE_BLIST_OTHER_NODE:
    - break;
    - }
    -
    - purple_signal_emit(purple_blist_get_handle(), PLUGIN "-submit-fields", fields, data);
    -
    - if(blist_destroy)
    - {
    - if(olddata->type == PURPLE_BLIST_BUDDY_NODE)
    - purple_blist_remove_buddy((PurpleBuddy *) olddata);
    - else if(olddata->type == PURPLE_BLIST_CHAT_NODE)
    - purple_blist_remove_chat((PurpleChat *) olddata);
    - }
    -
    - /* Save any changes */
    - purple_blist_schedule_save();
    -}
    -
    -static PurpleAccount *buddyedit_account_filter_func_data;
    -
    -static gboolean
    -buddyedit_account_filter_func(PurpleAccount * account)
    -{
    - PurplePluginProtocolInfo *gppi1 =
    - PURPLE_PLUGIN_PROTOCOL_INFO(purple_account_get_connection(account)->prpl);
    - PurplePluginProtocolInfo *gppi2 =
    - PURPLE_PLUGIN_PROTOCOL_INFO(purple_account_get_connection(buddyedit_account_filter_func_data)->
    - prpl);
    -
    - return gppi1 == gppi2;
    -}
    -
    -/* Node is either a contact or a buddy */
    -static void
    -buddy_edit_cb(PurpleBlistNode * node, gpointer data)
    -{
    - purple_debug(PURPLE_DEBUG_INFO, PLUGIN, "buddy_edit_cb(%p)\n", node);
    - PurpleRequestFields *fields;
    - PurpleRequestField *field;
    - PurpleRequestFieldGroup *group;
    - char *request_title = NULL;
    -
    - fields = purple_request_fields_new();
    -
    - switch (node->type)
    - {
    - case PURPLE_BLIST_BUDDY_NODE:
    - {
    - PurpleBuddy *buddy = (PurpleBuddy *) node;
    - group = purple_request_field_group_new("Buddy Details");
    - purple_request_fields_add_group(fields, group);
    -
    - field = purple_request_field_account_new("account", "Account", buddy->account);
    - purple_request_field_account_set_show_all(field, TRUE);
    - purple_request_field_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("name", "Name", buddy->name, FALSE);
    - purple_request_field_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("alias", "Alias", buddy->alias, FALSE);
    - purple_request_field_group_add_field(group, field);
    -
    - request_title = "Edit Buddy";
    - break;
    - }
    - case PURPLE_BLIST_CONTACT_NODE:
    - {
    - PurpleContact *contact = (PurpleContact *) node;
    - group = purple_request_field_group_new("Contact Details");
    - purple_request_fields_add_group(fields, group);
    -
    - field = purple_request_field_string_new("alias", "Alias", contact->alias, FALSE);
    - purple_request_field_group_add_field(group, field);
    -
    - request_title = "Edit Contact";
    - break;
    - }
    - case PURPLE_BLIST_GROUP_NODE:
    - {
    - PurpleGroup *grp = (PurpleGroup *) node;
    - group = purple_request_field_group_new("Group Details");
    - purple_request_fields_add_group(fields, group);
    -
    - field = purple_request_field_string_new("alias", "Name", grp->name, FALSE);
    - purple_request_field_group_add_field(group, field);
    -
    - request_title = "Edit Group";
    - break;
    - }
    - case PURPLE_BLIST_CHAT_NODE:
    - {
    - PurpleChat *chat = (PurpleChat *) node;
    - PurpleConnection *gc;
    - GList *list = NULL, *tmp;
    - group = purple_request_field_group_new("Chat Details");
    - purple_request_fields_add_group(fields, group);
    -
    - field = purple_request_field_account_new("account", "Account", chat->account);
    - purple_request_field_account_set_filter(field, buddyedit_account_filter_func);
    - buddyedit_account_filter_func_data = chat->account;
    - purple_request_field_group_add_field(group, field);
    - gc = purple_account_get_connection(chat->account);
    - if(PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
    - list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc);
    -
    - for (tmp = g_list_first(list); tmp; tmp = g_list_next(tmp))
    - {
    - struct proto_chat_entry *pce = tmp->data;
    - const char *value;
    -
    - purple_debug(PURPLE_DEBUG_INFO, PLUGIN,
    - "identifier=%s, label=%s, is_int=%d, required=%d\n", pce->identifier,
    - pce->label, pce->is_int, pce->required);
    -
    - if(pce->is_int)
    - continue; /* Not yet */
    - value = g_hash_table_lookup(chat->components, pce->identifier);
    - field = purple_request_field_string_new(pce->identifier, pce->label, value, FALSE);
    - purple_request_field_set_required(field, pce->required);
    - purple_request_field_group_add_field(group, field);
    - }
    - field = purple_request_field_string_new("alias", "Alias", chat->alias, FALSE);
    - purple_request_field_group_add_field(group, field);
    -
    - request_title = "Edit Chat";
    - break;
    - }
    - default:
    - request_title = "Edit";
    - break;
    - }
    -
    - purple_signal_emit(purple_blist_get_handle(), PLUGIN "-create-fields", fields, node);
    -
    - purple_request_fields(plugin_self, request_title, NULL, NULL, fields,
    - "OK", G_CALLBACK(buddyedit_editcomplete_cb),
    - "Cancel", NULL,
    - NULL, NULL, NULL, /* XXX: These should be set. */
    - node);
    -}
    -
    -static void
    -buddy_menu_cb(PurpleBlistNode * node, GList ** menu, void *data)
    -{
    - PurpleMenuAction *action;
    -
    - switch (node->type)
    - {
    - /* These are the types we handle */
    - case PURPLE_BLIST_BUDDY_NODE:
    - case PURPLE_BLIST_CONTACT_NODE:
    - case PURPLE_BLIST_GROUP_NODE:
    - case PURPLE_BLIST_CHAT_NODE:
    - break;
    -
    - case PURPLE_BLIST_OTHER_NODE:
    - default:
    - return;
    - }
    -
    - action = purple_menu_action_new("Edit...", PURPLE_CALLBACK(buddy_edit_cb), NULL, NULL);
    - *menu = g_list_append(*menu, action);
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin * plugin)
    -{
    -
    - plugin_self = plugin;
    -
    - void *blist_handle = purple_blist_get_handle();
    -
    - purple_signal_register(blist_handle, PLUGIN "-create-fields", /* Called when about to create dialog */
    - purple_marshal_VOID__POINTER_POINTER, NULL, 2, /* (FieldList*,BlistNode*) */
    - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_TYPE_POINTER), /* FieldList */
    - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_BLIST));
    -
    - purple_signal_register(blist_handle, PLUGIN "-submit-fields", /* Called when dialog submitted */
    - purple_marshal_VOID__POINTER_POINTER, NULL, 2, /* (FieldList*,BlistNode*) */
    - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_TYPE_POINTER), /* FieldList */
    - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_BLIST));
    -
    - purple_signal_connect(blist_handle, "blist-node-extended-menu", plugin,
    - PURPLE_CALLBACK(buddy_menu_cb), NULL);
    -
    - return TRUE;
    -}
    -
    -
    -static PurplePluginInfo info = {
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD,
    - NULL,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    -
    - PLUGIN,
    - "Buddy Edit Module",
    - G_STRINGIFY(PLUGIN_VERSION),
    -
    - "Enable editing of buddy properties",
    - "A plugin that adds an edit to to buddies allowing you to change various details you can't normally change. "
    - "It also provides a mechanism for subsequent plugins to add themselves to that dialog. ",
    - "Martijn van Oosterhout <kleptog@svana.org>",
    - "http://buddytools.sf.net",
    -
    - plugin_load,
    - NULL,
    - NULL,
    -
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -init_plugin(PurplePlugin * plugin)
    -{
    -}
    -
    -PURPLE_INIT_PLUGIN(buddyedit, init_plugin, info);
    --- a/buddytime/buddytime.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,490 +0,0 @@
    -/*
    - * Buddy Time - Displays a buddy's local time
    - *
    - * A libpurple plugin that allows you to configure a timezone on a per-contact
    - * basis so it can display the localtime of your contact when a conversation
    - * starts. Convenient if you deal with contacts from many parts of the
    - * world.
    - *
    - * Copyright (C) 2006-2007, Richard Laager <rlaager@pidgin.im>
    - * Copyright (C) 2006, Martijn van Oosterhout <kleptog@svana.org>
    - *
    - * 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 of the
    - * License, 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., 59 Temple Place - Suite 330, Boston, MA
    - * 02111-1307, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include "buddytime.h"
    -#define PLUGIN_STATIC_NAME CORE_PLUGIN_STATIC_NAME
    -#define PLUGIN_ID CORE_PLUGIN_ID
    -
    -#define SETTING_NAME "timezone"
    -#define CONTROL_NAME PLUGIN_ID "-" SETTING_NAME
    -
    -#include <glib.h>
    -#include <errno.h>
    -#include <ctype.h>
    -#include <string.h>
    -
    -#include "conversation.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "plugin.h"
    -#include "request.h"
    -#include "util.h"
    -#include "value.h"
    -#include "version.h"
    -
    -#include "gtkblist.h"
    -
    -#include "recurse.h"
    -
    -#define TIMEZONE_FLAG ((void*)1)
    -#define DISABLED_FLAG ((void*)2)
    -
    -BuddyTimeUiOps *ui_ops = NULL;
    -
    -static PurplePlugin *plugin_self;
    -
    -/* Resolve specifies what the return value should mean:
    - *
    - * If TRUE, it's for display, we want to know the *effect* thus hiding the
    - * "none" value and going to going level to find the default
    - *
    - * If false, we only want what the user enter, thus the string "none" if
    - * that's what it is
    - *
    - * data is here so we can use this as a callback for IPC
    - */
    -static const char *
    -buddy_get_timezone(PurpleBlistNode * node, gboolean resolve, void *data)
    -{
    - PurpleBlistNode *datanode = NULL;
    - const char *timezone;
    -
    - switch (node->type)
    - {
    - case PURPLE_BLIST_BUDDY_NODE:
    - datanode = (PurpleBlistNode *) purple_buddy_get_contact((PurpleBuddy *) node);
    - break;
    - case PURPLE_BLIST_CONTACT_NODE:
    - datanode = node;
    - break;
    - case PURPLE_BLIST_GROUP_NODE:
    - datanode = node;
    - break;
    - default:
    - return NULL;
    - }
    -
    - timezone = purple_blist_node_get_string(datanode, SETTING_NAME);
    -
    - if (!resolve)
    - return timezone;
    -
    - /* The effect of "none" is to stop recursion */
    - if (timezone && strcmp(timezone, "none") == 0)
    - return NULL;
    -
    - if (timezone)
    - return timezone;
    -
    - if (datanode->type == PURPLE_BLIST_CONTACT_NODE)
    - {
    - /* There is no purple_blist_contact_get_group(), though there probably should be */
    - datanode = datanode->parent;
    - timezone = purple_blist_node_get_string(datanode, SETTING_NAME);
    - }
    -
    - if (timezone && strcmp(timezone, "none") == 0)
    - return NULL;
    -
    - return timezone;
    -}
    -
    -/* Calcuates the difference between two struct tm's. */
    -static double
    -timezone_calc_difference(struct tm *remote_tm, struct tm *tmp_tm)
    -{
    - int hours_diff = 0;
    - int minutes_diff = 0;
    -
    - /* Note this only works because the times are
    - * known to be within 24 hours of each other! */
    - if (remote_tm->tm_mday != tmp_tm->tm_mday)
    - hours_diff = 24;
    -
    - hours_diff += (remote_tm->tm_hour - tmp_tm->tm_hour);
    - minutes_diff = (remote_tm->tm_min - tmp_tm->tm_min);
    -
    - return (double)minutes_diff / 60.0 + hours_diff;
    -}
    -
    -/* data is here so we can use this as a callback for IPC */
    -static int
    -timezone_get_time(const char *timezone, struct tm *tm, double *diff, void *data)
    -{
    - time_t now;
    - struct tm *tm_tmp;
    -#ifdef PRIVATE_TZLIB
    - struct state *tzinfo = timezone_load(timezone);
    -
    - if(!tzinfo)
    - return -1;
    -
    - time(&now);
    - localsub(&now, 0, tm, tzinfo);
    - free(tzinfo);
    -#else
    - const gchar *old_tz;
    -
    - /* Store the current TZ value. */
    - old_tz = g_getenv("TZ");
    -
    - g_setenv("TZ", timezone, TRUE);
    -
    - time(&now);
    - tm_tmp = localtime(&now);
    - *tm = *tm_tmp; /* Must copy, localtime uses local buffer */
    -
    - /* Reset the old TZ value. */
    - if (old_tz == NULL)
    - g_unsetenv("TZ");
    - else
    - g_setenv("TZ", old_tz, TRUE);
    -#endif
    -
    - /* Calculate user's localtime, and compare. If same, no output */
    - tm_tmp = localtime(&now);
    -
    - if (tm_tmp->tm_hour == tm->tm_hour && tm_tmp->tm_min == tm->tm_min)
    - return 1;
    -
    - *diff = timezone_calc_difference(tm, tm_tmp);
    - return 0;
    -}
    -
    -static void
    -timezone_createconv_cb(PurpleConversation * conv, void *data)
    -{
    - const char *name;
    - PurpleBuddy *buddy;
    - struct tm tm;
    - const char *timezone;
    - double diff;
    - int ret;
    -
    - if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM)
    - return;
    -
    - name = purple_conversation_get_name(conv);
    - buddy = purple_find_buddy(purple_conversation_get_account(conv), name);
    - if (!buddy)
    - return;
    -
    - timezone = buddy_get_timezone((PurpleBlistNode *) buddy, TRUE, NULL);
    -
    - if (!timezone)
    - return;
    -
    - ret = timezone_get_time(timezone, &tm, &diff, NULL);
    -
    - if (ret == 0)
    - {
    - const char *text = purple_time_format(&tm);
    -
    - char *str;
    - if (diff < 0)
    - {
    - diff = 0 - diff;
    - str = g_strdup_printf(dngettext(GETTEXT_PACKAGE,
    - "Remote Local Time: %s (%.4g hour behind)",
    - "Remote Local Time: %s (%.4g hours behind)", diff),
    - text, diff);
    - }
    - else
    - {
    - str = g_strdup_printf(dngettext(GETTEXT_PACKAGE,
    - "Remote Local Time: %s (%.4g hour ahead)",
    - "Remote Local Time: %s (%.4g hours ahead)", diff),
    - text, diff);
    - }
    -
    - purple_conversation_write(conv, PLUGIN_STATIC_NAME, str, PURPLE_MESSAGE_SYSTEM, time(NULL));
    -
    - g_free(str);
    - }
    -}
    -
    -#if 0
    -static void
    -buddytimezone_submitfields_cb(PurpleRequestFields * fields, PurpleBlistNode * data)
    -{
    - PurpleBlistNode *node;
    - PurpleRequestField *list;
    -
    - /* timezone stuff */
    - purple_debug(PURPLE_DEBUG_INFO, PLUGIN_STATIC_NAME, "buddytimezone_submitfields_cb(%p,%p)\n", fields, data);
    -
    - switch (data->type)
    - {
    - case PURPLE_BLIST_BUDDY_NODE:
    - node = (PurpleBlistNode *) purple_buddy_get_contact((PurpleBuddy *) data);
    - break;
    - case PURPLE_BLIST_CONTACT_NODE:
    - case PURPLE_BLIST_GROUP_NODE:
    - /* code handles either case */
    - node = data;
    - break;
    - case PURPLE_BLIST_CHAT_NODE:
    - case PURPLE_BLIST_OTHER_NODE:
    - default:
    - /* Not applicable */
    - return;
    - }
    -
    - list = purple_request_fields_get_field(fields, CONTROL_NAME);
    - if (ui_ops != NULL && ui_ops->get_timezone_menu_selection != NULL)
    - {
    - const char *seldata = ui_ops->get_timezone_menu_selection(list->ui_data);
    - if (seldata == NULL)
    - purple_blist_node_remove_setting(node, SETTING_NAME);
    - else
    - purple_blist_node_set_string(node, SETTING_NAME, seldata);
    - }
    - else
    - {
    - const GList *sellist;
    - void *seldata = NULL;
    - sellist = purple_request_field_list_get_selected(list);
    - if (sellist)
    - seldata = purple_request_field_list_get_data(list, sellist->data);
    -
    - /* Otherwise, it's fixed value and this means deletion */
    - if (seldata == TIMEZONE_FLAG)
    - purple_blist_node_set_string(node, SETTING_NAME, sellist->data);
    - else if (seldata == DISABLED_FLAG)
    - purple_blist_node_set_string(node, SETTING_NAME, "none");
    - else
    - purple_blist_node_remove_setting(node, SETTING_NAME);
    - }
    -}
    -
    -static int
    -buddy_add_timezone_cb(char *filename, void *data)
    -{
    - PurpleRequestField *field = (PurpleRequestField *) data;
    - if (isupper(filename[0]))
    - purple_request_field_list_add(field, filename, TIMEZONE_FLAG);
    - return 0;
    -}
    -
    -static void
    -buddytimezone_createfields_cb(PurpleRequestFields * fields, PurpleBlistNode * data)
    -{
    - purple_debug(PURPLE_DEBUG_INFO, PLUGIN_STATIC_NAME, "buddytimezone_createfields_cb(%p,%p)\n", fields, data);
    - PurpleRequestField *field;
    - PurpleRequestFieldGroup *group;
    - const char *timezone;
    - gboolean is_default;
    -
    - switch (data->type)
    - {
    - case PURPLE_BLIST_BUDDY_NODE:
    - case PURPLE_BLIST_CONTACT_NODE:
    - is_default = FALSE;
    - break;
    - case PURPLE_BLIST_GROUP_NODE:
    - is_default = TRUE;
    - break;
    - case PURPLE_BLIST_CHAT_NODE:
    - case PURPLE_BLIST_OTHER_NODE:
    - default:
    - /* Not applicable */
    - return;
    - }
    -
    - group = purple_request_field_group_new(NULL);
    - purple_request_fields_add_group(fields, group);
    -
    - timezone = buddy_get_timezone(data, FALSE, NULL);
    -
    - if (ui_ops != NULL && ui_ops->create_menu)
    - {
    - field =
    - purple_request_field_new(CONTROL_NAME,
    - is_default ? "Default timezone for group" : "Timezone of contact",
    - PURPLE_REQUEST_FIELD_LIST);
    - field->ui_data = ui_ops->create_menu(timezone);
    - }
    - else
    - {
    - field =
    - purple_request_field_list_new(CONTROL_NAME,
    - is_default ? "Default timezone for group" :
    - "Timezone of contact (type to select)");
    - purple_request_field_list_set_multi_select(field, FALSE);
    - purple_request_field_list_add(field, "<Default>", "");
    - purple_request_field_list_add(field, "<Disabled>", DISABLED_FLAG);
    -
    - recurse_directory("/usr/share/zoneinfo/", buddy_add_timezone_cb, field);
    -
    - if (timezone)
    - {
    - if (strcmp(timezone, "none") == 0)
    - purple_request_field_list_add_selected(field, "<Disabled>");
    - else
    - purple_request_field_list_add_selected(field, timezone);
    - }
    - else
    - purple_request_field_list_add_selected(field, "<Default>");
    - }
    -
    - purple_request_field_group_add_field(group, field);
    -}
    -#endif
    -
    -static void
    -marshal_POINTER__POINTER_BOOL(PurpleCallback cb, va_list args, void *data,
    - void **return_val)
    -{
    - gpointer ret_val;
    - void *arg1 = va_arg(args, void *);
    - gboolean arg2 = va_arg(args, gboolean);
    -
    - ret_val = ((gpointer (*)(void *, gboolean, void *))cb)(arg1, arg2, data);
    -
    - if (return_val != NULL)
    - *return_val = ret_val;
    -}
    -
    -static void
    -marshal_POINTER__POINTER_POINTER_POINTER(PurpleCallback cb, va_list args, void *data,
    - void **return_val)
    -{
    - gpointer ret_val;
    - void *arg1 = va_arg(args, void *);
    - void *arg2 = va_arg(args, void *);
    - void *arg3 = va_arg(args, void *);
    -
    - ret_val = ((gpointer (*)(void *, void *, void *, void *))cb)(arg1, arg2, arg3, data);
    -
    - if (return_val != NULL)
    - *return_val = ret_val;
    -}
    -
    -static gboolean
    -load_ui_plugin(gpointer data)
    -{
    - char *ui_plugin_id;
    - PurplePlugin *ui_plugin;
    -
    - ui_plugin_id = g_strconcat(purple_core_get_ui(), "-", PLUGIN_STATIC_NAME, NULL);
    - ui_plugin = purple_plugins_find_with_id(ui_plugin_id);
    -
    - if (ui_plugin != NULL)
    - {
    - if (!purple_plugin_load(ui_plugin))
    - {
    - purple_notify_error(ui_plugin, NULL, _("Failed to load the Buddy Timezone UI."),
    - ui_plugin->error ? ui_plugin->error : "");
    - }
    - }
    -
    - g_free(ui_plugin_id);
    -
    - return FALSE;
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin * plugin)
    -{
    - plugin_self = plugin;
    -
    - purple_signal_connect(purple_conversations_get_handle(), "conversation-created", plugin,
    - PURPLE_CALLBACK(timezone_createconv_cb), NULL);
    -
    - purple_plugin_ipc_register(plugin, BUDDYTIME_BUDDY_GET_TIMEZONE,
    - PURPLE_CALLBACK(buddy_get_timezone),
    - marshal_POINTER__POINTER_BOOL,
    - purple_value_new(PURPLE_TYPE_STRING),
    - 2,
    - purple_value_new(PURPLE_TYPE_SUBTYPE,
    - PURPLE_SUBTYPE_BLIST_NODE),
    - purple_value_new(PURPLE_TYPE_BOOLEAN));
    -
    - purple_plugin_ipc_register(plugin, BUDDYTIME_TIMEZONE_GET_TIME,
    - PURPLE_CALLBACK(timezone_get_time),
    - marshal_POINTER__POINTER_POINTER_POINTER,
    - purple_value_new(PURPLE_TYPE_INT),
    - 2,
    - purple_value_new(PURPLE_TYPE_POINTER),
    - purple_value_new(PURPLE_TYPE_POINTER));
    -
    - /* This is done as an idle callback to avoid an infinite loop
    - * when we try to load the UI plugin which depends on this plugin
    - * which isn't officially loaded yet. */
    - purple_timeout_add(0, load_ui_plugin, NULL);
    -
    - return TRUE;
    -}
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - 0,
    - PURPLE_PLUGIN_STANDARD, /**< type */
    - NULL, /**< ui_requirement */
    - 0, /**< flags */
    - NULL, /**< dependencies */
    - PURPLE_PRIORITY_DEFAULT, /**< priority */
    - PLUGIN_ID, /**< id */
    - NULL, /**< name */
    - PP_VERSION, /**< version */
    - NULL, /**< summary */
    - NULL, /**< description */
    - PLUGIN_AUTHOR, /**< author */
    - PP_WEBSITE, /**< homepage */
    - plugin_load, /**< load */
    - NULL, /**< unload */
    - NULL, /**< destroy */
    - NULL, /**< ui_info */
    - NULL, /**< extra_info */
    - NULL, /**< prefs_info */
    - NULL, /**< actions */
    - NULL, /**< reserved 1 */
    - NULL, /**< reserved 2 */
    - NULL, /**< reserved 3 */
    - NULL /**< reserved 4 */
    -};
    -
    -static void
    -init_plugin(PurplePlugin * plugin)
    -{
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("Buddy Time");
    - info.summary = _("Quickly see the local time of a buddy");
    - info.description = _("Quickly see the local time of a buddy");
    -}
    -
    -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info);
    --- a/buddytime/buddytime.c.old Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,260 +0,0 @@
    -/*
    - * Plugin Name - Summary
    - * Copyright (C) 2004
    - *
    - * 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 of the
    - * License, 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
    - * 02111-1301, USA.
    - */
    -
    -#include "../common/pp_internal.h"
    -
    -#define PLUGIN_ID "gtk-plugin_pack-buddytime"
    -#define PLUGIN_STATIC_NAME "buddytime"
    -#define PLUGIN_AUTHOR "Gary Kramlich <grim@reaperworld.com>"
    -
    -#include <gdk/gdk.h>
    -#include <gtk/gtk.h>
    -
    -#include <blist.h>
    -#include <gtkutils.h>
    -#include <gtkplugin.h>
    -#include <request.h>
    -
    -/******************************************************************************
    - * Defines
    - *****************************************************************************/
    -#define BT_NODE_SETTING "bt-timezone"
    -
    -/******************************************************************************
    - * Structures
    - *****************************************************************************/
    -typedef struct {
    - PurpleBlistNode *node;
    - PurpleRequestField *timezone;
    - gpointer handle;
    -} BTDialog;
    -
    -typedef struct {
    - GtkWidget *ebox;
    - GtkWidget *label;
    - PurpleConversation *conv;
    - guint timeout;
    -} BTWidget;
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -static GList *dialogs = NULL;
    -static GList *widgets = NULL;
    -
    -/******************************************************************************
    - * Main Stuff
    - *****************************************************************************/
    -static BTWidget *
    -bt_widget_new(PurpleConversation *conv) {
    - BTWidget *ret;
    -
    - g_return_val_if_fail(conv, NULL);
    -
    - ret = g_new0(BTWidget, 1);
    - ret->conv = conv;
    -
    - ret->ebox = gtk_event_box_new();
    -
    - ret->label = gtk_label_new("label");
    - gtk_container_add(GTK_CONTAINER(ret->ebox), ret->label);
    -}
    -
    -/******************************************************************************
    - * Blist Stuff
    - *****************************************************************************/
    -static void
    -bt_dialog_ok_cb(gpointer data, PurpleRequestFields *fields) {
    - BTDialog *dialog = (BTDialog *)data;
    -
    - dialogs = g_list_remove(dialogs, dialog);
    - g_free(dialog);
    -}
    -
    -static void
    -bt_dialog_cancel_cb(gpointer data, PurpleRequestFields *fields) {
    - BTDialog *dialog = (BTDialog *)data;
    -
    - dialogs = g_list_remove(dialogs, dialog);
    - g_free(dialog);
    -}
    -
    -static void
    -bt_show_dialog(PurpleBlistNode *node) {
    - BTDialog *dialog;
    - PurpleRequestFields *fields;
    - PurpleRequestFieldGroup *group;
    - PurpleAccount *account = NULL;
    - gint current = 0;
    -
    - dialog = g_new0(BTDialog, 1);
    -
    - if(!dialog)
    - return;
    -
    - dialog->node = node;
    -
    - current = purple_blist_node_get_int(node, BT_NODE_SETTING);
    -
    - /* TODO: set account from node */
    -
    - /* build the request fields */
    - fields = purple_request_fields_new();
    - group = purple_request_field_group_new(NULL);
    - purple_request_fields_add_group(fields, group);
    -
    - dialog->timezone = purple_request_field_choice_new("timezone",
    - _("_Timezone"), 1);
    - purple_request_field_group_add_field(group, dialog->timezone);
    -
    - purple_request_field_choice_add(dialog->timezone, _("Clear setting"));
    -
    - purple_request_field_choice_add(dialog->timezone, _("GMT-12"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-11"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-10"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-9"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-8"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-7"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-6"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-5"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-4"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-3"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-2"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT-1"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+1"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+2"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+3"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+4"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+5"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+6"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+7"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+8"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+9"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+10"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+11"));
    - purple_request_field_choice_add(dialog->timezone, _("GMT+12"));
    -
    -// purple_request_field_choice_set_default_value(dialog->timezone, current);
    -// purple_request_field_coice_set_value(dialog->timezone, current);
    -
    - /* TODO: set who from blist node */
    - dialog->handle =
    - purple_request_fields(NULL, _("Select timezone"),
    - NULL, "foo", fields,
    - _("OK"), PURPLE_CALLBACK(bt_dialog_ok_cb),
    - _("Cancel"), PURPLE_CALLBACK(bt_dialog_cancel_cb),
    - account, NULL /* who */, NULL, dialog);
    -
    - dialogs = g_list_append(dialogs, dialog);
    -}
    -
    -static void
    -bt_edit_timezone_cb(PurpleBlistNode *node, gpointer data) {
    - bt_show_dialog(node);
    -}
    -
    -static void
    -bt_blist_drawing_menu_cb(PurpleBlistNode *node, GList **menu) {
    - PurpleMenuAction *action;
    -
    - if (purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)
    - return;
    -
    - /* ignore chats and groups */
    - if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_GROUP(node))
    - return;
    -
    - action = purple_menu_action_new(_("Timezone"),
    - PURPLE_CALLBACK(bt_edit_timezone_cb),
    - NULL,
    - NULL);
    - (*menu) = g_list_append(*menu, action);
    -}
    -
    -/******************************************************************************
    - * Conversation stuff
    - *****************************************************************************/
    -
    -
    -/******************************************************************************
    - * Plugin Stuff
    - *****************************************************************************/
    -static gboolean
    -plugin_load(PurplePlugin *plugin) {
    - purple_signal_connect(purple_blist_get_handle(),
    - "blist-node-extended-menu",
    - plugin,
    - PURPLE_CALLBACK(bt_blist_drawing_menu_cb),
    - NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin) {
    - return TRUE;
    -}
    -
    -static PurplePluginInfo info = {
    - PURPLE_PLUGIN_MAGIC, /* Magic */
    - PURPLE_MAJOR_VERSION, /* Purple Major Version */
    - PURPLE_MINOR_VERSION, /* Purple Minor Version */
    - PURPLE_PLUGIN_STANDARD, /* plugin type */
    - PIDGIN_PLUGIN_TYPE, /* ui requirement */
    - 0, /* flags */
    - NULL, /* dependencies */
    - PURPLE_PRIORITY_DEFAULT, /* priority */
    -
    - PLUGIN_ID, /* plugin id */
    - NULL, /* name */
    - PP_VERSION, /* version */
    - NULL, /* summary */
    - NULL, /* description */
    - PLUGIN_AUTHOR, /* author */
    - PP_WEBSITE, /* website */
    -
    - plugin_load, /* load */
    - plugin_unload, /* unload */
    - NULL, /* destroy */
    -
    - NULL, /* ui_info */
    - NULL, /* extra_info */
    - NULL, /* prefs_info */
    - NULL, /* actions */
    - NULL, /* reserved 1 */
    - NULL, /* reserved 2 */
    - NULL, /* reserved 3 */
    - NULL /* reserved 4 */
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin) {
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("Buddy Time");
    - info.summary = _("summary");
    - info.description = _("description");
    -}
    -
    -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
    --- a/buddytime/buddytime.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,52 +0,0 @@
    -/*
    - * Buddy Time - Displays a buddy's local time
    - *
    - * A libpurple plugin that allows you to configure a timezone on a per-contact
    - * basis so it can display the localtime of your contact when a conversation
    - * starts. Convenient if you deal with contacts from many parts of the
    - * world.
    - *
    - * Copyright (C) 2006-2007, Richard Laager <rlaager@users.sf.net>
    - * Copyright (C) 2006, Martijn van Oosterhout <kleptog@svana.org>
    - *
    - * 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 of the
    - * License, 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., 59 Temple Place - Suite 330, Boston, MA
    - * 02111-1307, USA.
    - */
    -#ifndef _BUDDYTIME_H_
    -#define _BUDDYTIME_H_
    -
    -#define CORE_PLUGIN_STATIC_NAME "buddytime"
    -#define CORE_PLUGIN_ID "core-kleptog-" CORE_PLUGIN_STATIC_NAME
    -
    -#define PLUGIN_AUTHOR "Martijn van Oosterhout <kleptog@svana.org>" \
    - "\n\t\t\tRichard Laager <rlaager@pidgin.im>"
    -
    -#define BUDDYTIME_BUDDY_GET_TIMEZONE "buddy_get_timezone"
    -#define BUDDYTIME_TIMEZONE_GET_TIME "timezone_get_time"
    -
    -typedef struct _BuddyTimeUiOps BuddyTimeUiOps;
    -
    -struct _BuddyTimeUiOps
    -{
    - void *(*create_menu)(const char *selected); /**< Creates a timezone menu. */
    - const char * (*get_timezone_menu_selection)(void *ui_data); /**< Retrieves the menu setting. */
    -
    - void (*_buddytime_reserved1)(void);
    - void (*_buddytime_reserved2)(void);
    - void (*_buddytime_reserved3)(void);
    - void (*_buddytime_reserved4)(void);
    -};
    -
    -#endif /* _BUDDYTIME_H_ */
    --- a/buddytime/gtkbuddytime.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,150 +0,0 @@
    -/*
    - * Buddy Time - Displays a buddy's local time
    - *
    - * A libpurple plugin that allows you to configure a timezone on a per-contact
    - * basis so it can display the localtime of your contact when a conversation
    - * starts. Convenient if you deal with contacts from many parts of the
    - * world.
    - *
    - * Copyright (C) 2006-2007, Richard Laager <rlaager@pidgin.im>
    - * Copyright (C) 2006, Martijn van Oosterhout <kleptog@svana.org>
    - *
    - * 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 of the
    - * License, 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., 59 Temple Place - Suite 330, Boston, MA
    - * 02111-1307, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#define PLUGIN_STATIC_NAME "gtkbuddytime"
    -#define PLUGIN_ID PIDGIN_UI "-buddytime"
    -
    -#include "buddytime.h"
    -
    -#include <glib.h>
    -
    -#include "plugin.h"
    -#include "version.h"
    -
    -#include "gtkblist.h"
    -#include "gtkplugin.h"
    -
    -PurplePlugin *core_plugin = NULL;
    -
    -static void
    -buddytimezone_tooltip_cb(PurpleBlistNode * node, char **text, gboolean full, void *data)
    -{
    - char *newtext;
    - const char *timezone;
    - struct tm tm;
    - double diff;
    - int ret;
    -
    - if (!full)
    - return;
    -
    - timezone = purple_plugin_ipc_call(core_plugin, BUDDYTIME_BUDDY_GET_TIMEZONE,
    - NULL, node, TRUE);
    -
    - if (!timezone)
    - return;
    -
    - ret = GPOINTER_TO_INT(purple_plugin_ipc_call(core_plugin, BUDDYTIME_TIMEZONE_GET_TIME,
    - NULL, timezone, &tm, &diff));
    - if (ret < 0)
    - newtext = g_strdup_printf("%s\n<b>Timezone:</b> %s (error)", *text, timezone);
    - else if (ret == 0)
    - {
    - const char *timetext = purple_time_format(&tm);
    -
    - if (diff < 0)
    - {
    - diff = 0 - diff;
    - newtext = g_strdup_printf(dngettext(GETTEXT_PACKAGE,
    - "%s\n<b>Local Time:</b> %s (%.4g hour behind)",
    - "%s\n<b>Local Time:</b> %s (%.4g hours behind)", diff),
    - *text, timetext, diff);
    - }
    - else
    - {
    - newtext = g_strdup_printf(dngettext(GETTEXT_PACKAGE,
    - "%s\n<b>Local Time:</b> %s (%.4g hour ahead)",
    - "%s\n<b>Local Time:</b> %s (%.4g hours ahead)", diff),
    - *text, timetext, diff); }
    - }
    - else
    - return;
    -
    - g_free(*text);
    - *text = newtext;
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin * plugin)
    -{
    - purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip", plugin,
    - PURPLE_CALLBACK(buddytimezone_tooltip_cb), NULL);
    -
    - core_plugin = purple_plugins_find_with_id(CORE_PLUGIN_ID);
    -
    - return (core_plugin != NULL);
    -}
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - 0,
    - PURPLE_PLUGIN_STANDARD, /**< type */
    - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */
    - PURPLE_PLUGIN_FLAG_INVISIBLE, /**< flags */
    - NULL, /**< dependencies */
    - PURPLE_PRIORITY_DEFAULT, /**< priority */
    - PLUGIN_ID, /**< id */
    - NULL, /**< name */
    - PP_VERSION, /**< version */
    - NULL, /**< summary */
    - NULL, /**< description */
    - PLUGIN_AUTHOR, /**< author */
    - PP_WEBSITE, /**< homepage */
    - plugin_load, /**< load */
    - NULL, /**< unload */
    - NULL, /**< destroy */
    - NULL, /**< ui_info */
    - NULL, /**< extra_info */
    - NULL, /**< prefs_info */
    - NULL, /**< actions */
    - NULL, /**< reserved 1 */
    - NULL, /**< reserved 2 */
    - NULL, /**< reserved 3 */
    - NULL /**< reserved 4 */
    -};
    -
    -static void
    -init_plugin(PurplePlugin * plugin)
    -{
    - info.dependencies = g_list_append(info.dependencies, CORE_PLUGIN_ID);
    -
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("Buddy Time (Pidgin UI)");
    - info.summary = _("Pidgin user interface for the Buddy Time plugin.");
    - info.description = _("Pidgin user interface for the Buddy Time plugin.");
    -}
    -
    -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info);
    --- a/buddytime/gtktimezone.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,229 +0,0 @@
    -/*************************************************************************
    - * GTK Timezone widget module
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Licenced under the GNU General Public Licence version 2.
    - *
    - * This module creates the GTK widget used to select timezones. It's here to
    - * clearly seperate the GTK stuff from the plugin itself.
    - *************************************************************************/
    -
    -#include <gtk/gtk.h>
    -#include <string.h>
    -#include <ctype.h>
    -#include "recurse.h"
    -
    -#define DISABLED_STRING "<Disabled>"
    -#define DEFAULT_STRING "<Default>"
    -#define MORE_STRING "More..."
    -
    -struct nodestate
    -{
    - GtkWidget *submenu;
    - gchar *string;
    -};
    -
    -struct state
    -{
    - GtkWidget *base;
    - GtkWidget *extra;
    - int currdepth;
    - struct nodestate stack[4];
    -};
    -
    -static inline const char *
    -menuitem_get_label(GtkMenuItem * menuitem)
    -{
    - return gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(menuitem))));
    -}
    -
    -static GtkMenuItem *
    -menu_get_first_menuitem(GtkWidget * menu)
    -{
    - GList *list = gtk_container_get_children(GTK_CONTAINER(menu));
    - GtkMenuItem *selection = GTK_MENU_ITEM(g_list_nth_data(list, 0));
    - g_list_free(list);
    - return selection;
    -}
    -
    -static int
    -menu_select_cb(GtkMenuItem * menuitem, GtkWidget * menu)
    -{
    - const char *label = menuitem_get_label(menuitem);
    -
    - if(label[0] == '<')
    - {
    - GtkWidget *selection = GTK_WIDGET(menu_get_first_menuitem(menu));
    - gtk_widget_hide(selection);
    - }
    - else
    - {
    - char *str = g_strdup(label);
    -
    - GtkWidget *parent;
    -
    - for (;;)
    - {
    - GtkMenuItem *parentitem;
    - const char *label2;
    - char *temp;
    -
    - parent = gtk_widget_get_parent(GTK_WIDGET(menuitem));
    - if(menu == parent)
    - break;
    -
    - parentitem = GTK_MENU_ITEM(gtk_menu_get_attach_widget(GTK_MENU(parent)));
    - label2 = menuitem_get_label(parentitem);
    - if(strcmp(label2, MORE_STRING) != 0)
    - {
    - temp = g_strconcat(label2, "/", str, NULL);
    - g_free(str);
    - str = temp;
    - }
    -
    - menuitem = parentitem;
    - }
    - {
    - GtkLabel *label;
    -
    - GtkMenuItem *selection = menu_get_first_menuitem(menu);
    - GtkOptionMenu *optionmenu = GTK_OPTION_MENU(gtk_menu_get_attach_widget(GTK_MENU(menu)));
    -
    - label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(selection)));
    -
    - gtk_label_set_text(label, str);
    - gtk_widget_show(GTK_WIDGET(selection));
    - gtk_option_menu_set_history(optionmenu, 0);
    -
    - printf("optionmenu=%p, menu=%p, menuitem=%p, label=%p\n", optionmenu, menu, selection,
    - label);
    - g_free(str);
    - }
    - }
    - return 0;
    -}
    -
    -static int
    -make_menu_cb(char *path, struct state *state)
    -{
    - int i, j;
    -
    - char **elements;
    -
    - /* Here we ignore strings not beginning with uppercase, since they are auxilliary files, not timezones */
    - if(!isupper(path[0]))
    - return 0;
    -
    - elements = g_strsplit(path, "/", 4);
    -
    - for (i = 0; i < state->currdepth && state->stack[i].string; i++)
    - {
    - if(strcmp(elements[i], state->stack[i].string) != 0)
    - break;
    - }
    - /* i is now the index of the first non-matching element, so free the rest */
    - for (j = i; j < state->currdepth; j++)
    - g_free(state->stack[j].string);
    - state->currdepth = i;
    -
    - while (elements[i])
    - {
    - GtkWidget *parent = (i == 0) ? state->base : state->stack[i - 1].submenu;
    - GtkWidget *menuitem;
    -
    - if(i == 0 && elements[1] == NULL)
    - parent = state->extra;
    -
    - menuitem = gtk_menu_item_new_with_label(elements[i]);
    - gtk_menu_append(parent, menuitem);
    -
    - if(elements[i + 1] != NULL) /* Has submenu */
    - {
    - state->stack[i].submenu = gtk_menu_new();
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), state->stack[i].submenu);
    -
    - state->currdepth++;
    - state->stack[i].string = g_strdup(elements[i]);
    - }
    - else
    - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb),
    - state->base);
    -
    - i++;
    - }
    - g_strfreev(elements);
    - return 0;
    -}
    -
    -void *
    -make_timezone_menu(const char *selected)
    -{
    - int i;
    - struct state state;
    -
    - GtkWidget *menu;
    - GtkWidget *optionmenu, *menuitem, *selection;
    -
    - if(selected == NULL)
    - selected = "";
    -
    - menu = gtk_menu_new();
    - menuitem = gtk_menu_item_new_with_label(selected);
    - gtk_menu_append(menu, menuitem);
    - selection = menuitem;
    -
    - menuitem = gtk_menu_item_new_with_label(DISABLED_STRING);
    - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb), menu);
    - gtk_menu_append(menu, menuitem);
    - menuitem = gtk_menu_item_new_with_label(DEFAULT_STRING);
    - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb), menu);
    - gtk_menu_append(menu, menuitem);
    - menuitem = gtk_menu_item_new_with_label(MORE_STRING);
    - gtk_menu_append(menu, menuitem);
    -
    - state.base = menu;
    - state.extra = gtk_menu_new();
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), state.extra);
    -
    - state.currdepth = 0;
    -
    - recurse_directory("/usr/share/zoneinfo", (DirRecurseMatch) make_menu_cb, &state);
    -
    - for (i = 0; i < state.currdepth; i++)
    - g_free(state.stack[i].string);
    -
    - optionmenu = gtk_option_menu_new();
    - gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);
    - gtk_widget_show_all(optionmenu);
    -
    - if(strcmp(selected, "") == 0)
    - {
    - gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 2);
    - gtk_widget_hide(selection);
    - }
    - else if(strcmp(selected, "none") == 0)
    - {
    - gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 1);
    - gtk_widget_hide(selection);
    - }
    - else
    - {
    - gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 0);
    - }
    -
    - return optionmenu;
    -}
    -
    -const char *
    -get_timezone_menu_selection(void *widget)
    -{
    - GtkOptionMenu *menu = GTK_OPTION_MENU(widget);
    -
    - int sel = gtk_option_menu_get_history(menu);
    - if(sel == 2) /* Default */
    - return NULL;
    - if(sel == 1) /* Disabled */
    - return "none";
    -
    - GtkLabel *l = GTK_LABEL(gtk_bin_get_child(GTK_BIN(menu)));
    - return gtk_label_get_text(l);
    -}
    --- a/buddytime/gtktimezonetest.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,335 +0,0 @@
    -/*************************************************************************
    - * GTK Timezone test program
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Licenced under the GNU General Public Licence version 2.
    - *
    - * A test program to play with different ways that user could select from
    - * the huge list of timezones. Eventually things tested here should migrate
    - * to the module itself.
    - *************************************************************************/
    -
    -#include <gtk/gtk.h>
    -#include <string.h>
    -#include <ctype.h>
    -#include "recurse.h"
    -
    -#define PACKAGE "Hello World"
    -#define VERSION "0.1"
    -
    -#define DISABLED_STRING "<Disabled>"
    -#define DEFAULT_STRING "<Default>"
    -#define MORE_STRING "More..."
    -/*
    - * Terminate the main loop.
    - */
    -static void
    -on_destroy(GtkWidget * widget, gpointer data)
    -{
    - gtk_main_quit();
    -}
    -
    -enum
    -{
    - STRING_COLUMN,
    - N_COLUMNS
    -};
    -
    -struct nodestate
    -{
    -#ifdef USE_COMBOBOX
    - GtkTreeIter iter;
    -#else
    - GtkWidget *submenu;
    -#endif
    - gchar *string;
    -};
    -
    -struct state
    -{
    -#ifdef USE_COMBOBOX
    - GtkTreeStore *store;
    - GtkTreeIter *extra;
    -#else
    - GtkWidget *base;
    - GtkWidget *extra;
    -#endif
    - int currdepth;
    - struct nodestate stack[4];
    -};
    -
    -static inline const char *
    -menuitem_get_label(GtkMenuItem * menuitem)
    -{
    - return gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(menuitem))));
    -}
    -
    -static GtkWidget *
    -menu_get_first_menuitem(GtkMenu * menu)
    -{
    - GList *list = gtk_container_get_children(GTK_CONTAINER(menu));
    - GtkWidget *selection = GTK_WIDGET(g_list_nth_data(list, 0));
    - g_list_free(list);
    - return selection;
    -}
    -
    -int
    -menu_select_cb(GtkMenuItem * menuitem, GtkWidget * menu)
    -{
    - const char *label = menuitem_get_label(menuitem);
    -
    -// printf( "menuitem = %s(%p), menu = %s(%p)\n", G_OBJECT_TYPE_NAME(menuitem), menuitem, G_OBJECT_TYPE_NAME(menu), menu );
    -
    - if(label[0] == '<')
    - {
    - GtkWidget *selection = menu_get_first_menuitem(GTK_MENU(menu));
    - gtk_widget_hide(selection);
    - }
    - else
    - {
    - char *str = g_strdup(label);
    -
    - GtkWidget *parent;
    -
    - for (;;)
    - {
    - GtkMenuItem *parentitem;
    - const char *label2;
    - char *temp;
    -
    - parent = gtk_widget_get_parent(GTK_WIDGET(menuitem));
    -// printf( "parent = %s(%p)\n", G_OBJECT_TYPE_NAME(parent), parent);
    - if(menu == parent)
    - break;
    -
    - parentitem = GTK_MENU_ITEM(gtk_menu_get_attach_widget(GTK_MENU(parent)));
    -// printf( "parentitem = %s(%p)\n", G_OBJECT_TYPE_NAME(parentitem), parentitem);
    - label2 = menuitem_get_label(parentitem);
    - if(strcmp(label2, MORE_STRING) != 0)
    - {
    - temp = g_strconcat(label2, "/", str, NULL);
    - g_free(str);
    - str = temp;
    - }
    -
    - menuitem = parentitem;
    - }
    - {
    - GtkLabel *label;
    -
    - GtkWidget *selection = menu_get_first_menuitem(GTK_MENU(menu));
    - GtkOptionMenu *optionmenu = GTK_OPTION_MENU(gtk_menu_get_attach_widget(GTK_MENU(menu)));
    -
    - label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(selection)));
    -
    - gtk_label_set_text(label, str);
    - gtk_widget_show(GTK_WIDGET(selection));
    - gtk_option_menu_set_history(optionmenu, 0);
    - g_free(str);
    - }
    - }
    - return 0;
    -}
    -
    -int
    -make_menu_cb(char *path, struct state *state)
    -{
    - int i, j;
    -
    - char **elements;
    -
    - /* Here we ignore strings not beginning with uppercase, since they are auxilliary files, not timezones */
    - if(!isupper(path[0]))
    - return 0;
    -
    - elements = g_strsplit(path, "/", 4);
    -
    - for (i = 0; i < state->currdepth && state->stack[i].string; i++)
    - {
    - if(strcmp(elements[i], state->stack[i].string) != 0)
    - break;
    - }
    - /* i is now the index of the first non-matching element, so free the rest */
    - for (j = i; j < state->currdepth; j++)
    - g_free(state->stack[j].string);
    - state->currdepth = i;
    -
    - while (elements[i])
    - {
    -#ifdef USE_COMBOBOX
    - GtkTreeIter *parent = (i == 0) ? NULL : &state->stack[i - 1].iter;
    -#else
    - GtkWidget *parent = (i == 0) ? state->base : state->stack[i - 1].submenu;
    - GtkWidget *menuitem;
    -#endif
    -
    - if(i == 0 && elements[1] == NULL)
    - parent = state->extra;
    -
    -#ifdef USE_COMBOBOX
    - gtk_tree_store_append(state->store, &state->stack[i].iter, parent);
    - gtk_tree_store_set(state->store, &state->stack[i].iter, STRING_COLUMN, elements[i], -1);
    - state->stack[i].string = g_strdup(elements[i]);
    - state->currdepth++;
    -#else
    - menuitem = gtk_menu_item_new_with_label(elements[i]);
    - gtk_menu_append(parent, menuitem);
    -
    - if(elements[i + 1] != NULL) /* Has submenu */
    - {
    - state->stack[i].submenu = gtk_menu_new();
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), state->stack[i].submenu);
    -
    - state->currdepth++;
    - state->stack[i].string = g_strdup(elements[i]);
    - }
    - else
    - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb),
    - state->base);
    -
    -#endif
    -
    - i++;
    - }
    - g_strfreev(elements);
    - return 0;
    -}
    -
    -GtkWidget *
    -make_menu2(char *selected)
    -{
    - int i;
    - struct state state;
    -
    -#ifdef USE_COMBOBOX
    - GtkTreeStore *store = gtk_tree_store_new(N_COLUMNS, /* Total number of columns */
    - G_TYPE_STRING); /* Timezone */
    - GtkWidget *tree;
    -
    - GtkCellRenderer *renderer;
    - GtkTreeIter iter1, iter2;
    -
    - gtk_tree_store_append(store, &iter1, NULL); /* Acquire an iterator */
    - gtk_tree_store_set(store, &iter1, STRING_COLUMN, DISABLED_STRING, -1);
    - gtk_tree_store_append(store, &iter1, NULL); /* Acquire an iterator */
    - gtk_tree_store_set(store, &iter1, STRING_COLUMN, DEFAULT_STRING, -1);
    - gtk_tree_store_append(store, &iter2, &iter1);
    - gtk_tree_store_set(store, &iter1, STRING_COLUMN, MORE_STRING, -1);
    -
    - state.store = store;
    - state.extra = &iter1;
    -#else
    -
    - GtkWidget *menu;
    - GtkWidget *optionmenu, *menuitem, *selection;
    -
    - menu = gtk_menu_new();
    - menuitem = gtk_menu_item_new_with_label(selected);
    - gtk_menu_append(menu, menuitem);
    - selection = menuitem;
    -
    - menuitem = gtk_menu_item_new_with_label(DISABLED_STRING);
    - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb), menu);
    - gtk_menu_append(menu, menuitem);
    - menuitem = gtk_menu_item_new_with_label(DEFAULT_STRING);
    - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb), menu);
    - gtk_menu_append(menu, menuitem);
    - menuitem = gtk_menu_item_new_with_label(MORE_STRING);
    - gtk_menu_append(menu, menuitem);
    -
    - state.base = menu;
    - state.extra = gtk_menu_new();
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), state.extra);
    -#endif
    - state.currdepth = 0;
    -
    - recurse_directory("/usr/share/zoneinfo", (DirRecurseMatch) make_menu_cb, &state);
    -
    - for (i = 0; i < state.currdepth; i++)
    - g_free(state.stack[i].string);
    -
    -#ifdef USE_COMBOBOX
    - tree = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
    -
    - renderer = gtk_cell_renderer_text_new();
    - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(tree), renderer, TRUE);
    - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(tree), renderer, "text", 0, NULL);
    -
    - gtk_widget_show_all(tree);
    - return tree;
    -#else
    - optionmenu = gtk_option_menu_new();
    - gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);
    - gtk_widget_show_all(optionmenu);
    -
    - if(strcmp(selected, "") == 0)
    - {
    - gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 2);
    - gtk_widget_hide(selection);
    - }
    - else if(strcmp(selected, "none") == 0)
    - {
    - gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 1);
    - gtk_widget_hide(selection);
    - }
    - else
    - {
    - gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 0);
    - }
    -
    - return optionmenu;
    -#endif
    -
    -}
    -
    -int
    -main(int argc, char *argv[])
    -{
    - GtkWidget *window;
    - GtkWidget *label;
    - GtkWidget *menu;
    - GtkWidget *frame;
    -
    - gtk_init(&argc, &argv);
    -
    - /* create the main, top level, window */
    - window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    -
    - /* give the window a 20px wide border */
    - gtk_container_set_border_width(GTK_CONTAINER(window), 20);
    -
    - /* give it the title */
    - gtk_window_set_title(GTK_WINDOW(window), PACKAGE " " VERSION);
    -
    - /* open it a bit wider so that both the label and title show up */
    - gtk_window_set_default_size(GTK_WINDOW(window), 200, 50);
    -
    - /* Connect the destroy event of the window with our on_destroy function
    - * When the window is about to be destroyed we get a notificaiton and
    - * stop the main GTK loop
    - */
    - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(on_destroy), NULL);
    -
    - /* Create the "Hello, World" label */
    - label = gtk_label_new("Select a timezone:");
    - gtk_widget_show(label);
    -
    - frame = gtk_vbox_new(FALSE, 0);
    - gtk_widget_show(frame);
    -
    - /* and insert it into the main window */
    - gtk_container_add(GTK_CONTAINER(window), frame);
    - gtk_container_add(GTK_CONTAINER(frame), label);
    -
    - menu = make_menu2("none");
    -
    - gtk_container_add(GTK_CONTAINER(frame), menu);
    - gtk_widget_show(menu);
    -
    - /* make sure that everything, window and label, are visible */
    - gtk_widget_show(window);
    -
    - /* start the main loop */
    - gtk_main();
    -
    - return 0;
    -}
    --- a/buddytime/localtime.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1250 +0,0 @@
    -/*************************************************************************
    - * Timezone Module
    - * copied from the Olson Timezone code, licence unchanged.
    - * by Martijn van Oosterhout <kleptog@svana.org> April 2006
    - * Original Licence below (public domain).
    - *
    - * This code has been copied from the Olson Timezone code, but heavily
    - * adapted to meet my needs. In particular, you can load multiple timezones
    - * and specify which timezone to convert with.
    - *************************************************************************/
    -
    -/*
    -** This file is in the public domain, so clarified as of
    -** 1996-06-05 by Arthur David Olson.
    -*/
    -
    -#define TM_GMTOFF tm_gmtoff
    -#define TM_ZONE tm_zone
    -
    -/*
    -** Leap second handling from Bradley White.
    -** POSIX-style TZ environment variable handling from Guy Harris.
    -*/
    -
    -/*LINTLIBRARY*/
    -
    -#include "private.h"
    -#include "tzfile.h"
    -#include "fcntl.h"
    -#include "float.h" /* for FLT_MAX and DBL_MAX */
    -
    -#ifndef TZ_ABBR_MAX_LEN
    -#define TZ_ABBR_MAX_LEN 16
    -#endif /* !defined TZ_ABBR_MAX_LEN */
    -
    -#ifndef TZ_ABBR_CHAR_SET
    -#define TZ_ABBR_CHAR_SET \
    - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
    -#endif /* !defined TZ_ABBR_CHAR_SET */
    -
    -#ifndef TZ_ABBR_ERR_CHAR
    -#define TZ_ABBR_ERR_CHAR '_'
    -#endif /* !defined TZ_ABBR_ERR_CHAR */
    -
    -/*
    -** SunOS 4.1.1 headers lack O_BINARY.
    -*/
    -
    -#ifdef O_BINARY
    -#define OPEN_MODE (O_RDONLY | O_BINARY)
    -#endif /* defined O_BINARY */
    -#ifndef O_BINARY
    -#define OPEN_MODE O_RDONLY
    -#endif /* !defined O_BINARY */
    -
    -#ifndef WILDABBR
    -/*
    -** Someone might make incorrect use of a time zone abbreviation:
    -** 1. They might reference tzname[0] before calling tzset (explicitly
    -** or implicitly).
    -** 2. They might reference tzname[1] before calling tzset (explicitly
    -** or implicitly).
    -** 3. They might reference tzname[1] after setting to a time zone
    -** in which Daylight Saving Time is never observed.
    -** 4. They might reference tzname[0] after setting to a time zone
    -** in which Standard Time is never observed.
    -** 5. They might reference tm.TM_ZONE after calling offtime.
    -** What's best to do in the above cases is open to debate;
    -** for now, we just set things up so that in any of the five cases
    -** WILDABBR is used. Another possibility: initialize tzname[0] to the
    -** string "tzname[0] used before set", and similarly for the other cases.
    -** And another: initialize tzname[0] to "ERA", with an explanation in the
    -** manual page of what this "time zone abbreviation" means (doing this so
    -** that tzname[0] has the "normal" length of three characters).
    -*/
    -#define WILDABBR " "
    -#endif /* !defined WILDABBR */
    -
    -static char wildabbr[] = WILDABBR;
    -
    -static const char gmt[] = "GMT";
    -
    -static char *tzdir;
    -
    -/*
    -** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
    -** We default to US rules as of 1999-08-17.
    -** POSIX 1003.1 section 8.1.1 says that the default DST rules are
    -** implementation dependent; for historical reasons, US rules are a
    -** common default.
    -*/
    -#ifndef TZDEFRULESTRING
    -#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
    -#endif /* !defined TZDEFDST */
    -
    -struct ttinfo { /* time type information */
    - long tt_gmtoff; /* UTC offset in seconds */
    - int tt_isdst; /* used to set tm_isdst */
    - int tt_abbrind; /* abbreviation list index */
    - int tt_ttisstd; /* TRUE if transition is std time */
    - int tt_ttisgmt; /* TRUE if transition is UTC */
    -};
    -
    -struct lsinfo { /* leap second information */
    - time_t ls_trans; /* transition time */
    - long ls_corr; /* correction to apply */
    -};
    -
    -#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
    -
    -#ifdef TZNAME_MAX
    -#define MY_TZNAME_MAX TZNAME_MAX
    -#endif /* defined TZNAME_MAX */
    -#ifndef TZNAME_MAX
    -#define MY_TZNAME_MAX 255
    -#endif /* !defined TZNAME_MAX */
    -
    -struct state {
    - int leapcnt;
    - int timecnt;
    - int typecnt;
    - int charcnt;
    - time_t ats[TZ_MAX_TIMES];
    - unsigned char types[TZ_MAX_TIMES];
    - struct ttinfo ttis[TZ_MAX_TYPES];
    - char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
    - (2 * (MY_TZNAME_MAX + 1)))];
    - struct lsinfo lsis[TZ_MAX_LEAPS];
    -};
    -
    -struct rule {
    - int r_type; /* type of rule--see below */
    - int r_day; /* day number of rule */
    - int r_week; /* week number of rule */
    - int r_mon; /* month number of rule */
    - long r_time; /* transition time of rule */
    -};
    -
    -#define JULIAN_DAY 0 /* Jn - Julian day */
    -#define DAY_OF_YEAR 1 /* n - day of year */
    -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
    -
    -/*
    -** Prototypes for static functions.
    -*/
    -
    -static long detzcode P((const char * codep));
    -static const char * getzname P((const char * strp));
    -static const char * getqzname P((const char * strp, const char delim));
    -static const char * getnum P((const char * strp, int * nump, int min,
    - int max));
    -static const char * getsecs P((const char * strp, long * secsp));
    -static const char * getoffset P((const char * strp, long * offsetp));
    -static const char * getrule P((const char * strp, struct rule * rulep));
    -static void gmtload P((struct state * sp));
    -struct tm * gmtsub P((const time_t * timep, long offset,
    - struct tm * tmp));
    -struct tm * localsub P((const time_t * timep, long offset,
    - struct tm * tmp, struct state *sp));
    -static int increment_overflow P((int * number, int delta));
    -static int leaps_thru_end_of P((int y));
    -static struct tm * timesub P((const time_t * timep, long offset,
    - const struct state * sp, struct tm * tmp));
    -static time_t transtime P((time_t janfirst, int year,
    - const struct rule * rulep, long offset));
    -static int tzload P((const char * name, struct state * sp));
    -static int tzparse P((const char * name, struct state * sp,
    - int lastditch));
    -
    -struct state *timezone_load P((const char * name));
    -
    -#ifdef ALL_STATE
    -static struct state * gmtptr;
    -#endif /* defined ALL_STATE */
    -
    -#ifndef ALL_STATE
    -static struct state gmtmem;
    -#define gmtptr (&gmtmem)
    -#endif /* State Farm */
    -
    -#ifndef TZ_STRLEN_MAX
    -#define TZ_STRLEN_MAX 255
    -#endif /* !defined TZ_STRLEN_MAX */
    -
    -//static char lcl_TZname[TZ_STRLEN_MAX + 1];
    -//static int lcl_is_set;
    -static int gmt_is_set;
    -
    -/*
    -** Section 4.12.3 of X3.159-1989 requires that
    -** Except for the strftime function, these functions [asctime,
    -** ctime, gmtime, localtime] return values in one of two static
    -** objects: a broken-down time structure and an array of char.
    -** Thanks to Paul Eggert for noting this.
    -*/
    -
    -//static struct tm tm;
    -
    -#ifdef USG_COMPAT
    -time_t timezone = 0;
    -int daylight = 0;
    -#endif /* defined USG_COMPAT */
    -
    -#ifdef ALTZONE
    -time_t altzone = 0;
    -#endif /* defined ALTZONE */
    -
    -static long
    -detzcode(codep)
    -const char * const codep;
    -{
    - register long result;
    - register int i;
    -
    - result = (codep[0] & 0x80) ? ~0L : 0L;
    - for (i = 0; i < 4; ++i)
    - result = (result << 8) | (codep[i] & 0xff);
    - return result;
    -}
    -
    -static int
    -tzload(name, sp)
    -register const char * name;
    -register struct state * const sp;
    -{
    - register const char * p;
    - register int i;
    - register int fid;
    -
    - if (name == NULL && (name = TZDEFAULT) == NULL)
    - return -1;
    - {
    - register int doaccess;
    - /*
    - ** Section 4.9.1 of the C standard says that
    - ** "FILENAME_MAX expands to an integral constant expression
    - ** that is the size needed for an array of char large enough
    - ** to hold the longest file name string that the implementation
    - ** guarantees can be opened."
    - */
    - char fullname[FILENAME_MAX + 1];
    -
    - if (name[0] == ':')
    - ++name;
    - doaccess = name[0] == '/';
    - if (!doaccess) {
    - if ((p = tzdir) == NULL)
    - return -1;
    - if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
    - return -1;
    - (void) strcpy(fullname, p);
    - (void) strcat(fullname, "/");
    - (void) strcat(fullname, name);
    - /*
    - ** Set doaccess if '.' (as in "../") shows up in name.
    - */
    - if (strchr(name, '.') != NULL)
    - doaccess = TRUE;
    - name = fullname;
    - }
    - if (doaccess && access(name, R_OK) != 0)
    - return -1;
    - if ((fid = open(name, OPEN_MODE)) == -1)
    - return -1;
    - }
    - {
    - struct tzhead * tzhp;
    - union {
    - struct tzhead tzhead;
    - char buf[sizeof *sp + sizeof *tzhp];
    - } u;
    - int ttisstdcnt;
    - int ttisgmtcnt;
    -
    - i = read(fid, u.buf, sizeof u.buf);
    - if (close(fid) != 0)
    - return -1;
    - ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
    - ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
    - sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
    - sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
    - sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
    - sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
    - p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
    - if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
    - sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
    - sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
    - sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
    - (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
    - (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
    - return -1;
    - if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
    - sp->timecnt + /* types */
    - sp->typecnt * (4 + 2) + /* ttinfos */
    - sp->charcnt + /* chars */
    - sp->leapcnt * (4 + 4) + /* lsinfos */
    - ttisstdcnt + /* ttisstds */
    - ttisgmtcnt) /* ttisgmts */
    - return -1;
    - for (i = 0; i < sp->timecnt; ++i) {
    - sp->ats[i] = detzcode(p);
    - p += 4;
    - }
    - for (i = 0; i < sp->timecnt; ++i) {
    - sp->types[i] = (unsigned char) *p++;
    - if (sp->types[i] >= sp->typecnt)
    - return -1;
    - }
    - for (i = 0; i < sp->typecnt; ++i) {
    - register struct ttinfo * ttisp;
    -
    - ttisp = &sp->ttis[i];
    - ttisp->tt_gmtoff = detzcode(p);
    - p += 4;
    - ttisp->tt_isdst = (unsigned char) *p++;
    - if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
    - return -1;
    - ttisp->tt_abbrind = (unsigned char) *p++;
    - if (ttisp->tt_abbrind < 0 ||
    - ttisp->tt_abbrind > sp->charcnt)
    - return -1;
    - }
    - for (i = 0; i < sp->charcnt; ++i)
    - sp->chars[i] = *p++;
    - sp->chars[i] = '\0'; /* ensure '\0' at end */
    - for (i = 0; i < sp->leapcnt; ++i) {
    - register struct lsinfo * lsisp;
    -
    - lsisp = &sp->lsis[i];
    - lsisp->ls_trans = detzcode(p);
    - p += 4;
    - lsisp->ls_corr = detzcode(p);
    - p += 4;
    - }
    - for (i = 0; i < sp->typecnt; ++i) {
    - register struct ttinfo * ttisp;
    -
    - ttisp = &sp->ttis[i];
    - if (ttisstdcnt == 0)
    - ttisp->tt_ttisstd = FALSE;
    - else {
    - ttisp->tt_ttisstd = *p++;
    - if (ttisp->tt_ttisstd != TRUE &&
    - ttisp->tt_ttisstd != FALSE)
    - return -1;
    - }
    - }
    - for (i = 0; i < sp->typecnt; ++i) {
    - register struct ttinfo * ttisp;
    -
    - ttisp = &sp->ttis[i];
    - if (ttisgmtcnt == 0)
    - ttisp->tt_ttisgmt = FALSE;
    - else {
    - ttisp->tt_ttisgmt = *p++;
    - if (ttisp->tt_ttisgmt != TRUE &&
    - ttisp->tt_ttisgmt != FALSE)
    - return -1;
    - }
    - }
    - /*
    - ** Out-of-sort ats should mean we're running on a
    - ** signed time_t system but using a data file with
    - ** unsigned values (or vice versa).
    - */
    - for (i = 0; i < sp->timecnt - 2; ++i)
    - if (sp->ats[i] > sp->ats[i + 1]) {
    - ++i;
    - if (TYPE_SIGNED(time_t)) {
    - /*
    - ** Ignore the end (easy).
    - */
    - sp->timecnt = i;
    - } else {
    - /*
    - ** Ignore the beginning (harder).
    - */
    - register int j;
    -
    - for (j = 0; j + i < sp->timecnt; ++j) {
    - sp->ats[j] = sp->ats[j + i];
    - sp->types[j] = sp->types[j + i];
    - }
    - sp->timecnt = j;
    - }
    - break;
    - }
    - }
    - return 0;
    -}
    -
    -struct state *timezone_load(name)
    -const char * name;
    -{
    - struct state *sp = malloc( sizeof(struct state) );
    - int res;
    -
    - if( !sp )
    - return NULL;
    - res = tzload( name, sp );
    - if( res < 0 )
    - {
    - free(sp);
    - return NULL;
    - }
    - return sp;
    -}
    -
    -
    -static const int mon_lengths[2][MONSPERYEAR] = {
    - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
    - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    -};
    -
    -static const int year_lengths[2] = {
    - DAYSPERNYEAR, DAYSPERLYEAR
    -};
    -
    -/*
    -** Given a pointer into a time zone string, scan until a character that is not
    -** a valid character in a zone name is found. Return a pointer to that
    -** character.
    -*/
    -
    -static const char *
    -getzname(strp)
    -register const char * strp;
    -{
    - register char c;
    -
    - while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
    - c != '+')
    - ++strp;
    - return strp;
    -}
    -
    -/*
    -** Given a pointer into an extended time zone string, scan until the ending
    -** delimiter of the zone name is located. Return a pointer to the delimiter.
    -**
    -** As with getzname above, the legal character set is actually quite
    -** restricted, with other characters producing undefined results.
    -** We choose not to care - allowing almost anything to be in the zone abbrev.
    -*/
    -
    -static const char *
    -#if __STDC__
    -getqzname(register const char *strp, const char delim)
    -#else /* !__STDC__ */
    -getqzname(strp, delim)
    -register const char * strp;
    -const char delim;
    -#endif /* !__STDC__ */
    -{
    - register char c;
    -
    - while ((c = *strp) != '\0' && c != delim)
    - ++strp;
    - return strp;
    -}
    -
    -/*
    -** Given a pointer into a time zone string, extract a number from that string.
    -** Check that the number is within a specified range; if it is not, return
    -** NULL.
    -** Otherwise, return a pointer to the first character not part of the number.
    -*/
    -
    -static const char *
    -getnum(strp, nump, min, max)
    -register const char * strp;
    -int * const nump;
    -const int min;
    -const int max;
    -{
    - register char c;
    - register int num;
    -
    - if (strp == NULL || !is_digit(c = *strp))
    - return NULL;
    - num = 0;
    - do {
    - num = num * 10 + (c - '0');
    - if (num > max)
    - return NULL; /* illegal value */
    - c = *++strp;
    - } while (is_digit(c));
    - if (num < min)
    - return NULL; /* illegal value */
    - *nump = num;
    - return strp;
    -}
    -
    -/*
    -** Given a pointer into a time zone string, extract a number of seconds,
    -** in hh[:mm[:ss]] form, from the string.
    -** If any error occurs, return NULL.
    -** Otherwise, return a pointer to the first character not part of the number
    -** of seconds.
    -*/
    -
    -static const char *
    -getsecs(strp, secsp)
    -register const char * strp;
    -long * const secsp;
    -{
    - int num;
    -
    - /*
    - ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
    - ** "M10.4.6/26", which does not conform to Posix,
    - ** but which specifies the equivalent of
    - ** ``02:00 on the first Sunday on or after 23 Oct''.
    - */
    - strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
    - if (strp == NULL)
    - return NULL;
    - *secsp = num * (long) SECSPERHOUR;
    - if (*strp == ':') {
    - ++strp;
    - strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
    - if (strp == NULL)
    - return NULL;
    - *secsp += num * SECSPERMIN;
    - if (*strp == ':') {
    - ++strp;
    - /* `SECSPERMIN' allows for leap seconds. */
    - strp = getnum(strp, &num, 0, SECSPERMIN);
    - if (strp == NULL)
    - return NULL;
    - *secsp += num;
    - }
    - }
    - return strp;
    -}
    -
    -/*
    -** Given a pointer into a time zone string, extract an offset, in
    -** [+-]hh[:mm[:ss]] form, from the string.
    -** If any error occurs, return NULL.
    -** Otherwise, return a pointer to the first character not part of the time.
    -*/
    -
    -static const char *
    -getoffset(strp, offsetp)
    -register const char * strp;
    -long * const offsetp;
    -{
    - register int neg = 0;
    -
    - if (*strp == '-') {
    - neg = 1;
    - ++strp;
    - } else if (*strp == '+')
    - ++strp;
    - strp = getsecs(strp, offsetp);
    - if (strp == NULL)
    - return NULL; /* illegal time */
    - if (neg)
    - *offsetp = -*offsetp;
    - return strp;
    -}
    -
    -/*
    -** Given a pointer into a time zone string, extract a rule in the form
    -** date[/time]. See POSIX section 8 for the format of "date" and "time".
    -** If a valid rule is not found, return NULL.
    -** Otherwise, return a pointer to the first character not part of the rule.
    -*/
    -
    -static const char *
    -getrule(strp, rulep)
    -const char * strp;
    -register struct rule * const rulep;
    -{
    - if (*strp == 'J') {
    - /*
    - ** Julian day.
    - */
    - rulep->r_type = JULIAN_DAY;
    - ++strp;
    - strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
    - } else if (*strp == 'M') {
    - /*
    - ** Month, week, day.
    - */
    - rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
    - ++strp;
    - strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
    - if (strp == NULL)
    - return NULL;
    - if (*strp++ != '.')
    - return NULL;
    - strp = getnum(strp, &rulep->r_week, 1, 5);
    - if (strp == NULL)
    - return NULL;
    - if (*strp++ != '.')
    - return NULL;
    - strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
    - } else if (is_digit(*strp)) {
    - /*
    - ** Day of year.
    - */
    - rulep->r_type = DAY_OF_YEAR;
    - strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
    - } else return NULL; /* invalid format */
    - if (strp == NULL)
    - return NULL;
    - if (*strp == '/') {
    - /*
    - ** Time specified.
    - */
    - ++strp;
    - strp = getsecs(strp, &rulep->r_time);
    - } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
    - return strp;
    -}
    -
    -/*
    -** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
    -** year, a rule, and the offset from UTC at the time that rule takes effect,
    -** calculate the Epoch-relative time that rule takes effect.
    -*/
    -
    -static time_t
    -transtime(janfirst, year, rulep, offset)
    -const time_t janfirst;
    -const int year;
    -register const struct rule * const rulep;
    -const long offset;
    -{
    - register int leapyear;
    - register time_t value;
    - register int i;
    - int d, m1, yy0, yy1, yy2, dow;
    -
    - INITIALIZE(value);
    - leapyear = isleap(year);
    - switch (rulep->r_type) {
    -
    - case JULIAN_DAY:
    - /*
    - ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
    - ** years.
    - ** In non-leap years, or if the day number is 59 or less, just
    - ** add SECSPERDAY times the day number-1 to the time of
    - ** January 1, midnight, to get the day.
    - */
    - value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
    - if (leapyear && rulep->r_day >= 60)
    - value += SECSPERDAY;
    - break;
    -
    - case DAY_OF_YEAR:
    - /*
    - ** n - day of year.
    - ** Just add SECSPERDAY times the day number to the time of
    - ** January 1, midnight, to get the day.
    - */
    - value = janfirst + rulep->r_day * SECSPERDAY;
    - break;
    -
    - case MONTH_NTH_DAY_OF_WEEK:
    - /*
    - ** Mm.n.d - nth "dth day" of month m.
    - */
    - value = janfirst;
    - for (i = 0; i < rulep->r_mon - 1; ++i)
    - value += mon_lengths[leapyear][i] * SECSPERDAY;
    -
    - /*
    - ** Use Zeller's Congruence to get day-of-week of first day of
    - ** month.
    - */
    - m1 = (rulep->r_mon + 9) % 12 + 1;
    - yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
    - yy1 = yy0 / 100;
    - yy2 = yy0 % 100;
    - dow = ((26 * m1 - 2) / 10 +
    - 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
    - if (dow < 0)
    - dow += DAYSPERWEEK;
    -
    - /*
    - ** "dow" is the day-of-week of the first day of the month. Get
    - ** the day-of-month (zero-origin) of the first "dow" day of the
    - ** month.
    - */
    - d = rulep->r_day - dow;
    - if (d < 0)
    - d += DAYSPERWEEK;
    - for (i = 1; i < rulep->r_week; ++i) {
    - if (d + DAYSPERWEEK >=
    - mon_lengths[leapyear][rulep->r_mon - 1])
    - break;
    - d += DAYSPERWEEK;
    - }
    -
    - /*
    - ** "d" is the day-of-month (zero-origin) of the day we want.
    - */
    - value += d * SECSPERDAY;
    - break;
    - }
    -
    - /*
    - ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
    - ** question. To get the Epoch-relative time of the specified local
    - ** time on that day, add the transition time and the current offset
    - ** from UTC.
    - */
    - return value + rulep->r_time + offset;
    -}
    -
    -/*
    -** Given a POSIX section 8-style TZ string, fill in the rule tables as
    -** appropriate.
    -*/
    -
    -static int
    -tzparse(name, sp, lastditch)
    -const char * name;
    -register struct state * const sp;
    -const int lastditch;
    -{
    - const char * stdname;
    - const char * dstname;
    - size_t stdlen;
    - size_t dstlen;
    - long stdoffset;
    - long dstoffset;
    - register time_t * atp;
    - register unsigned char * typep;
    - register char * cp;
    - register int load_result;
    -
    - INITIALIZE(dstname);
    - stdname = name;
    - if (lastditch) {
    - stdlen = strlen(name); /* length of standard zone name */
    - name += stdlen;
    - if (stdlen >= sizeof sp->chars)
    - stdlen = (sizeof sp->chars) - 1;
    - stdoffset = 0;
    - } else {
    - if (*name == '<') {
    - name++;
    - stdname = name;
    - name = getqzname(name, '>');
    - if (*name != '>')
    - return (-1);
    - stdlen = name - stdname;
    - name++;
    - } else {
    - name = getzname(name);
    - stdlen = name - stdname;
    - }
    - if (*name == '\0')
    - return -1;
    - name = getoffset(name, &stdoffset);
    - if (name == NULL)
    - return -1;
    - }
    - load_result = tzload(TZDEFRULES, sp);
    - if (load_result != 0)
    - sp->leapcnt = 0; /* so, we're off a little */
    - if (*name != '\0') {
    - if (*name == '<') {
    - dstname = ++name;
    - name = getqzname(name, '>');
    - if (*name != '>')
    - return -1;
    - dstlen = name - dstname;
    - name++;
    - } else {
    - dstname = name;
    - name = getzname(name);
    - dstlen = name - dstname; /* length of DST zone name */
    - }
    - if (*name != '\0' && *name != ',' && *name != ';') {
    - name = getoffset(name, &dstoffset);
    - if (name == NULL)
    - return -1;
    - } else dstoffset = stdoffset - SECSPERHOUR;
    - if (*name == '\0' && load_result != 0)
    - name = TZDEFRULESTRING;
    - if (*name == ',' || *name == ';') {
    - struct rule start;
    - struct rule end;
    - register int year;
    - register time_t janfirst;
    - time_t starttime;
    - time_t endtime;
    -
    - ++name;
    - if ((name = getrule(name, &start)) == NULL)
    - return -1;
    - if (*name++ != ',')
    - return -1;
    - if ((name = getrule(name, &end)) == NULL)
    - return -1;
    - if (*name != '\0')
    - return -1;
    - sp->typecnt = 2; /* standard time and DST */
    - /*
    - ** Two transitions per year, from EPOCH_YEAR to 2037.
    - */
    - sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
    - if (sp->timecnt > TZ_MAX_TIMES)
    - return -1;
    - sp->ttis[0].tt_gmtoff = -dstoffset;
    - sp->ttis[0].tt_isdst = 1;
    - sp->ttis[0].tt_abbrind = stdlen + 1;
    - sp->ttis[1].tt_gmtoff = -stdoffset;
    - sp->ttis[1].tt_isdst = 0;
    - sp->ttis[1].tt_abbrind = 0;
    - atp = sp->ats;
    - typep = sp->types;
    - janfirst = 0;
    - for (year = EPOCH_YEAR; year <= 2037; ++year) {
    - starttime = transtime(janfirst, year, &start,
    - stdoffset);
    - endtime = transtime(janfirst, year, &end,
    - dstoffset);
    - if (starttime > endtime) {
    - *atp++ = endtime;
    - *typep++ = 1; /* DST ends */
    - *atp++ = starttime;
    - *typep++ = 0; /* DST begins */
    - } else {
    - *atp++ = starttime;
    - *typep++ = 0; /* DST begins */
    - *atp++ = endtime;
    - *typep++ = 1; /* DST ends */
    - }
    - janfirst += year_lengths[isleap(year)] *
    - SECSPERDAY;
    - }
    - } else {
    - register long theirstdoffset;
    - register long theirdstoffset;
    - register long theiroffset;
    - register int isdst;
    - register int i;
    - register int j;
    -
    - if (*name != '\0')
    - return -1;
    - /*
    - ** Initial values of theirstdoffset and theirdstoffset.
    - */
    - theirstdoffset = 0;
    - for (i = 0; i < sp->timecnt; ++i) {
    - j = sp->types[i];
    - if (!sp->ttis[j].tt_isdst) {
    - theirstdoffset =
    - -sp->ttis[j].tt_gmtoff;
    - break;
    - }
    - }
    - theirdstoffset = 0;
    - for (i = 0; i < sp->timecnt; ++i) {
    - j = sp->types[i];
    - if (sp->ttis[j].tt_isdst) {
    - theirdstoffset =
    - -sp->ttis[j].tt_gmtoff;
    - break;
    - }
    - }
    - /*
    - ** Initially we're assumed to be in standard time.
    - */
    - isdst = FALSE;
    - theiroffset = theirstdoffset;
    - /*
    - ** Now juggle transition times and types
    - ** tracking offsets as you do.
    - */
    - for (i = 0; i < sp->timecnt; ++i) {
    - j = sp->types[i];
    - sp->types[i] = sp->ttis[j].tt_isdst;
    - if (sp->ttis[j].tt_ttisgmt) {
    - /* No adjustment to transition time */
    - } else {
    - /*
    - ** If summer time is in effect, and the
    - ** transition time was not specified as
    - ** standard time, add the summer time
    - ** offset to the transition time;
    - ** otherwise, add the standard time
    - ** offset to the transition time.
    - */
    - /*
    - ** Transitions from DST to DDST
    - ** will effectively disappear since
    - ** POSIX provides for only one DST
    - ** offset.
    - */
    - if (isdst && !sp->ttis[j].tt_ttisstd) {
    - sp->ats[i] += dstoffset -
    - theirdstoffset;
    - } else {
    - sp->ats[i] += stdoffset -
    - theirstdoffset;
    - }
    - }
    - theiroffset = -sp->ttis[j].tt_gmtoff;
    - if (sp->ttis[j].tt_isdst)
    - theirdstoffset = theiroffset;
    - else theirstdoffset = theiroffset;
    - }
    - /*
    - ** Finally, fill in ttis.
    - ** ttisstd and ttisgmt need not be handled.
    - */
    - sp->ttis[0].tt_gmtoff = -stdoffset;
    - sp->ttis[0].tt_isdst = FALSE;
    - sp->ttis[0].tt_abbrind = 0;
    - sp->ttis[1].tt_gmtoff = -dstoffset;
    - sp->ttis[1].tt_isdst = TRUE;
    - sp->ttis[1].tt_abbrind = stdlen + 1;
    - sp->typecnt = 2;
    - }
    - } else {
    - dstlen = 0;
    - sp->typecnt = 1; /* only standard time */
    - sp->timecnt = 0;
    - sp->ttis[0].tt_gmtoff = -stdoffset;
    - sp->ttis[0].tt_isdst = 0;
    - sp->ttis[0].tt_abbrind = 0;
    - }
    - sp->charcnt = stdlen + 1;
    - if (dstlen != 0)
    - sp->charcnt += dstlen + 1;
    - if ((size_t) sp->charcnt > sizeof sp->chars)
    - return -1;
    - cp = sp->chars;
    - (void) strncpy(cp, stdname, stdlen);
    - cp += stdlen;
    - *cp++ = '\0';
    - if (dstlen != 0) {
    - (void) strncpy(cp, dstname, dstlen);
    - *(cp + dstlen) = '\0';
    - }
    - return 0;
    -}
    -
    -static void
    -gmtload(sp)
    -struct state * const sp;
    -{
    - if (tzload(gmt, sp) != 0)
    - (void) tzparse(gmt, sp, TRUE);
    -}
    -
    -/*
    -** The easy way to behave "as if no library function calls" localtime
    -** is to not call it--so we drop its guts into "localsub", which can be
    -** freely called. (And no, the PANS doesn't require the above behavior--
    -** but it *is* desirable.)
    -**
    -** The unused offset argument is for the benefit of mktime variants.
    -*/
    -
    -struct tm *
    -localsub(timep, offset, tmp, sp)
    -const time_t * const timep;
    -const long offset;
    -struct tm * const tmp;
    -struct state * sp;
    -{
    - register const struct ttinfo * ttisp;
    - register int i;
    - register struct tm * result;
    - const time_t t = *timep;
    -
    -#ifdef ALL_STATE
    - if (sp == NULL)
    - return gmtsub(timep, offset, tmp);
    -#endif /* defined ALL_STATE */
    - if (sp->timecnt == 0 || t < sp->ats[0]) {
    - i = 0;
    - while (sp->ttis[i].tt_isdst)
    - if (++i >= sp->typecnt) {
    - i = 0;
    - break;
    - }
    - } else {
    - for (i = 1; i < sp->timecnt; ++i)
    - if (t < sp->ats[i])
    - break;
    - i = (int) sp->types[i - 1];
    - }
    - ttisp = &sp->ttis[i];
    - /*
    - ** To get (wrong) behavior that's compatible with System V Release 2.0
    - ** you'd replace the statement below with
    - ** t += ttisp->tt_gmtoff;
    - ** timesub(&t, 0L, sp, tmp);
    - */
    - result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
    - tmp->tm_isdst = ttisp->tt_isdst;
    - tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
    -#ifdef TM_ZONE
    - tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
    -#endif /* defined TM_ZONE */
    - return result;
    -}
    -
    -/*
    -** gmtsub is to gmtime as localsub is to localtime.
    -*/
    -
    -struct tm *
    -gmtsub(timep, offset, tmp)
    -const time_t * const timep;
    -const long offset;
    -struct tm * const tmp;
    -{
    - register struct tm * result;
    -
    - if (!gmt_is_set) {
    - gmt_is_set = TRUE;
    -#ifdef ALL_STATE
    - gmtptr = (struct state *) malloc(sizeof *gmtptr);
    - if (gmtptr != NULL)
    -#endif /* defined ALL_STATE */
    - gmtload(gmtptr);
    - }
    - result = timesub(timep, offset, gmtptr, tmp);
    -#ifdef TM_ZONE
    - /*
    - ** Could get fancy here and deliver something such as
    - ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
    - ** but this is no time for a treasure hunt.
    - */
    - if (offset != 0)
    - tmp->TM_ZONE = wildabbr;
    - else {
    -#ifdef ALL_STATE
    - if (gmtptr == NULL)
    - tmp->TM_ZONE = gmt;
    - else tmp->TM_ZONE = gmtptr->chars;
    -#endif /* defined ALL_STATE */
    -#ifndef ALL_STATE
    - tmp->TM_ZONE = gmtptr->chars;
    -#endif /* State Farm */
    - }
    -#endif /* defined TM_ZONE */
    - return result;
    -}
    -
    -/*
    -** Return the number of leap years through the end of the given year
    -** where, to make the math easy, the answer for year zero is defined as zero.
    -*/
    -
    -static int
    -leaps_thru_end_of(y)
    -register const int y;
    -{
    - return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
    - -(leaps_thru_end_of(-(y + 1)) + 1);
    -}
    -
    -static struct tm *
    -timesub(timep, offset, sp, tmp)
    -const time_t * const timep;
    -const long offset;
    -register const struct state * const sp;
    -register struct tm * const tmp;
    -{
    - register const struct lsinfo * lp;
    - register time_t tdays;
    - register int idays; /* unsigned would be so 2003 */
    - register long rem;
    - int y;
    - register const int * ip;
    - register long corr;
    - register int hit;
    - register int i;
    -
    - corr = 0;
    - hit = 0;
    -#ifdef ALL_STATE
    - i = (sp == NULL) ? 0 : sp->leapcnt;
    -#endif /* defined ALL_STATE */
    -#ifndef ALL_STATE
    - i = sp->leapcnt;
    -#endif /* State Farm */
    - while (--i >= 0) {
    - lp = &sp->lsis[i];
    - if (*timep >= lp->ls_trans) {
    - if (*timep == lp->ls_trans) {
    - hit = ((i == 0 && lp->ls_corr > 0) ||
    - lp->ls_corr > sp->lsis[i - 1].ls_corr);
    - if (hit)
    - while (i > 0 &&
    - sp->lsis[i].ls_trans ==
    - sp->lsis[i - 1].ls_trans + 1 &&
    - sp->lsis[i].ls_corr ==
    - sp->lsis[i - 1].ls_corr + 1) {
    - ++hit;
    - --i;
    - }
    - }
    - corr = lp->ls_corr;
    - break;
    - }
    - }
    - y = EPOCH_YEAR;
    - tdays = *timep / SECSPERDAY;
    - rem = *timep - tdays * SECSPERDAY;
    - while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
    - int newy;
    - register time_t tdelta;
    - register int idelta;
    - register int leapdays;
    -
    - tdelta = tdays / DAYSPERLYEAR;
    - idelta = tdelta;
    - if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
    - return NULL;
    - if (idelta == 0)
    - idelta = (tdays < 0) ? -1 : 1;
    - newy = y;
    - if (increment_overflow(&newy, idelta))
    - return NULL;
    - leapdays = leaps_thru_end_of(newy - 1) -
    - leaps_thru_end_of(y - 1);
    - tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
    - tdays -= leapdays;
    - y = newy;
    - }
    - {
    - register long seconds;
    -
    - seconds = tdays * SECSPERDAY + 0.5;
    - tdays = seconds / SECSPERDAY;
    - rem += seconds - tdays * SECSPERDAY;
    - }
    - /*
    - ** Given the range, we can now fearlessly cast...
    - */
    - idays = tdays;
    - rem += offset - corr;
    - while (rem < 0) {
    - rem += SECSPERDAY;
    - --idays;
    - }
    - while (rem >= SECSPERDAY) {
    - rem -= SECSPERDAY;
    - ++idays;
    - }
    - while (idays < 0) {
    - if (increment_overflow(&y, -1))
    - return NULL;
    - idays += year_lengths[isleap(y)];
    - }
    - while (idays >= year_lengths[isleap(y)]) {
    - idays -= year_lengths[isleap(y)];
    - if (increment_overflow(&y, 1))
    - return NULL;
    - }
    - tmp->tm_year = y;
    - if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
    - return NULL;
    - tmp->tm_yday = idays;
    - /*
    - ** The "extra" mods below avoid overflow problems.
    - */
    - tmp->tm_wday = EPOCH_WDAY +
    - ((y - EPOCH_YEAR) % DAYSPERWEEK) *
    - (DAYSPERNYEAR % DAYSPERWEEK) +
    - leaps_thru_end_of(y - 1) -
    - leaps_thru_end_of(EPOCH_YEAR - 1) +
    - idays;
    - tmp->tm_wday %= DAYSPERWEEK;
    - if (tmp->tm_wday < 0)
    - tmp->tm_wday += DAYSPERWEEK;
    - tmp->tm_hour = (int) (rem / SECSPERHOUR);
    - rem %= SECSPERHOUR;
    - tmp->tm_min = (int) (rem / SECSPERMIN);
    - /*
    - ** A positive leap second requires a special
    - ** representation. This uses "... ??:59:60" et seq.
    - */
    - tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
    - ip = mon_lengths[isleap(y)];
    - for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
    - idays -= ip[tmp->tm_mon];
    - tmp->tm_mday = (int) (idays + 1);
    - tmp->tm_isdst = 0;
    -#ifdef TM_GMTOFF
    - tmp->TM_GMTOFF = offset;
    -#endif /* defined TM_GMTOFF */
    - return tmp;
    -}
    -
    -
    -/*
    -** Adapted from code provided by Robert Elz, who writes:
    -** The "best" way to do mktime I think is based on an idea of Bob
    -** Kridle's (so its said...) from a long time ago.
    -** It does a binary search of the time_t space. Since time_t's are
    -** just 32 bits, its a max of 32 iterations (even at 64 bits it
    -** would still be very reasonable).
    -*/
    -
    -#ifndef WRONG
    -#define WRONG (-1)
    -#endif /* !defined WRONG */
    -
    -/*
    -** Simplified normalize logic courtesy Paul Eggert.
    -*/
    -
    -static int
    -increment_overflow(number, delta)
    -int * number;
    -int delta;
    -{
    - int number0;
    -
    - number0 = *number;
    - *number += delta;
    - return (*number < number0) != (delta < 0);
    -}
    -
    -int tz_init( const char *zoneinfo_dir )
    -{
    - char *ptr;
    - int fd;
    -
    - if( zoneinfo_dir == NULL )
    - zoneinfo_dir = TZDIR;
    -
    - ptr = malloc( strlen(zoneinfo_dir) + 10 );
    - sprintf( ptr, "%s/zone.tab", zoneinfo_dir );
    - fd = open( ptr, O_RDONLY );
    - free(ptr);
    -
    - if( fd < 0 )
    - return -1;
    - close(fd);
    - if( tzdir )
    - free(tzdir);
    - tzdir = strdup(zoneinfo_dir);
    - return 0;
    -}
    --- a/buddytime/localtime.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,16 +0,0 @@
    -/*************************************************************************
    - * Header file for timezone module
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Licenced under the GNU General Public Licence version 2.
    - *************************************************************************/
    -
    -#include <time.h>
    -
    -struct state;
    -
    -struct state *timezone_load (const char * name);
    -struct tm * gmtsub (const time_t * timep, long offset,
    - struct tm * tmp);
    -struct tm * localsub (const time_t * timep, long offset,
    - struct tm * tmp, struct state *sp);
    -int tz_init(const char *zoneinfo_dir);
    --- a/buddytime/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    -if TYPES.contains('incomplete') and PURPLE.found()
    - executable('recursetest',
    - 'recurse.c',
    - 'recursetest.c')
    -
    - buddytime = shared_library('buddytime',
    - 'buddytime.c',
    - # FIXME: I don't think this file should use Pidgin.
    - dependencies : [PIDGIN, PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'buddytime'
    -
    - if PIDGIN.found()
    - executable('gtktimezonetest',
    - 'gtktimezone.c',
    - 'gtktimezonetest.c',
    - 'recurse.c',
    - dependencies : GTK)
    -
    - gtkbuddytime = shared_module('gtkbuddytime',
    - 'gtkbuddytime.c',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'gtkbuddytime'
    - endif
    -endif
    --- a/buddytime/private.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,316 +0,0 @@
    -/*************************************************************************
    - * Private Header file for timezone module
    - * copied from Olson timezone code, licence unchanged
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - *************************************************************************/
    -
    -#ifndef PRIVATE_H
    -
    -#define PRIVATE_H
    -
    -/*
    -** This file is in the public domain, so clarified as of
    -** 1996-06-05 by Arthur David Olson.
    -*/
    -
    -/*
    -** This header is for use ONLY with the time conversion code.
    -** There is no guarantee that it will remain unchanged,
    -** or that it will remain at all.
    -** Do NOT copy it to any system include directory.
    -** Thank you!
    -*/
    -
    -/*
    -** ID
    -*/
    -
    -#define GRANDPARENTED "Local time zone must be set--see zic manual page"
    -
    -/*
    -** Defaults for preprocessor symbols.
    -** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
    -*/
    -
    -#ifndef HAVE_ADJTIME
    -#define HAVE_ADJTIME 1
    -#endif /* !defined HAVE_ADJTIME */
    -
    -#ifndef HAVE_GETTEXT
    -#define HAVE_GETTEXT 0
    -#endif /* !defined HAVE_GETTEXT */
    -
    -#ifndef HAVE_INCOMPATIBLE_CTIME_R
    -#define HAVE_INCOMPATIBLE_CTIME_R 0
    -#endif /* !defined INCOMPATIBLE_CTIME_R */
    -
    -#ifndef HAVE_SETTIMEOFDAY
    -#define HAVE_SETTIMEOFDAY 3
    -#endif /* !defined HAVE_SETTIMEOFDAY */
    -
    -#ifndef HAVE_STRERROR
    -#define HAVE_STRERROR 1
    -#endif /* !defined HAVE_STRERROR */
    -
    -#ifndef HAVE_SYMLINK
    -#define HAVE_SYMLINK 1
    -#endif /* !defined HAVE_SYMLINK */
    -
    -#ifndef HAVE_SYS_STAT_H
    -#define HAVE_SYS_STAT_H 1
    -#endif /* !defined HAVE_SYS_STAT_H */
    -
    -#ifndef HAVE_SYS_WAIT_H
    -#define HAVE_SYS_WAIT_H 1
    -#endif /* !defined HAVE_SYS_WAIT_H */
    -
    -#ifndef HAVE_UNISTD_H
    -#define HAVE_UNISTD_H 1
    -#endif /* !defined HAVE_UNISTD_H */
    -
    -#ifndef HAVE_UTMPX_H
    -#define HAVE_UTMPX_H 0
    -#endif /* !defined HAVE_UTMPX_H */
    -
    -#ifndef LOCALE_HOME
    -#define LOCALE_HOME "/usr/lib/locale"
    -#endif /* !defined LOCALE_HOME */
    -
    -#if HAVE_INCOMPATIBLE_CTIME_R
    -#define asctime_r _incompatible_asctime_r
    -#define ctime_r _incompatible_ctime_r
    -#endif /* HAVE_INCOMPATIBLE_CTIME_R */
    -
    -/*
    -** Nested includes
    -*/
    -
    -#include "sys/types.h" /* for time_t */
    -#include "stdio.h"
    -#include "errno.h"
    -#include "string.h"
    -#include "limits.h" /* for CHAR_BIT */
    -#include "time.h"
    -#include "stdlib.h"
    -
    -#if HAVE_GETTEXT
    -#include "libintl.h"
    -#endif /* HAVE_GETTEXT */
    -
    -#if HAVE_SYS_WAIT_H
    -#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
    -#endif /* HAVE_SYS_WAIT_H */
    -
    -#ifndef WIFEXITED
    -#define WIFEXITED(status) (((status) & 0xff) == 0)
    -#endif /* !defined WIFEXITED */
    -#ifndef WEXITSTATUS
    -#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
    -#endif /* !defined WEXITSTATUS */
    -
    -#if HAVE_UNISTD_H
    -#include "unistd.h" /* for F_OK and R_OK */
    -#endif /* HAVE_UNISTD_H */
    -
    -#if !HAVE_UNISTD_H
    -#ifndef F_OK
    -#define F_OK 0
    -#endif /* !defined F_OK */
    -#ifndef R_OK
    -#define R_OK 4
    -#endif /* !defined R_OK */
    -#endif /* !HAVE_UNISTD_H */
    -
    -/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
    -#define is_digit(c) ((unsigned)(c) - '0' <= 9)
    -
    -/*
    -** Workarounds for compilers/systems.
    -*/
    -
    -/*
    -** SunOS 4.1.1 cc lacks prototypes.
    -*/
    -
    -#ifndef P
    -#if __STDC__
    -#define P(x) x
    -#else /* !__STDC__ */
    -#define P(x) ()
    -#endif /* !__STDC__ */
    -#endif /* !defined P */
    -
    -/*
    -** SunOS 4.1.1 headers lack EXIT_SUCCESS.
    -*/
    -
    -#ifndef EXIT_SUCCESS
    -#define EXIT_SUCCESS 0
    -#endif /* !defined EXIT_SUCCESS */
    -
    -/*
    -** SunOS 4.1.1 headers lack EXIT_FAILURE.
    -*/
    -
    -#ifndef EXIT_FAILURE
    -#define EXIT_FAILURE 1
    -#endif /* !defined EXIT_FAILURE */
    -
    -/*
    -** SunOS 4.1.1 headers lack FILENAME_MAX.
    -*/
    -
    -#ifndef FILENAME_MAX
    -
    -#ifndef MAXPATHLEN
    -#ifdef unix
    -#include "sys/param.h"
    -#endif /* defined unix */
    -#endif /* !defined MAXPATHLEN */
    -
    -#ifdef MAXPATHLEN
    -#define FILENAME_MAX MAXPATHLEN
    -#endif /* defined MAXPATHLEN */
    -#ifndef MAXPATHLEN
    -#define FILENAME_MAX 1024 /* Pure guesswork */
    -#endif /* !defined MAXPATHLEN */
    -
    -#endif /* !defined FILENAME_MAX */
    -
    -/*
    -** SunOS 4.1.1 libraries lack remove.
    -*/
    -
    -#ifndef remove
    -extern int unlink P((const char * filename));
    -#define remove unlink
    -#endif /* !defined remove */
    -
    -/*
    -** Some ancient errno.h implementations don't declare errno.
    -** But some newer errno.h implementations define it as a macro.
    -** Fix the former without affecting the latter.
    -*/
    -
    -#ifndef errno
    -extern int errno;
    -#endif /* !defined errno */
    -
    -/*
    -** Some time.h implementations don't declare asctime_r.
    -** Others might define it as a macro.
    -** Fix the former without affecting the latter.
    -*/
    -
    -#ifndef asctime_r
    -extern char * asctime_r();
    -#endif
    -
    -/*
    -** Private function declarations.
    -*/
    -
    -char * icalloc P((int nelem, int elsize));
    -char * icatalloc P((char * old, const char * new));
    -char * icpyalloc P((const char * string));
    -char * imalloc P((int n));
    -void * irealloc P((void * pointer, int size));
    -void icfree P((char * pointer));
    -void ifree P((char * pointer));
    -const char *scheck P((const char *string, const char *format));
    -
    -/*
    -** Finally, some convenience items.
    -*/
    -
    -#ifndef TRUE
    -#define TRUE 1
    -#endif /* !defined TRUE */
    -
    -#ifndef FALSE
    -#define FALSE 0
    -#endif /* !defined FALSE */
    -
    -#ifndef TYPE_BIT
    -#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
    -#endif /* !defined TYPE_BIT */
    -
    -#ifndef TYPE_SIGNED
    -#define TYPE_SIGNED(type) (((type) -1) < 0)
    -#endif /* !defined TYPE_SIGNED */
    -
    -/*
    -** Since the definition of TYPE_INTEGRAL contains floating point numbers,
    -** it cannot be used in preprocessor directives.
    -*/
    -
    -#ifndef TYPE_INTEGRAL
    -#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
    -#endif /* !defined TYPE_INTEGRAL */
    -
    -#ifndef INT_STRLEN_MAXIMUM
    -/*
    -** 302 / 1000 is log10(2.0) rounded up.
    -** Subtract one for the sign bit if the type is signed;
    -** add one for integer division truncation;
    -** add one more for a minus sign if the type is signed.
    -*/
    -#define INT_STRLEN_MAXIMUM(type) \
    - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
    - 1 + TYPE_SIGNED(type))
    -#endif /* !defined INT_STRLEN_MAXIMUM */
    -
    -/*
    -** INITIALIZE(x)
    -*/
    -
    -#ifndef GNUC_or_lint
    -#ifdef lint
    -#define GNUC_or_lint
    -#endif /* defined lint */
    -#ifndef lint
    -#ifdef __GNUC__
    -#define GNUC_or_lint
    -#endif /* defined __GNUC__ */
    -#endif /* !defined lint */
    -#endif /* !defined GNUC_or_lint */
    -
    -#ifndef INITIALIZE
    -#ifdef GNUC_or_lint
    -#define INITIALIZE(x) ((x) = 0)
    -#endif /* defined GNUC_or_lint */
    -#ifndef GNUC_or_lint
    -#define INITIALIZE(x)
    -#endif /* !defined GNUC_or_lint */
    -#endif /* !defined INITIALIZE */
    -
    -/*
    -** For the benefit of GNU folk...
    -** `_(MSGID)' uses the current locale's message library string for MSGID.
    -** The default is to use gettext if available, and use MSGID otherwise.
    -*/
    -
    -#ifndef _
    -#if HAVE_GETTEXT
    -#define _(msgid) gettext(msgid)
    -#else /* !HAVE_GETTEXT */
    -#define _(msgid) msgid
    -#endif /* !HAVE_GETTEXT */
    -#endif /* !defined _ */
    -
    -#ifndef TZ_DOMAIN
    -#define TZ_DOMAIN "tz"
    -#endif /* !defined TZ_DOMAIN */
    -
    -#if HAVE_INCOMPATIBLE_CTIME_R
    -#undef asctime_r
    -#undef ctime_r
    -char *asctime_r P((struct tm const *, char *));
    -char *ctime_r P((time_t const *, char *));
    -#endif /* HAVE_INCOMPATIBLE_CTIME_R */
    -
    -/*
    -** UNIX was a registered trademark of The Open Group in 2003.
    -*/
    -
    -#endif /* !defined PRIVATE_H */
    --- a/buddytime/recurse.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,141 +0,0 @@
    -/*************************************************************************
    - * Recursion module
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Licenced under the GNU General Public Licence version 2.
    - *
    - * Provides a function to recurse a directory and call a callback for each
    - * file found.
    - *************************************************************************/
    -
    -#define _GNU_SOURCE
    -#include <sys/types.h>
    -#include <dirent.h>
    -#include <stdio.h>
    -#include <stdlib.h>
    -#include <string.h>
    -#include <sys/stat.h>
    -
    -#include "recurse.h"
    -
    -#if 1
    -/* GLibc specific version. In this version, the entries are sorted */
    -/* We assume dirname ends in a /, prefix also unless empty */
    -static int
    -recurse_directory_int(char *dirname, char *prefix, DirRecurseMatch func, void *data)
    -{
    - struct dirent **namelist;
    - int ents;
    - struct dirent *ent;
    - int i;
    - int ret = 0;
    -
    - if((ents = scandir(dirname, &namelist, 0, alphasort)) < 0)
    - return -1;
    -
    - for (i = 0; i < ents; i++)
    - {
    - char *ptr;
    - struct stat s;
    -
    - ent = namelist[i];
    - asprintf(&ptr, "%s%s", dirname, ent->d_name);
    - if(stat(ptr, &s) < 0)
    - {
    - free(ptr);
    - continue;
    - }
    -
    - if(S_ISREG(s.st_mode))
    - {
    - free(ptr);
    - asprintf(&ptr, "%s%s", prefix, ent->d_name);
    - ret = func(ptr, data);
    - }
    - else if(S_ISDIR(s.st_mode))
    - {
    - char *newdirname, *newprefix;
    -
    - if(ent->d_name[0] != '.')
    - {
    - asprintf(&newdirname, "%s%s/", dirname, ent->d_name);
    - asprintf(&newprefix, "%s%s/", prefix, ent->d_name);
    - ret = recurse_directory_int(newdirname, newprefix, func, data);
    - free(newdirname);
    - free(newprefix);
    - }
    - }
    - free(ptr);
    - if(ret < 0)
    - break;
    - }
    - free(namelist);
    - return 0;
    -}
    -#else
    -/* generic version, here they are unsorted */
    -/* We assume dirname ends in a /, prefix also unless empty */
    -static int
    -recurse_directory_int(char *dirname, char *prefix, DirRecurseMatch func, void *data)
    -{
    - DIR *dir;
    - struct dirent *ent;
    - int ret = 0;
    -
    - dir = opendir(dirname);
    - if(!dir)
    - return -1;
    - while ((ent = readdir(dir)) != NULL)
    - {
    - char *ptr;
    - struct stat s;
    -
    - asprintf(&ptr, "%s%s", dirname, ent->d_name);
    - if(stat(ptr, &s) < 0)
    - {
    - free(ptr);
    - continue;
    - }
    -
    - if(S_ISREG(s.st_mode))
    - {
    - free(ptr);
    - asprintf(&ptr, "%s%s", prefix, ent->d_name);
    - ret = func(ptr, data);
    - }
    - else if(S_ISDIR(s.st_mode))
    - {
    - char *newdirname, *newprefix;
    -
    - if(ent->d_name[0] != '.')
    - {
    - asprintf(&newdirname, "%s%s/", dirname, ent->d_name);
    - asprintf(&newprefix, "%s%s/", prefix, ent->d_name);
    - ret = recurse_directory_int(newdirname, newprefix, func, data);
    - free(newdirname);
    - free(newprefix);
    - }
    - }
    - free(ptr);
    - if(ret < 0)
    - break;
    - }
    - closedir(dir);
    - return ret;
    -}
    -#endif
    -
    -int
    -recurse_directory(char *dirname, DirRecurseMatch func, void *data)
    -{
    - char *newdirname = NULL;
    - int ret;
    -
    - if(dirname[strlen(dirname) - 1] != '/')
    - asprintf(&newdirname, "%s/", dirname);
    -
    - ret = recurse_directory_int(newdirname ? newdirname : dirname, "", func, data);
    -
    - if(newdirname)
    - free(newdirname);
    - return ret;
    -}
    --- a/buddytime/recurse.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -/*************************************************************************
    - * Header file for recursion module
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Licenced under the GNU General Public Licence version 2.
    - *************************************************************************/
    -
    -typedef int (*DirRecurseMatch)(char *filename, void *data);
    -int recurse_directory( char *dirname, DirRecurseMatch func, void *data );
    -
    --- a/buddytime/recursetest.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,25 +0,0 @@
    -/*************************************************************************
    - * Recursion test module
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Licenced under the GNU General Public Licence version 2.
    - *
    - * Code to test the recursion module.
    - *************************************************************************/
    -
    -#include <stdio.h>
    -
    -#include "recurse.h"
    -
    -int
    -process_entry(char *str, void *ptr)
    -{
    - printf("%s\n", str);
    - return 0;
    -}
    -
    -int
    -main()
    -{
    - recurse_directory("/usr/share/zoneinfo", process_entry, main);
    - return 0;
    -}
    --- a/buddytime/timetest.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,28 +0,0 @@
    -/*************************************************************************
    - * Timezone test module
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - * Licenced under the GNU General Public Licence version 2.
    - *
    - * Code to test the timezone module.
    - *************************************************************************/
    -
    -//#include "private.h"
    -//#include "tzfile.h"
    -#include "localtime.h"
    -
    -int
    -main()
    -{
    - struct state *state;
    - time_t now = time(NULL);
    - struct tm tm;
    -
    - state = timezone_load("Australia/Sydney");
    -
    - if(!state)
    - return 0;
    -
    - localsub(&now, 0, &tm, state);
    - gmtsub(&now, 0, &tm);
    - return 0;
    -}
    --- a/buddytime/tzfile.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,175 +0,0 @@
    -/*************************************************************************
    - * Private Header file for timezone module
    - * copied from Olson timezone code, licence unchanged
    - * by Martijn van Oosterhout <kleptog@svana.org> (C) April 2006
    - *************************************************************************/
    -
    -#ifndef TZFILE_H
    -
    -#define TZFILE_H
    -
    -/*
    -** This file is in the public domain, so clarified as of
    -** 1996-06-05 by Arthur David Olson.
    -*/
    -
    -/*
    -** This header is for use ONLY with the time conversion code.
    -** There is no guarantee that it will remain unchanged,
    -** or that it will remain at all.
    -** Do NOT copy it to any system include directory.
    -** Thank you!
    -*/
    -
    -/*
    -** ID
    -*/
    -
    -/*
    -** Information about time zone files.
    -*/
    -
    -#ifndef TZDIR
    -#define TZDIR "/usr/share/zoneinfo" /* Default time zone object file directory */
    -#endif /* !defined TZDIR */
    -
    -#ifndef TZDEFAULT
    -#define TZDEFAULT "localtime"
    -#endif /* !defined TZDEFAULT */
    -
    -#ifndef TZDEFRULES
    -#define TZDEFRULES "posixrules"
    -#endif /* !defined TZDEFRULES */
    -
    -/*
    -** Each file begins with. . .
    -*/
    -
    -#define TZ_MAGIC "TZif"
    -
    -struct tzhead {
    - char tzh_magic[4]; /* TZ_MAGIC */
    - char tzh_reserved[16]; /* reserved for future use */
    - char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
    - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
    - char tzh_leapcnt[4]; /* coded number of leap seconds */
    - char tzh_timecnt[4]; /* coded number of transition times */
    - char tzh_typecnt[4]; /* coded number of local time types */
    - char tzh_charcnt[4]; /* coded number of abbr. chars */
    -};
    -
    -/*
    -** . . .followed by. . .
    -**
    -** tzh_timecnt (char [4])s coded transition times a la time(2)
    -** tzh_timecnt (unsigned char)s types of local time starting at above
    -** tzh_typecnt repetitions of
    -** one (char [4]) coded UTC offset in seconds
    -** one (unsigned char) used to set tm_isdst
    -** one (unsigned char) that's an abbreviation list index
    -** tzh_charcnt (char)s '\0'-terminated zone abbreviations
    -** tzh_leapcnt repetitions of
    -** one (char [4]) coded leap second transition times
    -** one (char [4]) total correction after above
    -** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
    -** time is standard time, if FALSE,
    -** transition time is wall clock time
    -** if absent, transition times are
    -** assumed to be wall clock time
    -** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
    -** time is UTC, if FALSE,
    -** transition time is local time
    -** if absent, transition times are
    -** assumed to be local time
    -*/
    -
    -/*
    -** In the current implementation, "tzset()" refuses to deal with files that
    -** exceed any of the limits below.
    -*/
    -
    -#ifndef TZ_MAX_TIMES
    -/*
    -** The TZ_MAX_TIMES value below is enough to handle a bit more than a
    -** year's worth of solar time (corrected daily to the nearest second) or
    -** 138 years of Pacific Presidential Election time
    -** (where there are three time zone transitions every fourth year).
    -*/
    -#define TZ_MAX_TIMES 370
    -#endif /* !defined TZ_MAX_TIMES */
    -
    -#ifndef TZ_MAX_TYPES
    -#ifndef NOSOLAR
    -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
    -#endif /* !defined NOSOLAR */
    -#ifdef NOSOLAR
    -/*
    -** Must be at least 14 for Europe/Riga as of Jan 12 1995,
    -** as noted by Earl Chew.
    -*/
    -#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
    -#endif /* !defined NOSOLAR */
    -#endif /* !defined TZ_MAX_TYPES */
    -
    -#ifndef TZ_MAX_CHARS
    -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
    - /* (limited by what unsigned chars can hold) */
    -#endif /* !defined TZ_MAX_CHARS */
    -
    -#ifndef TZ_MAX_LEAPS
    -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
    -#endif /* !defined TZ_MAX_LEAPS */
    -
    -#define SECSPERMIN 60
    -#define MINSPERHOUR 60
    -#define HOURSPERDAY 24
    -#define DAYSPERWEEK 7
    -#define DAYSPERNYEAR 365
    -#define DAYSPERLYEAR 366
    -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
    -#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
    -#define MONSPERYEAR 12
    -
    -#define TM_SUNDAY 0
    -#define TM_MONDAY 1
    -#define TM_TUESDAY 2
    -#define TM_WEDNESDAY 3
    -#define TM_THURSDAY 4
    -#define TM_FRIDAY 5
    -#define TM_SATURDAY 6
    -
    -#define TM_JANUARY 0
    -#define TM_FEBRUARY 1
    -#define TM_MARCH 2
    -#define TM_APRIL 3
    -#define TM_MAY 4
    -#define TM_JUNE 5
    -#define TM_JULY 6
    -#define TM_AUGUST 7
    -#define TM_SEPTEMBER 8
    -#define TM_OCTOBER 9
    -#define TM_NOVEMBER 10
    -#define TM_DECEMBER 11
    -
    -#define TM_YEAR_BASE 1900
    -
    -#define EPOCH_YEAR 1970
    -#define EPOCH_WDAY TM_THURSDAY
    -
    -#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
    -
    -/*
    -** Since everything in isleap is modulo 400 (or a factor of 400), we know that
    -** isleap(y) == isleap(y % 400)
    -** and so
    -** isleap(a + b) == isleap((a + b) % 400)
    -** or
    -** isleap(a + b) == isleap(a % 400 + b % 400)
    -** This is true even if % means modulo rather than Fortran remainder
    -** (which is allowed by C89 but not C99).
    -** We use this to avoid addition overflow problems.
    -*/
    -
    -#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
    -
    -#endif /* !defined TZFILE_H */
    --- a/chronic/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# Chronic
    -
    -status: incomplete
    -dependencies: libpurple
    -authors: John Bailey
    -introduced: 1.0beta3.1
    -
    -Allows buddies to remotely trigger sound playing in your running libpurple client with {S <sound>. Inspired by IRC channel resident EvilDennisR and ancient versions of AOL. THIS PLUGIN IS NOT YET FUNCTIONAL! IT IS USELESS!
    -
    --- a/chronic/chronic.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,115 +0,0 @@
    -/*
    - * Chronic - Remote sound play triggering
    - * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org>
    - *
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -/* libc */
    -#include <string.h>
    -
    -/* Purple */
    -#include <conversation.h>
    -#include <debug.h>
    -#include <plugin.h>
    -
    -static void
    -chronic_received_cb(PurpleAccount *account, char *sender, char *message,
    - PurpleConversation *conv, PurpleMessageFlags flags)
    -{
    -#if 0
    - /* this needs some work... */
    - char *sound = NULL, *path = NULL;
    -
    - if(strlen(message) > 3) {
    - if(!strncmp(message, "{S ", 3)) {
    - sound = (message + 4);
    - /* add code to find a matching sound */
    - /* purple_sound_play_file(); */
    - }
    - }
    -
    - return;
    -#endif
    -}
    -
    -static gboolean
    -chronic_load(PurplePlugin *plugin)
    -{
    - void *convhandle;
    -
    - convhandle = purple_conversations_get_handle();
    -
    - purple_signal_connect(convhandle, "received-im-msg", plugin,
    - PURPLE_CALLBACK(chronic_received_cb), NULL);
    - purple_signal_connect(convhandle, "received-chat-msg", plugin,
    - PURPLE_CALLBACK(chronic_received_cb), NULL);
    -
    - return TRUE;
    -}
    -
    -static PurplePluginInfo chronic_info =
    -{
    - PURPLE_PLUGIN_MAGIC, /* magic? do you think i'm gullible enough to
    - * believe in magic? */
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD,
    - NULL,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    - "core-plugin_pack-chronic",
    - NULL,
    - PP_VERSION,
    - NULL,
    - NULL,
    - "John Bailey <rekkanoryo@rekkanoryo.org>",
    - PP_WEBSITE,
    - chronic_load,
    - /* comment below is temporary until i decide if i need the function */
    - NULL, /*chronic_unload,*/
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -chronic_init(PurplePlugin *plugin)
    -{
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - chronic_info.name = _("Chronic");
    - chronic_info.summary = _("Sound playing triggers");
    - chronic_info.description = _("Allows buddies to remotely trigger sound"
    - " playing in your instance of Purple with {S &lt;sound&gt;. Inspired"
    - " by #guifications channel resident EvilDennisR and ancient"
    - " versions of AOL. THIS PLUGIN IS NOT YET FUNCTIONAL!"
    - " IT IS USELESS!");
    -}
    -
    -PURPLE_INIT_PLUGIN(chronic, chronic_init, chronic_info)
    --- a/chronic/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('incomplete') and PURPLE.found()
    - chronic = shared_library('chronic',
    - 'chronic.c',
    - dependencies : [PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'chronic'
    -endif
    --- a/findip/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,8 +0,0 @@
    -# Find IP
    -
    -dependencies: purple
    -authors: Sadrul Habib Chowdhury
    -introduced: 2.2.0
    -
    -Find the IP of a person in the buddylist. This doesn't really work.
    -
    --- a/findip/findip.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,182 +0,0 @@
    -/*
    - * Find IP - Find the IP of a person in the buddylist
    - * Copyright (C) 2007-2008
    - *
    - * 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 of the
    - * License, 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
    - * 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#define PLUGIN_ID "core-plugin_pack-findip"
    -#define PLUGIN_STATIC_NAME "findip"
    -#define PLUGIN_AUTHOR "someone <someone@somewhere.tld>"
    -
    -/* System headers */
    -#include <glib.h>
    -
    -/* Purple headers */
    -#include <plugin.h>
    -#include <blist.h>
    -#include <eventloop.h>
    -#include <server.h>
    -#include <version.h>
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#define PREF_ROOT "/plugins/core/plugin_pack/" PLUGIN_STATIC_NAME
    -#define PREF_NOTIFY PREF_ROOT "/notify"
    -
    -static gboolean
    -show_ip(gpointer node)
    -{
    - PurpleBuddy *buddy;
    - PurpleConversation *conv;
    -
    - buddy = (PurpleBuddy*)node;
    - if (buddy->account == NULL || buddy->account->gc == NULL)
    - return FALSE;
    -
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, buddy->account, buddy->name);
    - purple_conversation_write(conv, NULL, _("Looked up IP: 127.0.0.1\n"),
    - PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM,
    - time(NULL));
    - if (purple_prefs_get_bool(PREF_NOTIFY))
    - serv_send_im(buddy->account->gc, buddy->name, _("Yo! What's your IP?"), 0);
    - return FALSE;
    -}
    -
    -static void
    -find_ip(PurpleBlistNode *node, gpointer plugin)
    -{
    - PurpleConversation *conv;
    - PurpleBuddy *buddy;
    -
    - if (PURPLE_BLIST_NODE_IS_CONTACT(node))
    - node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);
    - if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
    - return;
    -
    - purple_timeout_add_seconds(5, show_ip, node);
    -
    - buddy = (PurpleBuddy*)node;
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, buddy->account, buddy->name);
    - purple_conversation_write(conv, NULL, _("Looking up the IP ...\n"),
    - PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM,
    - time(NULL));
    -}
    -
    -static void
    -context_menu(PurpleBlistNode *node, GList **menu, gpointer plugin)
    -{
    - PurpleMenuAction *action;
    -
    - if (!PURPLE_BLIST_NODE_IS_BUDDY(node) && !PURPLE_BLIST_NODE_IS_CONTACT(node))
    - return;
    -
    - action = purple_menu_action_new(_("Find IP"),
    - PURPLE_CALLBACK(find_ip), plugin, NULL);
    - (*menu) = g_list_prepend(*menu, action);
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin)
    -{
    - purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin,
    - PURPLE_CALLBACK(context_menu), plugin);
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin)
    -{
    - return TRUE;
    -}
    -
    -static PurplePluginPrefFrame *
    -get_plugin_pref_frame(PurplePlugin *plugin)
    -{
    - PurplePluginPrefFrame *frame;
    - PurplePluginPref *pref;
    -
    - frame = purple_plugin_pref_frame_new();
    -
    - pref = purple_plugin_pref_new_with_name_and_label(PREF_NOTIFY,
    - _("Notify the user that you are trying to get the IP"));
    - purple_plugin_pref_frame_add(frame, pref);
    -
    - return frame;
    -}
    -
    -static PurplePluginUiInfo pref_info = {
    - get_plugin_pref_frame,
    - 0,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static PurplePluginInfo info = {
    - PURPLE_PLUGIN_MAGIC, /* Magic */
    - PURPLE_MAJOR_VERSION, /* Purple Major Version */
    - PURPLE_MINOR_VERSION, /* Purple Minor Version */
    - PURPLE_PLUGIN_STANDARD, /* plugin type */
    - NULL, /* ui requirement */
    - 0, /* flags */
    - NULL, /* dependencies */
    - PURPLE_PRIORITY_DEFAULT, /* priority */
    -
    - PLUGIN_ID, /* plugin id */
    - NULL, /* name */
    - PP_VERSION, /* version */
    - NULL, /* summary */
    - NULL, /* description */
    - PLUGIN_AUTHOR, /* author */
    - PP_WEBSITE , /* website */
    -
    - plugin_load, /* load */
    - plugin_unload, /* unload */
    - NULL, /* destroy */
    -
    - NULL, /* ui_info */
    - NULL, /* extra_info */
    - &pref_info, /* prefs_info */
    - NULL, /* actions */
    - NULL, /* reserved */
    - NULL, /* reserved */
    - NULL, /* reserved */
    - NULL /* reserved */
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin) {
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("Find IP");
    - info.summary = _("Find the IP of a person in the buddylist.");
    - info.description = _("Find the IP of a person in the buddylist. This doesn't really work.");
    -
    - purple_prefs_add_none(PREF_ROOT);
    - purple_prefs_add_bool(PREF_NOTIFY, TRUE);
    -}
    -
    -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
    --- a/findip/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('default') and PURPLE.found()
    - findip = shared_library('findip',
    - 'findip.c',
    - dependencies : [PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'findip'
    -endif
    --- a/hideconv/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# Hide Conversation
    -
    -dependencies: pidgin
    -authors: Sadrul Habib Chowdhury
    -introduced: 1.0
    -notes: Superseded by functionality present in Pidgin 2.3.0 and newer
    -
    -Hide conversations without closing them.
    -
    --- a/hideconv/hideconv.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,291 +0,0 @@
    -/*
    - * Hide Conversations - You can hide conversations without having to close them.
    - * Copyright (C) 2007-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.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 the Free Software Foundation; either version 2 of the
    - * License, 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
    - * 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#define PLUGIN_ID "gtk-plugin_pack-hideconv"
    -#define PLUGIN_STATIC_NAME "hideconv"
    -#define PLUGIN_AUTHOR "Sadrul H Chowdhury <sadrul@users.sourceforge.net>"
    -
    -/* System headers */
    -#include <gdk/gdk.h>
    -#include <gtk/gtk.h>
    -
    -/* Purple headers */
    -#include <gtkconv.h>
    -#include <gtkconvwin.h>
    -#include <gtkplugin.h>
    -#include <gtkutils.h>
    -
    -#define MENUSET "hideconv::menuset"
    -
    -static PidginWindow *window = NULL;
    -static void (*orig_conv_present)(PurpleConversation *conv);
    -
    -static void conv_created(PurpleConversation *conv, gpointer null);
    -
    -static void
    -create_hidden_convwin()
    -{
    - /* This is a 'wee bit' hacky. Create two conv windows, remove the second
    - * one from the list, and then destroy the first one. This is because we
    - * want to hide this entire conversation window from pidgin itself.
    - */
    - GList *null;
    - PidginWindow *tmp = pidgin_conv_window_new();
    - window = pidgin_conv_window_new();
    - null = pidgin_conv_windows_get_list();
    - null = g_list_remove(null, window);
    - pidgin_conv_window_hide(window);
    - pidgin_conv_window_destroy(tmp);
    -}
    -
    -static void
    -gtkconv_redisplaying(PidginConversation *gtkconv)
    -{
    - conv_created(gtkconv->active_conv, NULL);
    - g_signal_handlers_disconnect_by_func(G_OBJECT(gtkconv->imhtml),
    - G_CALLBACK(gtkconv_redisplaying), gtkconv);
    -}
    -
    -static void
    -hide_gtkconv(PidginConversation *gtkconv)
    -{
    - pidgin_conv_window_add_gtkconv(window, gtkconv);
    - g_signal_connect_swapped(G_OBJECT(gtkconv->imhtml), "visibility_notify_event",
    - G_CALLBACK(gtkconv_redisplaying), gtkconv);
    -}
    -
    -static void
    -hide_conv_cb(GtkWidget *wid, PidginWindow *win)
    -{
    - PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
    - pidgin_conv_window_remove_gtkconv(win, gtkconv);
    - hide_gtkconv(gtkconv);
    -}
    -
    -static void
    -show_convs_cb(PurplePluginAction *dontcare)
    -{
    - GList *list = g_list_copy(pidgin_conv_window_get_gtkconvs(window)), *iter;
    -
    - for (iter = list; iter; iter = iter->next) {
    - PidginConversation *gtkconv = iter->data;
    - pidgin_conv_window_remove_gtkconv(window, gtkconv);
    - pidgin_conv_placement_place(gtkconv);
    - purple_conversation_present(gtkconv->active_conv);
    - conv_created(gtkconv->active_conv, NULL);
    - }
    - g_list_free(list);
    -
    - create_hidden_convwin();
    -}
    -
    -static void
    -attach_menu_to_window(PidginWindow *win)
    -{
    - GtkWidget *widget, *item;
    - if (g_object_get_data(G_OBJECT(win->window), MENUSET))
    - return;
    - g_object_set_data(G_OBJECT(win->window), MENUSET, GINT_TO_POINTER(TRUE));
    -
    - widget = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options"));
    -
    - /* We cannot use pidgin_separator, unfortunately. */
    - item = gtk_separator_menu_item_new();
    - gtk_widget_show(item);
    - gtk_menu_shell_append(GTK_MENU_SHELL(widget), item);
    - g_object_set_data(G_OBJECT(item), MENUSET, GINT_TO_POINTER(TRUE));
    -
    - item = gtk_menu_item_new_with_mnemonic(_("_Hide Conversation"));
    - g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(hide_conv_cb), win);
    - gtk_widget_show(item);
    - gtk_menu_shell_append(GTK_MENU_SHELL(widget), item);
    - g_object_set_data(G_OBJECT(item), MENUSET, GINT_TO_POINTER(TRUE));
    -
    - item = gtk_menu_item_new_with_mnemonic(_("Show Hidden Conversations"));
    - g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(show_convs_cb), NULL);
    - gtk_widget_show(item);
    - gtk_menu_shell_append(GTK_MENU_SHELL(widget), item);
    - g_object_set_data(G_OBJECT(item), MENUSET, GINT_TO_POINTER(TRUE));
    -}
    -
    -static void
    -detach_menu_from_window(PidginWindow *win)
    -{
    - GtkWidget *widget;
    - GList *children;
    -
    - widget = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options"));
    - children = gtk_container_get_children(GTK_CONTAINER(widget));
    - while (children) {
    - GtkWidget *item = children->data;
    - children = children->next;
    - if (g_object_get_data(G_OBJECT(item), MENUSET))
    - gtk_widget_destroy(item);
    - }
    - g_object_set_data(G_OBJECT(win->window), MENUSET, GINT_TO_POINTER(FALSE));
    -}
    -
    -static gboolean
    -conv_created_to(PurpleConversation *conv)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    -
    - if (!gtkconv || !gtkconv->win || !gtkconv->win->window)
    - return TRUE;
    - if (!GTK_WIDGET_VISIBLE(gtkconv->win->window))
    - return TRUE;
    -
    - attach_menu_to_window(gtkconv->win);
    - return FALSE;
    -}
    -
    -static void
    -conv_created(PurpleConversation *conv, gpointer null)
    -{
    - g_timeout_add(1000, (GSourceFunc)conv_created_to, conv);
    -}
    -
    -static void
    -twisted_present(PurpleConversation *conv)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    - if (gtkconv && gtkconv->win == window) {
    - gboolean last = (g_list_length(window->gtkconvs) == 1);
    - pidgin_conv_window_remove_gtkconv(window, gtkconv);
    - pidgin_conv_placement_place(gtkconv);
    - if (last)
    - create_hidden_convwin();
    - }
    - orig_conv_present(conv);
    - conv_created(conv, NULL);
    -}
    -
    -static void
    -hide_all_conv(PurplePluginAction *dontcare)
    -{
    - GList *iter = pidgin_conv_windows_get_list();
    - while (iter) {
    - GList *it = pidgin_conv_window_get_gtkconvs(iter->data);
    - iter = iter->next;
    - while (it) {
    - PidginConversation *gtkconv = it->data;
    - it = it->next;
    - pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv);
    - hide_gtkconv(gtkconv);
    - }
    - }
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin)
    -{
    - PurpleConversationUiOps *ops = pidgin_conversations_get_conv_ui_ops();
    - orig_conv_present = ops->present;
    - ops->present = twisted_present;
    -
    - create_hidden_convwin();
    -
    - purple_signal_connect(purple_conversations_get_handle(), "conversation-created",
    - plugin, PURPLE_CALLBACK(conv_created), NULL);
    -
    - g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)attach_menu_to_window, NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin)
    -{
    - PurpleConversationUiOps *ops = pidgin_conversations_get_conv_ui_ops();
    - ops->present = orig_conv_present;
    -
    - show_convs_cb(NULL);
    - pidgin_conv_window_destroy(window);
    - window = NULL;
    -
    - g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)detach_menu_from_window, NULL);
    -
    - return TRUE;
    -}
    -
    -static GList *
    -actions(PurplePlugin *plugin, gpointer context)
    -{
    - PurplePluginAction *act;
    - GList *list = NULL;
    -
    - act = purple_plugin_action_new(_("Show All Hidden Conversations"), show_convs_cb);
    - list = g_list_append(list, act);
    -
    - act = purple_plugin_action_new(_("Hide All Conversations"), hide_all_conv);
    - list = g_list_append(list, act);
    -
    - return list;
    -}
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC, /* Magic */
    - PURPLE_MAJOR_VERSION, /* Purple Major Version */
    - PURPLE_MINOR_VERSION, /* Purple Minor Version */
    - PURPLE_PLUGIN_STANDARD, /* plugin type */
    - PIDGIN_PLUGIN_TYPE, /* ui requirement */
    - 0, /* flags */
    - NULL, /* dependencies */
    - PURPLE_PRIORITY_DEFAULT, /* priority */
    -
    - PLUGIN_ID, /* plugin id */
    - NULL, /* name */
    - PP_VERSION, /* version */
    - NULL, /* summary */
    - NULL, /* description */
    - PLUGIN_AUTHOR, /* author */
    - PP_WEBSITE, /* website */
    -
    - plugin_load, /* load */
    - plugin_unload, /* unload */
    - NULL, /* destroy */
    -
    - NULL, /* ui_info */
    - NULL, /* extra_info */
    - NULL, /* prefs_info */
    - actions, /* actions */
    - NULL, /* reserved 1 */
    - NULL, /* reserved 2 */
    - NULL, /* reserved 3 */
    - NULL /* reserved 4 */
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin) {
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("Hide Conversation");
    - info.summary = _("Hide conversations without closing them.");
    - info.description = _("Hide conversations without closing them.");
    -}
    -
    -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
    --- a/hideconv/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('incomplete') and PIDGIN.found()
    - hideconv = shared_module('hideconv',
    - 'hideconv.c',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'hideconv'
    -endif
    --- a/ignorance/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,10 +0,0 @@
    -# Ignorance
    -
    -status: incomplete
    -dependencies: pidgin
    -authors: Levi Bard
    -introduced: 1.0beta7
    -notes: Needs some TLC. It builds and probably works, but is far from an acceptible state.
    -
    -Allows management of users with various levels of activity.
    -
    --- a/ignorance/callbacks.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,701 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#include <string.h>
    -#include <gtk/gtk.h>
    -
    -#include "callbacks.h"
    -#include "interface.h"
    -#include "support.h"
    -#include "ignorance.h"
    -#include "ignorance_internal.h"
    -
    -static gboolean populate_panel(GtkTreeSelection *sel);
    -static gboolean verify_form();
    -static gboolean add_rule_from_form(GtkTreeView *view);
    -static gboolean add_group_from_form(GtkTreeView *view);
    -static gboolean edit_rule_from_form(GtkTreeView *view);
    -static gboolean del_rule_from_form(GtkTreeView *view);
    -static gboolean del_group_from_form(GtkTreeView *view);
    -static gboolean ignorance_rule_valid(const gchar *ruletext, gint ruletype);
    -static gboolean ignorance_rulename_valid(const gchar *rule_name);
    -
    -gboolean on_levelView_row_activated (GtkTreeSelection *sel, gpointer user_data) {
    - populate_panel(sel);
    - return FALSE;
    -}
    -
    -void on_levelAdd_clicked (GtkButton *button, gpointer user_data) {
    -
    - if(verify_form()) {
    - if(!add_rule_from_form(user_data)) {
    - /* XXX: */
    - }
    - }
    -}
    -
    -void on_groupAdd_clicked (GtkButton *button, gpointer user_data) {
    - if(verify_form()) {
    - if(!add_group_from_form(user_data)) {
    - /* XXX: Do something I guess? -- sadrul */
    - }
    - }
    -}
    -
    -void on_levelEdit_clicked (GtkButton *button, gpointer user_data) {
    - if(verify_form()) {
    - if(!edit_rule_from_form(user_data)) {
    - /* XXX: Do something I guess? -- sadrul */
    - }
    - }
    -}
    -
    -void
    -on_levelDel_clicked (GtkButton *button, gpointer user_data) {
    - if(rule_selected) {
    - if(verify_form()) {
    - if(!del_rule_from_form(user_data)) {
    - /* XXX: */
    - }
    - }
    - } else {
    - del_group_from_form(user_data);
    - }
    -}
    -
    -static gboolean populate_panel(GtkTreeSelection *sel) {
    - GtkTreeIter iter;
    - GtkTreeModel *model;
    - gchar *levnames, *rulenames;
    - ignorance_level *level=NULL;
    - ignorance_rule *rule=NULL;
    - GString *levgs, *rulegs;
    -
    - if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - gtk_tree_model_get (model, &iter, LEVEL_COLUMN, &levnames, -1);
    - gtk_tree_model_get(model,&iter,RULE_COLUMN,&rulenames,-1);
    - if(strlen(rulenames)) {
    - rule_selected=TRUE;
    - levgs=g_string_new(levnames);
    - rulegs=g_string_new(rulenames);
    - level=ignorance_get_level_name(levgs);
    - rule=ignorance_level_get_rule(level,rulegs);
    - if(!rule) {
    - fprintf(stderr,"Ignorance: Unable to find rule %s on level %s\n",
    - rulegs->str,level->name->str);
    - return FALSE;
    - }
    -
    - gtk_entry_set_text(GTK_ENTRY(rulename),rulenames);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb),
    - rule->score & IGNORANCE_FLAG_FILTER);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ignore_cb),
    - rule->score & IGNORANCE_FLAG_IGNORE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(message_cb),
    - rule->score & IGNORANCE_FLAG_MESSAGE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sound_cb),
    - rule->score & IGNORANCE_FLAG_SOUND);
    -
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(execute_cb),
    - rule->score & IGNORANCE_FLAG_EXECUTE);
    -
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(message_cb))) {
    - gtk_entry_set_text(GTK_ENTRY(message_entry),rule->message);
    - }
    -
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sound_cb))) {
    - gtk_entry_set_text(GTK_ENTRY(sound_entry),rule->sound);
    - }
    -
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(execute_cb))) {
    - gtk_entry_set_text(GTK_ENTRY(execute_entry),rule->command);
    - }
    -
    - gtk_entry_set_text(GTK_ENTRY(filtervalue),
    - (const gchar*)(rule->value));
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(im_type_cb),
    - rule->flags & IGNORANCE_APPLY_IM);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chat_type_cb),
    - rule->flags & IGNORANCE_APPLY_CHAT);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(username_type_cb),
    - rule->flags & IGNORANCE_APPLY_USER);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enterleave_type_cb),
    - rule->flags & IGNORANCE_APPLY_ENTERLEAVE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(invite_type_cb),
    - rule->flags & IGNORANCE_APPLY_INVITE);
    -
    -
    -#ifdef HAVE_REGEX_H
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(regex_cb),
    - rule->type & IGNORANCE_RULE_REGEX);
    -#endif
    -
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_cb),
    - rule->type & IGNORANCE_RULE_REPEAT);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enabled_cb),
    - rule->enabled);
    -
    - gtk_button_set_label(GTK_BUTTON(levelDel),"Remove rule");
    -
    - g_string_free(levgs,TRUE);
    - g_string_free(rulegs,TRUE);
    - } else {
    - rule_selected=FALSE;
    - gtk_entry_set_text(GTK_ENTRY(rulename),"");
    - gtk_entry_set_text(GTK_ENTRY(filtervalue),"");
    -
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(im_type_cb),FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chat_type_cb),FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(username_type_cb),
    - FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enterleave_type_cb),
    - FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(invite_type_cb),
    - FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb),FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ignore_cb),FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(message_cb),FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sound_cb),FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(execute_cb),FALSE);
    -
    -#ifdef HAVE_REGEX_H
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(regex_cb),FALSE);
    -#endif
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_cb),FALSE);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enabled_cb),FALSE);
    -
    - gtk_button_set_label(GTK_BUTTON(levelDel),"Remove group");
    - }
    -
    - g_free(levnames);
    - g_free(rulenames);
    - }
    -
    - return FALSE;
    -}
    -
    -void on_filter_cb_toggled (GtkButton *button, gpointer user_data) {
    - gboolean amdisabled=!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
    -
    - if(!amdisabled) {
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(message_cb),amdisabled);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ignore_cb),amdisabled);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sound_cb),amdisabled);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(execute_cb),amdisabled);
    - }
    -
    - /* Maybe we *should* allow them to send a message on filter */
    - gtk_widget_set_sensitive(message_cb,amdisabled);
    - gtk_widget_set_sensitive(ignore_cb,amdisabled);
    - gtk_widget_set_sensitive(sound_cb,amdisabled);
    - gtk_widget_set_sensitive(execute_cb,amdisabled);
    -}
    -
    -void on_ignore_cb_toggled (GtkButton *button, gpointer user_data) {
    - gboolean amdisabled=!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
    -
    - if(!amdisabled) {
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(message_cb),amdisabled);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb),amdisabled);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sound_cb),amdisabled);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(execute_cb),amdisabled);
    - }
    -
    - /* Maybe we *should* allow them to send a message on ignore */
    - gtk_widget_set_sensitive(message_cb,amdisabled);
    - gtk_widget_set_sensitive(filter_cb,amdisabled);
    - gtk_widget_set_sensitive(sound_cb,amdisabled);
    - gtk_widget_set_sensitive(execute_cb,amdisabled);
    -}
    -
    -void on_message_cb_toggled (GtkButton *button, gpointer user_data) {
    - gboolean isactive=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(message_cb));
    - if(!isactive) {
    - gtk_entry_set_text(GTK_ENTRY(message_entry),"");
    - }
    - gtk_widget_set_sensitive(message_entry,isactive);
    -}
    -
    -void on_sound_cb_toggled (GtkButton *button, gpointer user_data) {
    - gboolean isactive=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sound_cb));
    -
    - if(!isactive) {
    - gtk_entry_set_text(GTK_ENTRY(sound_entry),"");
    - }
    - gtk_widget_set_sensitive(sound_entry,isactive);
    -#if GTK_CHECK_VERSION(2,4,0)
    - gtk_widget_set_sensitive(sound_browse,isactive);
    -#endif
    -}
    -
    -void on_execute_cb_toggled (GtkButton *button, gpointer user_data) {
    - gboolean isactive=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(execute_cb));
    - if(!isactive) {
    - gtk_entry_set_text(GTK_ENTRY(execute_entry),"");
    - }
    - gtk_widget_set_sensitive(execute_entry,isactive);
    - /*gtk_widget_set_sensitive(execute_browse,isactive);*/
    -}
    -
    -void on_sound_browse_clicked (GtkButton *button, gpointer user_data) {
    -#if GTK_CHECK_VERSION(2,4,0)
    - GtkWidget *fcd=gtk_file_chooser_dialog_new("Choose sound",NULL,
    - GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
    - GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
    - NULL);
    - gchar *filename=NULL;
    -
    - if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(fcd))) {
    - filename=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
    - if(filename) {
    - gtk_entry_set_text(GTK_ENTRY(sound_entry),filename);
    - g_free(filename);
    - }
    - }
    -
    - gtk_widget_destroy(fcd);
    -#endif
    -}
    -
    -/*No command browser for now
    -void on_execute_browse_clicked (GtkButton *button, gpointer user_data) {
    - gchar *filename=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(execute_browse));
    - if(filename) {
    - gtk_entry_set_text(GTK_ENTRY(execute_entry),filename);
    - }
    -}
    -*/
    -
    -static gboolean verify_form() {
    - gboolean rv=TRUE;
    - gchar *tmp;
    - gint type=IGNORANCE_RULE_SIMPLETEXT;
    -
    - tmp=(gchar*)gtk_entry_get_text(GTK_ENTRY(filtervalue));
    -#ifdef HAVE_REGEX_H
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(regex_cb))) {
    - type=IGNORANCE_RULE_REGEX;
    - }
    -#endif
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_cb))) {
    - type=IGNORANCE_RULE_REPEAT;
    - }
    -
    - rv=ignorance_rule_valid(tmp,type);
    -
    - if(!rv) {
    - purple_debug_error("ignorance","Rule invalid: %s\n",tmp);
    - }else{
    - tmp=(gchar*)gtk_entry_get_text(GTK_ENTRY(rulename));
    - rv=ignorance_rulename_valid(tmp);
    -
    - if(!rv) {
    - purple_debug_error("ignorance","Rule name invalid: %s\n",tmp);
    - }
    - }
    -
    - return rv;
    -}
    -
    -
    -static gboolean add_rule_from_form(GtkTreeView *view) {
    - ignorance_rule *rule=NULL;
    - ignorance_level *level=NULL;
    - GString *rule_name, *level_name;
    - GtkTreeSelection *sel;
    - GtkTreeIter iter, childiter;
    - GtkTreeModel *model;
    - gchar *rule_value=NULL, *rule_message=NULL, *rule_sound=NULL,
    - *rule_command=NULL;
    - gint rule_score=0, rule_type=IGNORANCE_RULE_SIMPLETEXT, rule_flags=0;
    - gboolean rv=FALSE, rule_enable;
    -
    - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
    -
    - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - return rv;
    - }
    -
    - gtk_tree_model_get (model, &iter, LEVEL_COLUMN, &rule_value, -1);
    -
    - if(!rule_value) {
    - return rv;
    - }else if(!strlen(rule_value)) {
    - g_free(rule_value);
    - return rv;
    - }
    -
    - level_name=g_string_new(rule_value);
    - g_free(rule_value);
    -
    - gtk_tree_model_get(model,&iter,RULE_COLUMN,&rule_value,-1);
    - if(strlen(rule_value)) {
    - childiter=iter;
    - gtk_tree_model_iter_parent(model,&iter,&childiter);
    - }
    -
    - g_free(rule_value);
    -
    - level=ignorance_get_level_name(level_name);
    -
    - rule_name=g_string_new(gtk_entry_get_text(GTK_ENTRY(rulename)));
    -
    - rule=ignorance_level_get_rule(level,rule_name);
    -
    - if(rule) {
    - g_string_free(level_name,TRUE);
    - g_string_free(rule_name,TRUE);
    - return rv;
    - }
    -
    -#ifdef HAVE_REGEX_H
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(regex_cb))) {
    - rule_type=IGNORANCE_RULE_REGEX;
    - }
    -#endif
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_cb))) {
    - rule_type=IGNORANCE_RULE_REPEAT;
    - }
    -
    - rule_value=(gchar*)gtk_entry_get_text(GTK_ENTRY(filtervalue));
    -
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(im_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_IM;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chat_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_CHAT;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(username_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_USER;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enterleave_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_ENTERLEAVE;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(invite_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_INVITE;
    - }
    -
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(filter_cb))) {
    - rule_score |= IGNORANCE_FLAG_FILTER;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ignore_cb))) {
    - rule_score |= IGNORANCE_FLAG_IGNORE;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(message_cb))) {
    - rule_score |= IGNORANCE_FLAG_MESSAGE;
    - rule_message=(gchar*)gtk_entry_get_text(GTK_ENTRY(message_entry));
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sound_cb))) {
    - rule_score |= IGNORANCE_FLAG_SOUND;
    - rule_sound=(gchar*)gtk_entry_get_text(GTK_ENTRY(sound_entry));
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(execute_cb))) {
    - rule_score |= IGNORANCE_FLAG_EXECUTE;
    - rule_command=(gchar*)gtk_entry_get_text(GTK_ENTRY(execute_entry));
    - }
    -
    - rule_enable=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enabled_cb));
    -
    - rule=ignorance_rule_newp(rule_name,rule_type,rule_value,rule_score,
    - rule_flags,rule_enable,rule_message,rule_sound,
    - rule_command);
    -
    - rv=ignorance_level_add_rule(level,rule);
    -
    - if(rv) {
    - gtk_tree_store_append(GTK_TREE_STORE(model),&childiter,&iter);
    - gtk_tree_store_set(GTK_TREE_STORE(model),&childiter,LEVEL_COLUMN,level_name->str,RULE_COLUMN,rule_name->str,-1);
    - }
    -
    - g_string_free(level_name,TRUE);
    - g_string_free(rule_name,TRUE);
    -
    - return rv;
    -}
    -
    -static gboolean add_group_from_form(GtkTreeView *view) {
    - gboolean rv=TRUE;
    - GtkTreeIter iter;
    - GtkTreeStore *store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(view)));
    -
    - ignorance_level *level=ignorance_level_new();
    - level->name=g_string_new(gtk_entry_get_text(GTK_ENTRY(rulename)));
    -
    - rv=ignorance_add_level(level);
    -
    - if(rv) {
    - gtk_tree_store_append(store,&iter,NULL);
    - gtk_tree_store_set (store, &iter, LEVEL_COLUMN, level->name->str,
    - RULE_COLUMN, "", -1);
    - }
    -
    - return rv;
    -}
    -
    -static gboolean edit_rule_from_form(GtkTreeView *view) {
    - ignorance_level *level;
    - ignorance_rule *rule;
    - GString *rule_name, *level_name;
    - GtkTreeSelection *sel;
    - GtkTreeIter iter;
    - GtkTreeModel *model;
    - gchar *rule_value, *rule_message=NULL, *rule_sound=NULL, *rule_command=NULL;
    - gint rule_score=0, rule_type=IGNORANCE_RULE_SIMPLETEXT, rule_flags=0;
    - gboolean rule_enable, rv;
    -
    - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
    -
    - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - return FALSE;
    - }
    -
    - gtk_tree_model_get (model, &iter, LEVEL_COLUMN, &rule_value, -1);
    -
    - if(!rule_value) {
    - return FALSE;
    - }else if(!strlen(rule_value)) {
    - g_free(rule_value);
    - return FALSE;
    - }
    -
    - level_name=g_string_new(rule_value);
    - g_free(rule_value);
    - level=ignorance_get_level_name(level_name);
    -
    - rule_name=g_string_new(gtk_entry_get_text(GTK_ENTRY(rulename)));
    -
    - rule=ignorance_level_get_rule(level,rule_name);
    -
    - if(!rule) {
    - fprintf(stderr,"Ignorance: Rule \"%s\" not found on level %s\n",rule_name->str,level_name->str);
    - g_string_free(rule_name,TRUE);
    - g_string_free(level_name,TRUE);
    - return FALSE;
    - }
    -
    -#ifdef HAVE_REGEX_H
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(regex_cb))) {
    - rule_type=IGNORANCE_RULE_REGEX;
    - }
    -#endif
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_cb))) {
    - rule_type=IGNORANCE_RULE_REPEAT;
    - }
    -
    - rule->type=rule_type;
    -
    - rule_value=(gchar*)gtk_entry_get_text(GTK_ENTRY(filtervalue));
    - g_free(rule->value);
    - rule->value=g_strdup(rule_value);
    -
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(im_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_IM;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chat_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_CHAT;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(username_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_USER;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enterleave_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_ENTERLEAVE;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(invite_type_cb))) {
    - rule_flags |= IGNORANCE_APPLY_INVITE;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(filter_cb))) {
    - rule_score |= IGNORANCE_FLAG_FILTER;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ignore_cb))) {
    - rule_score |= IGNORANCE_FLAG_IGNORE;
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(message_cb))) {
    - rule_score |= IGNORANCE_FLAG_MESSAGE;
    - rule_message=(gchar*)gtk_entry_get_text(GTK_ENTRY(message_entry));
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sound_cb))) {
    - rule_score |= IGNORANCE_FLAG_SOUND;
    - rule_sound=(gchar*)gtk_entry_get_text(GTK_ENTRY(sound_entry));
    - }
    - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(execute_cb))) {
    - rule_score |= IGNORANCE_FLAG_EXECUTE;
    - rule_command=(gchar*)gtk_entry_get_text(GTK_ENTRY(execute_entry));
    - }
    -
    - rule_enable=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enabled_cb));
    -
    - rule=ignorance_rule_newp(rule_name, rule_type, rule_value, rule_score,
    - rule_flags,rule_enable,rule_message,rule_sound,
    - rule_command);
    -
    - rv=ignorance_level_remove_rule(level,rule_name);
    -
    - if(rv) {
    - rv=ignorance_level_add_rule(level,rule);
    - }
    -
    - rule->flags=rule_flags;
    - rule->enabled=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enabled_cb));
    -
    - g_string_free(rule_name,TRUE);
    - g_string_free(level_name,TRUE);
    -
    - return rv;
    -}
    -
    -static gboolean del_rule_from_form(GtkTreeView *view) {
    - ignorance_level *level;
    - ignorance_rule *rule;
    - GString *rule_name, *level_name;
    - GtkTreeSelection *sel;
    - GtkTreeIter iter;
    - GtkTreeModel *model;
    - gchar *rule_value;
    - gboolean rv=FALSE;
    -
    - sel=gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
    -
    - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - return rv;
    - }
    -
    - gtk_tree_model_get (model, &iter, LEVEL_COLUMN, &rule_value, -1);
    -
    - if(!rule_value) {
    - return rv;
    - }else if(!strlen(rule_value)) {
    - g_free(rule_value);
    - return rv;
    - }
    -
    - level_name=g_string_new(rule_value);
    - g_free(rule_value);
    - level=ignorance_get_level_name(level_name);
    -
    - rule_name=g_string_new(gtk_entry_get_text(GTK_ENTRY(rulename)));
    -
    - rule=ignorance_level_get_rule(level,rule_name);
    -
    - if(!rule) {
    - fprintf(stderr,"Ignorance: Rule \"%s\" not found on level %s\n",
    - rule_name->str,level_name->str);
    - g_string_free(rule_name,TRUE);
    - g_string_free(level_name,TRUE);
    - return rv;
    - }
    -
    - rv=ignorance_level_remove_rule(level,rule_name);
    -
    - if(rv) {
    - gtk_tree_store_remove(GTK_TREE_STORE(model),&iter);
    - }
    -
    - g_string_free(rule_name,TRUE);
    - g_string_free(level_name,TRUE);
    -
    - return rv;
    -}
    -
    -static gboolean del_group_from_form(GtkTreeView *view) {
    - GString *level_name;
    - GtkTreeSelection *sel;
    - GtkTreeIter iter;
    - GtkTreeModel *model;
    - gchar *rule_value;
    - gboolean rv=FALSE;
    -
    - sel=gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
    -
    - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
    - return rv;
    - }
    -
    - gtk_tree_model_get (model, &iter, LEVEL_COLUMN, &rule_value, -1);
    -
    - if(!rule_value) {
    - return rv;
    - }else if(!strlen(rule_value)) {
    - g_free(rule_value);
    - return rv;
    - }
    -
    - level_name=g_string_new(rule_value);
    - g_free(rule_value);
    -
    - ignorance_remove_level(level_name);
    - gtk_tree_store_remove(GTK_TREE_STORE(model),&iter);
    - rv=TRUE;
    -
    - return rv;
    -}
    -
    -
    -
    -gboolean load_form_with_levels(GtkTreeView *tree, GPtrArray *levels) {
    - int i,j;
    - GtkTreeStore *store=GTK_TREE_STORE(gtk_tree_view_get_model(tree));
    - GtkTreeIter iter, ruleiter;
    - ignorance_level *level;
    - ignorance_rule *rule;
    -
    - if(!levels) {
    - return FALSE;
    - }
    -
    - for(i=0;i<levels->len;++i) {
    - level=(ignorance_level*)g_ptr_array_index(levels,i);
    - gtk_tree_store_append(store,&iter,NULL);
    - gtk_tree_store_set (store, &iter, LEVEL_COLUMN, level->name->str,
    - RULE_COLUMN, "", -1);
    -
    - for(j=0;j<level->rules->len;++j) {
    - rule=(ignorance_rule*)g_ptr_array_index(level->rules,j);
    - gtk_tree_store_append(store,&ruleiter,&iter);
    - gtk_tree_store_set(store,&ruleiter, LEVEL_COLUMN, level->name->str,
    - RULE_COLUMN, rule->name->str, -1);
    - }
    - }
    -
    - return FALSE;
    -}
    -
    -static gboolean ignorance_rule_valid(const gchar *ruletext, gint ruletype) {
    - gboolean rv=FALSE;
    -
    -#ifdef HAVE_REGEX_H
    - regex_t reg;
    -#endif
    -
    - switch(ruletype) {
    - case IGNORANCE_RULE_SIMPLETEXT:
    - case IGNORANCE_RULE_REPEAT:
    - rv=TRUE;
    - break;
    -#ifdef HAVE_REGEX_H
    - case IGNORANCE_RULE_REGEX:
    - rv=!((gboolean)regcomp(&reg,ruletext,REG_EXTENDED | REG_NOSUB));
    - rv=(rv && strlen(ruletext));
    - regfree(&reg);
    - break;
    -#endif
    - }
    -
    - return rv;
    -}
    -
    -static gboolean ignorance_rulename_valid(const gchar *rule_name) {
    - return TRUE;
    -}
    --- a/ignorance/callbacks.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,60 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_CALLBACKS_H
    -#define IGNORANCE_CALLBACKS_H
    -
    -#include <gtk/gtk.h>
    -
    -#ifdef HAVE_REGEX_H
    -# include <regex.h>
    -#endif
    -
    -enum{
    - LEVEL_COLUMN,
    - RULE_COLUMN,
    - NUM_COLUMNS
    -};
    -
    -gboolean on_levelView_row_activated (GtkTreeSelection *sel, gpointer user_data);
    -
    -void on_levelAdd_clicked (GtkButton *button, gpointer user_data);
    -
    -void on_groupAdd_clicked (GtkButton *button, gpointer user_data);
    -
    -void on_levelEdit_clicked (GtkButton *button, gpointer user_data);
    -
    -void on_levelDel_clicked (GtkButton *button, gpointer user_data);
    -
    -void on_sound_browse_clicked (GtkButton *button, gpointer user_data);
    -
    -/*
    -void on_execute_browse_clicked (GtkButton *button, gpointer user_data);
    -*/
    -
    -void on_filter_cb_toggled (GtkButton *button, gpointer user_data);
    -
    -void on_ignore_cb_toggled (GtkButton *button, gpointer user_data);
    -
    -void on_message_cb_toggled (GtkButton *button, gpointer user_data);
    -
    -void on_sound_cb_toggled (GtkButton *button, gpointer user_data);
    -
    -void on_execute_cb_toggled (GtkButton *button, gpointer user_data);
    -
    -gboolean load_form_with_levels (GtkTreeView *tree, GPtrArray *levels);
    -
    -#endif
    --- a/ignorance/ignorance.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1209 +0,0 @@
    -/*
    - * Ignorance - Make up for deficiencies in Purple's privacy
    - *
    - * Copyright (c) 200?-2006 Levi Bard
    - * Copyright (c) 2005-2006 Peter Lawler
    - * Copyright (c) 2005-2006 John Bailey
    - *
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#define PURPLE_PLUGINS
    -
    -/* ignorance headers */
    -#include "ignorance_level.h"
    -#include "ignorance_internal.h"
    -#include "ignorance.h"
    -#include "ignorance_denizen.h"
    -#include "ignorance_violation.h"
    -#include "ignorance_rule.h"
    -
    -/* GTK Purple headers */
    -#include <gtkplugin.h>
    -#include <gtkutils.h>
    -
    -#include "interface.h"
    -
    -/* libpurple headers */
    -#include <blist.h>
    -#include <cmds.h>
    -#include <conversation.h>
    -#include <purple.h>
    -#include <plugin.h>
    -#include <privacy.h>
    -#include <signals.h>
    -#include <sound.h>
    -#include <util.h>
    -#include <version.h>
    -
    -/* libc headers */
    -#include <errno.h>
    -#include <signal.h>
    -#include <stdio.h>
    -#include <string.h>
    -#include <stdlib.h>
    -#include <time.h>
    -#include <unistd.h>
    -
    -/* for OSes that can at least *pretend* to be decent... */
    -#ifndef _WIN32
    -# include <sys/wait.h>
    -# include <sys/types.h>
    -# include <sys/select.h>
    -# include <fcntl.h>
    -# include <sys/stat.h>
    -#endif
    -
    -/* globals */
    -static GPtrArray *levels;
    -
    -static ignorance_level* ignorance_get_default_level() {
    - gint i = 0;
    - ignorance_level *il;
    -
    - for(i = 0; i < levels->len; ++i) {
    - il = (ignorance_level*)g_ptr_array_index(levels, i);
    -
    - if(!strcmp(il->name->str, "Default"))
    - return il;
    - }
    -
    - return NULL;
    -}
    -
    -static gboolean ignorance_user_match(ignorance_level *il, const GString *username){
    - return (ignorance_level_has_denizen(il,username));
    -}
    -
    -static ignorance_level* ignorance_get_user_level(const GString *username){
    - gint i = 0;
    - ignorance_level *il = NULL;
    -
    - for(i = 0; i < levels->len; ++i){
    - il = (ignorance_level*)g_ptr_array_index(levels, i);
    - if(ignorance_user_match(il, username))
    - return il;
    - }
    -
    - return ignorance_get_default_level();
    -}
    -
    -static void purple_buddy_add(gpointer key, gpointer value, gpointer user_data) {
    - PurpleBuddy *buddy = (PurpleBuddy*)value;
    - ignorance_level *level = (ignorance_level*)user_data;
    - PurpleAccount *account = NULL;
    - gchar *name = NULL;
    - GString *tmp;
    -
    - if(buddy && level) {
    - name = (gchar*)buddy->name;
    - account = buddy->account;
    - tmp = g_string_new(purple_account_get_protocol_id(account));
    - g_string_append(tmp, purple_normalize_nocase(account, name));
    -
    - if(ignorance_get_user_level(tmp) == ignorance_get_default_level()) {
    - ignorance_level_add_denizen(level, tmp);
    -
    - if(strstr(level->name->str, "WL")) {
    - purple_privacy_deny_remove(account, name, FALSE);
    - purple_privacy_permit_add(account, name, FALSE);
    - } else if(strstr(level->name->str,"BL")) {
    - purple_privacy_permit_remove(account, name, FALSE);
    - purple_privacy_deny_add(account, name, FALSE);
    - }
    - }
    -
    - g_string_free(tmp, TRUE);
    - } else {
    - purple_debug_error("ignorance", "Bad arguments to purple_buddy_add\n");
    - }
    -}
    -
    -static gint buf_get_line(gchar *ibuf, gchar **buf, gint *position, gint len) {
    - gint pos = *position,
    - spos = pos;
    -
    - if (pos == len)
    - return 0;
    -
    - while (ibuf[pos++] != '\n') {
    - if (pos == len)
    - return 0;
    - }
    -
    - pos--;
    - ibuf[pos] = 0;
    - *buf = &ibuf[spos];
    - pos++;
    - *position = pos;
    -
    - return 1;
    -}
    -
    -static gboolean import_curphoo_list() {
    - gchar *buf, *ibuf;
    - gint pnt = 0,
    - rule_flags = IGNORANCE_APPLY_CHAT | IGNORANCE_APPLY_IM | IGNORANCE_APPLY_ENTERLEAVE;
    - gsize size = 0;
    - FILE *curphoofile;
    - ignorance_level *curphoolevel = NULL;
    - ignorance_rule *curphoorule = NULL;
    - GString *tmp = NULL;
    -
    - buf = g_build_filename(g_get_home_dir(), ".curphoo", "ignore", NULL);
    -
    - if(!(curphoofile = fopen(buf, "r"))) {
    - purple_debug_error("ignorance", "Unable to open %s\n", buf);
    - g_free(buf);
    -
    - return FALSE;
    - } else
    - fclose(curphoofile);
    -
    - g_file_get_contents(buf, &ibuf, &size, NULL);
    -
    - tmp = g_string_new("CurphooBL");
    - curphoolevel = ignorance_get_level_name(tmp);
    -
    - if(!curphoolevel) {
    - purple_debug_info("ignorance", "Creating new Curphoo blacklist\n");
    -#ifdef HAVE_REGEX_H
    - curphoorule = ignorance_rule_newp(g_string_new("Everything"),
    - IGNORANCE_RULE_REGEX,(gchar*)".*", IGNORANCE_FLAG_FILTER,
    - rule_flags, TRUE, NULL, NULL, NULL);
    -#else
    - curphoorule=ignorance_rule_newp(g_string_new("Everything"),
    - IGNORANCE_RULE_SIMPLETEXT, (gchar*)"", IGNORANCE_FLAG_FILTER,
    - rule_flags, TRUE, NULL, NULL, NULL);
    -#endif
    - curphoolevel = ignorance_level_new();
    - curphoolevel->name = g_string_new(tmp->str);
    - ignorance_level_add_rule(curphoolevel, curphoorule);
    - ignorance_add_level(curphoolevel);
    - }
    -
    - if(!tmp)
    - tmp = g_string_new("");
    -
    - g_free(buf);
    -
    - purple_debug_info("ignorance", "Preparing to read in curphoo blacklist users\n");
    -
    - for(; buf_get_line(ibuf, &buf, &pnt, size); ){
    - g_string_assign(tmp, "prpl-yahoo");
    - g_string_append(tmp, purple_normalize_nocase(NULL,buf));
    -
    - if(ignorance_get_user_level(tmp) == ignorance_get_default_level())
    - ignorance_level_add_denizen(curphoolevel, tmp);
    - }
    -
    - g_free(ibuf);
    -
    - pnt = 0;
    - buf = g_build_filename(g_get_home_dir(), ".curphoo", "buddies", NULL);
    -
    - g_file_get_contents(buf, &ibuf, &size, NULL);
    -
    - g_string_assign(tmp, "CurphooWL");
    -
    - curphoolevel = ignorance_get_level_name(tmp);
    -
    - if(!curphoolevel) {
    - purple_debug_info("ignorance", "Creating new Curphoo whitelist\n");
    -
    - curphoolevel = ignorance_level_new();
    - curphoolevel->name = g_string_new(tmp->str);
    - ignorance_add_level(curphoolevel);
    - }
    -
    - g_free(buf);
    -
    - purple_debug_info("ignorance", "Preparing to read in curphoo whitelist users\n");
    -
    - for(; buf_get_line(ibuf, &buf, &pnt, size); ) {
    - g_string_assign(tmp, "prpl-yahoo");
    - g_string_append(tmp, purple_normalize_nocase(NULL, buf));
    -
    - if(ignorance_get_user_level(tmp) == ignorance_get_default_level())
    - ignorance_level_add_denizen(curphoolevel, tmp);
    - }
    -
    - g_free(ibuf);
    -
    - purple_debug_info("ignorance", "Done importing Curphoo users\n");
    -
    - return TRUE;
    -}
    -
    -static gboolean import_purple_list() {
    - PurpleBuddyList *bl;
    - GString *wlname;
    - ignorance_level *wl;
    - gboolean rv = FALSE;
    -
    - bl = purple_get_blist();
    - wlname = g_string_new("WL");
    - wl = ignorance_get_level_name(wlname);
    -
    - if(bl && wl) {
    - g_hash_table_foreach(bl->buddies, purple_buddy_add, wl);
    - rv = TRUE;
    - } else
    - purple_debug_error("ignorance", "Unable to get Purple buddy list!\n");
    -
    - g_string_free(wlname, TRUE);
    -
    - return rv;
    -}
    -
    -static gboolean import_zinc_list()
    -{
    - FILE *zincfile;
    - gchar *buf, *ibuf;
    - gint pnt = 0,
    - rule_flags = IGNORANCE_APPLY_CHAT | IGNORANCE_APPLY_IM | IGNORANCE_APPLY_ENTERLEAVE;
    - gsize size = 0;
    - ignorance_level *zinclevel = NULL;
    - ignorance_rule *zincrule = NULL;
    - GString *tmp = NULL;
    -
    - buf = g_build_filename(g_get_home_dir(), ".zinc", "ignore", NULL);
    -
    - if(!(zincfile = fopen(buf, "r"))) {
    - purple_debug_error("ignorance", "Unable to open %s\n",buf);
    -
    - g_free(buf);
    -
    - return FALSE;
    - } else
    - fclose(zincfile);
    -
    - g_file_get_contents(buf, &ibuf, &size, NULL);
    -
    - tmp = g_string_new("ZincBL");
    -
    - zinclevel = ignorance_get_level_name(tmp);
    -
    - if(!zinclevel){
    - purple_debug_info("ignorance", "Creating new Zinc blacklist\n");
    -#ifdef HAVE_REGEX_H
    - zincrule = ignorance_rule_newp(g_string_new("Everything"),
    - IGNORANCE_RULE_REGEX, (gchar*)".*", IGNORANCE_FLAG_FILTER,
    - rule_flags, TRUE, NULL, NULL, NULL);
    -#else
    - zincrule = ignorance_rule_newp(g_string_new("Everything"),
    - IGNORANCE_RULE_SIMPLETEXT, (gchar*)"", IGNORANCE_FLAG_FILTER,
    - rule_flags, TRUE, NULL, NULL, NULL);
    -#endif
    - zinclevel = ignorance_level_new();
    - zinclevel->name = g_string_new(tmp->str);
    - ignorance_level_add_rule(zinclevel, zincrule);
    - ignorance_add_level(zinclevel);
    - }
    -
    - if(!tmp)
    - tmp=g_string_new("");
    -
    - g_free(buf);
    -
    - purple_debug_info("ignorance", "Preparing to read in zinc blacklist users\n");
    -
    - for(; buf_get_line(ibuf, &buf, &pnt, size); ) {
    - g_string_assign(tmp, "prpl-yahoo");
    - g_string_append(tmp, purple_normalize_nocase(NULL, buf));
    - if(ignorance_get_user_level(tmp) == ignorance_get_default_level())
    - ignorance_level_add_denizen(zinclevel, tmp);
    - }
    -
    - g_free(ibuf);
    -
    - pnt = 0;
    - buf = g_build_filename(g_get_home_dir(), ".zinc", "whitelist", NULL);
    -
    - g_file_get_contents(buf, &ibuf, &size, NULL);
    -
    - g_string_assign(tmp, "ZincWL");
    -
    - zinclevel = ignorance_get_level_name(tmp);
    -
    - if(!zinclevel) {
    - purple_debug_info("ignorance", "Creating new Zinc whitelist\n");
    - zinclevel = ignorance_level_new();
    - zinclevel->name = g_string_new(tmp->str);
    - ignorance_add_level(zinclevel);
    - }
    -
    - g_free(buf);
    -
    - purple_debug_info("ignorance", "Preparing to read in zinc whitelist users\n");
    -
    - for(; buf_get_line(ibuf, &buf, &pnt, size); ) {
    - /* maybe this will be this way eventually, when i kill unnecessary
    - * GStrings - rekkanoryo */
    - /* tmp = g_strdup_printf("prpl-yahoo %s", purple_normalize_nocase(NULL, buf)); */
    - g_string_assign(tmp, "prpl-yahoo");
    - g_string_append(tmp, purple_normalize_nocase(NULL, buf));
    - if(ignorance_get_user_level(tmp) == ignorance_get_default_level())
    - ignorance_level_add_denizen(zinclevel, tmp);
    - }
    -
    - g_free(ibuf);
    -
    - purple_debug_info("ignorance", "Done importing Zinc users\n");
    -
    - return TRUE;
    -}
    -
    -static gboolean ignorance_rm_user(PurpleConversation *conv, const gchar *username) {
    - gchar *msgbuf = NULL, *cursor = NULL;
    - gboolean retval = FALSE;
    - GString *usergs;
    - ignorance_level *level = NULL;
    - int len=0;
    -
    - usergs = g_string_new(purple_normalize_nocase(NULL, username));
    -
    - level=ignorance_get_user_level(usergs);
    -
    - if(level) {
    - retval = ignorance_level_remove_denizen(level, usergs);
    -
    - purple_debug_info("ignorance", "Done removing denizen from level\n");
    -
    - if(conv) {
    - purple_debug_info("ignorance",
    - "Creating status message for username %x and level %x\n",
    - username, level);
    -
    - if(retval) {
    - msgbuf = g_strdup_printf(_("Successfully removed %s from %s"),
    - username, level->name->str);
    -
    - retval = TRUE;
    - } else
    - msgbuf = g_strdup_printf(_("Unable to remove %s from %s\n"),
    - username, level->name->str);
    -
    - purple_debug_info("ignorance", "Writing status message\n");
    -
    - purple_conversation_write(conv, NULL, msgbuf, PURPLE_MESSAGE_NO_LOG,
    - time(NULL));
    -
    - g_free(msgbuf);
    - }
    - }
    -
    - if(conv) {
    - purple_debug_info("ignorance",
    - "Preparing to push through to purple privacy\n");
    -
    - len = strlen(purple_account_get_protocol_id(
    - purple_conversation_get_account(conv)));
    -
    - cursor = (gchar*)username + len;
    -
    - if(cursor && (strlen(username) > len)) {
    - purple_debug_info("ignorance", "Removing from permit list\n");
    -
    - purple_privacy_permit_remove(purple_conversation_get_account(conv),
    - cursor, FALSE);
    -
    - purple_debug_info("ignorance", "Removing from deny list\n");
    -
    - purple_privacy_deny_remove(purple_conversation_get_account(conv), cursor,
    - FALSE);
    -
    - if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
    - purple_debug_info("ignorance", "Removing from chat ignore list\n");
    -
    - purple_conv_chat_unignore(PURPLE_CONV_CHAT(conv), cursor);
    - }
    - }
    - }
    -
    - g_string_free(usergs, TRUE);
    -
    - purple_debug_info("ignorance", "Exiting\n");
    -
    - return retval;
    -}
    -
    -static gboolean
    -ignorance_place_user_name(const GString *level_name, const GString *username) {
    - ignorance_level *current_level = ignorance_get_user_level(username),
    - *newlevel = ignorance_get_level_name(level_name);
    -
    - if(newlevel) {
    - if(newlevel != current_level) {
    - ignorance_level_remove_denizen(current_level, username);
    - ignorance_level_add_denizen(newlevel, username);
    -
    - return TRUE;
    - }
    - } else
    - purple_debug_error("ignorance", "Invalid level %s\n", level_name->str);
    -
    - return FALSE;
    -}
    -
    -static gboolean ignorance_bl_user(PurpleConversation *conv, const gchar *username,
    - const gchar *actual_levelname)
    -{
    - gboolean retval = FALSE;
    - gchar *msgbuf = NULL;
    - GString *wlname, *usergs;
    - PurpleAccount *account = NULL;
    -
    - wlname = g_string_new(actual_levelname);
    -
    - g_return_val_if_fail(conv != NULL, retval);
    -
    - account = purple_conversation_get_account(conv);
    - usergs = g_string_new(purple_account_get_protocol_id(account));
    - g_string_append(usergs, purple_normalize_nocase(NULL, username));
    -
    - if(ignorance_place_user_name(wlname, usergs)){
    - retval = TRUE;
    - purple_privacy_permit_remove(account, username, FALSE);
    - purple_privacy_deny_add(account, username, FALSE);
    -
    - if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
    - purple_conv_chat_ignore(PURPLE_CONV_CHAT(conv), username);
    -
    - msgbuf = g_strdup_printf(_("Assigned user %s to %s"), username,
    - actual_levelname);
    - } else
    - msgbuf = g_strdup_printf(
    - _("Unable to assign user %s to %s - may already be there"),
    - username, actual_levelname);
    -
    - purple_conversation_write(conv, NULL, msgbuf, PURPLE_MESSAGE_NO_LOG, time(NULL));
    -
    - g_free(msgbuf);
    - g_string_free(usergs, TRUE);
    - g_string_free(wlname, TRUE);
    -
    - return retval;
    -}
    -
    -#ifndef _WIN32
    -static int read_nonblock(int fd,unsigned long len,unsigned long timeout,GString *inp){
    - int chrs=0, timedout=FALSE, rv=0;
    - unsigned long timeout_usec=timeout*1000000, tlapsed=0;
    - static const int SLEEP_INTERVAL=50000;
    -
    - gchar *ptr=g_malloc((len+1)*sizeof(gchar));
    -
    - while (chrs < len){
    - if (tlapsed > timeout_usec){
    - timedout = TRUE;
    - break;
    - }
    - if ((rv = read (fd, ptr, (len - chrs))) < 0){
    - rv = errno;
    - usleep (SLEEP_INTERVAL);
    - tlapsed += SLEEP_INTERVAL;
    - continue;
    - } else if (rv == 0)
    - break;
    - chrs += rv;
    - *(ptr + rv)= 0;
    - g_string_append(inp,ptr);
    - *ptr=0;
    - }
    - g_free(ptr);
    - if(timedout)
    - return -1;
    - else
    - return chrs;
    -}
    -#endif
    -
    -static gchar *
    -yahoo_strip_tattoo(gchar *origmessage) {
    - gchar *cursor = NULL, *cursor2 = NULL, *beginstack = NULL,
    - *endstack = NULL, *message = NULL;
    - gint rvindex;
    -
    - message = g_ascii_strdown(origmessage, -1);
    - cursor = strstr(message, "<font");
    -
    - if(message == cursor) {
    - cursor2 = strstr(message, "tattoo");
    -
    - if(cursor2) {
    - cursor = strstr(cursor2, ">");
    -
    - for( ; cursor; ) {
    - endstack = strstr(cursor, "</");
    - beginstack = strstr(cursor, "<");
    -
    - if(endstack == NULL || beginstack== NULL) {
    - cursor = NULL;
    - break;
    - }
    -
    - cursor = strstr(endstack, ">");
    -
    - if(beginstack == endstack)
    - break;
    - }
    -
    - if(cursor) {
    - rvindex = cursor + 1 - message;
    -
    - purple_debug_info("yahoo", "%s\nconverted to \n%s\n%s\n\n",
    - origmessage, cursor + 1, origmessage + rvindex);
    -
    - g_free(message);
    -
    - return origmessage + rvindex;
    - }
    - }
    - }
    -
    - g_free(message);
    -
    - return origmessage;
    -}
    -
    -static int
    -handle_exec_command (const gchar *command, GString *result,unsigned long maxlen) {
    -#ifndef _WIN32
    - GString *inp=g_string_new("");
    - int p[2], pid, chrs;
    -
    - pipe(p);
    -
    - chrs = inp->len;
    - if ((pid = fork ()) == -1) {
    - g_string_assign (result, command);
    - g_string_append (result, ": couldn't fork");
    - return -1;
    - } else if (pid){
    - int rv;
    - int flags = fcntl (p[0], F_GETFL, 0);
    -
    - close (p[1]);
    - fcntl (p[0], F_SETFL, flags | O_NONBLOCK);
    -
    - rv=read_nonblock(p[0],maxlen-chrs,EXEC_TIMEOUT,inp);
    -
    - if (kill (pid, 0) == 0)
    - kill (pid, SIGKILL);
    - if (rv < 0)
    - g_string_append (inp, "[process timed out]");
    - else if(maxlen==(rv+chrs))
    - g_string_append(inp,"...");
    - else if(('\n'==(inp->str)[inp->len-1]))
    - g_string_truncate(inp,inp->len-1);
    -
    - g_string_assign (result, inp->str);
    -
    - g_string_free(inp,TRUE);
    - waitpid (pid, NULL, 0);
    - } else {
    - close (p[0]);
    - if (p[1] != STDOUT_FILENO){
    - dup2 (p[1], STDOUT_FILENO);
    - close (p[1]);
    - }
    - if (STDERR_FILENO != STDOUT_FILENO)
    - dup2 (STDOUT_FILENO, STDERR_FILENO);
    - execlp ("sh", "sh", "-c", command, NULL);
    - }
    - return 0;
    -#else
    - gint retval;
    - gchar *message = NULL;
    -
    - g_string_assign (result, command);
    - g_string_append (result, ": ");
    -
    - if (G_WIN32_HAVE_WIDECHAR_API ()) {
    - wchar_t *wc_cmd = g_utf8_to_utf16(command,
    - -1, NULL, NULL, NULL);
    -
    - retval = (gint)ShellExecuteW(NULL,NULL,wc_cmd,NULL,NULL,SW_SHOWNORMAL);
    - g_free(wc_cmd);
    - } else {
    - char *l_cmd = g_locale_from_utf8(command,
    - -1, NULL, NULL, NULL);
    -
    - retval = (gint)ShellExecuteA(NULL,NULL,l_cmd,NULL,NULL,SW_SHOWNORMAL);
    - g_free(l_cmd);
    - }
    -
    - if (retval<=32) {
    - message = g_win32_error_message(retval);
    - g_string_append(result,message);
    - }
    -
    - purple_debug_info("Ignorance", "Execute command called for: "
    - "%s\n%s%s%s", command, retval ? "" : "Error: ",
    - retval ? "" : message, retval ? "" : "\n");
    - g_free(message);
    - return 0;
    -#endif
    -}
    -
    -static gboolean
    -apply_rule(PurpleConversation *conv, PurpleAccount *account,
    - const GString *username, const GString *text, gint flags)
    -{
    - gint text_score = 0;
    - gboolean rv = TRUE, newconv = FALSE;
    - GList *violations = NULL, *cursor = NULL;
    - GString *tmp;
    - GString *prpluser = g_string_new(purple_account_get_protocol_id(account));
    - ignorance_level *user_level;
    - ignorance_violation *viol;
    - PurpleConversationType conv_type;
    -
    - g_string_append(prpluser, purple_normalize_nocase(account, username->str));
    - user_level=ignorance_get_user_level(prpluser);
    -
    - purple_debug_info("ignorance", "Preparing to check %s\n", text->str);
    -
    - text_score=ignorance_level_rulecheck(user_level, prpluser, text, flags,
    - &violations);
    -
    - purple_debug_info("ignorance", "Got score %d\n", text_score);
    -
    - if(!(text_score & (IGNORANCE_FLAG_FILTER | IGNORANCE_FLAG_IGNORE))) {
    - rv = FALSE;
    - if(text_score) {
    - for(cursor = violations; cursor; cursor = cursor->next) {
    - viol = (ignorance_violation*)(cursor->data);
    - purple_debug_info("ignorance", "Got violation type %d: %s\n",
    - viol->type,viol->value);
    -
    - switch(viol->type) {
    - case IGNORANCE_FLAG_SOUND:
    - purple_debug_info("ignorance",
    - "Attempting to play sound %s\n", viol->value);
    -
    - purple_sound_play_file(viol->value, account);
    -
    - break;
    -
    - case IGNORANCE_FLAG_EXECUTE:
    - purple_debug_info("ignorance",
    - "Attempting to execute command %s\n",
    - viol->value);
    -
    - tmp = g_string_new("");
    -
    - handle_exec_command(viol->value, tmp, 512);
    -
    - if(conv)
    - purple_conversation_write(conv,
    - purple_account_get_username(account),
    - tmp->str, PURPLE_MESSAGE_NO_LOG, time(NULL));
    -
    - g_string_free(tmp, TRUE);
    -
    - break;
    -
    - case IGNORANCE_FLAG_MESSAGE:
    - if(!conv) {
    - newconv = TRUE;
    - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
    - account, username->str);
    - }
    -
    - conv_type = purple_conversation_get_type(conv);
    -
    - if(conv_type == PURPLE_CONV_TYPE_IM) {
    - purple_conv_im_send(PURPLE_CONV_IM(conv), viol->value);
    - } else if(conv_type == PURPLE_CONV_TYPE_CHAT) {
    - purple_conv_chat_send(PURPLE_CONV_CHAT(conv),
    - viol->value);
    - } /* braces for readability only */
    -
    - if(newconv)
    - purple_conversation_destroy(conv);
    -
    - break;
    -
    - default:
    - break;
    - }
    - }
    - }
    - } else if(text_score & IGNORANCE_FLAG_IGNORE)
    - ignorance_bl_user(conv, username->str, "BL");
    -
    - g_string_free(prpluser, TRUE);
    -
    - purple_debug_info("ignorance", "Preparing to free violation items\n");
    - g_list_foreach(violations, ignorance_violation_free_g, NULL);
    -
    - purple_debug_info("ignorance",
    - "Done freeing violation items, now freeing list itself\n");
    - g_list_free(violations);
    -
    - purple_debug_info("ignorance",
    - "Done checking, returning from applying rules\n");
    -
    - return rv;
    -}
    -
    -static gboolean substitute (PurpleConversation *conv,PurpleAccount *account,
    - const gchar *sender, gchar **message, gint msgflags) {
    - GString *username=NULL, *text=NULL;
    - gboolean rv=FALSE;
    - gchar *newmsg, *cursor;
    -
    - if (NULL == message)
    - return FALSE;
    - else if (NULL == (*message))
    - return FALSE;
    -
    -
    - username=g_string_new(purple_normalize_nocase(account,sender));
    -
    - purple_debug_info("ignorance","Got message \"%s\" from user \"%s\"\n",*message,sender);
    -
    - cursor=yahoo_strip_tattoo(*message);
    - if(cursor!=*message){
    - newmsg=g_strdup(cursor);
    - g_free(*message);
    - (*message)=newmsg;
    - }
    -
    - text=g_string_new(*message);
    -
    - rv=apply_rule(conv,account,username,text,msgflags);
    -
    - if(rv)
    - purple_debug_info("ignorance", "%s: %s violated!\n",username->str,text->str);
    -
    - g_string_free(username,TRUE);
    - g_string_free(text,TRUE);
    -
    - purple_debug_info("ignorance","Returning from substitution\n");
    -
    - return rv;
    -}
    -
    -static gboolean chat_cb(PurpleAccount *account, gchar **sender, gchar **buffer,
    - PurpleConversation *chat, void *data){
    - return substitute(chat, account, *sender, buffer,
    - IGNORANCE_APPLY_CHAT | IGNORANCE_APPLY_USER);
    -}
    -
    -static gboolean im_cb(PurpleAccount *account, gchar **sender, gchar **buffer,
    - gint *flags, void *data){
    - PurpleConversation *gc;
    -
    - gc=purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,*sender,account);
    -
    - return substitute(gc, account, *sender,buffer,
    - IGNORANCE_APPLY_IM | IGNORANCE_APPLY_USER);
    -}
    -
    -static gboolean chat_joinleave_cb (PurpleConversation *conv, const gchar *name,
    - void *data){
    - gchar *message=g_strdup(name);
    - gboolean rv=substitute(conv, purple_conversation_get_account(conv), name,
    - &message,IGNORANCE_APPLY_ENTERLEAVE | IGNORANCE_APPLY_USER);
    - g_free(message);
    -
    - return rv;
    -}
    -
    -static gint chat_invited_cb(PurpleAccount *account,const gchar *inviter, const gchar *chat, const gchar *invite_message, const GHashTable *components, void *data){
    - gchar *message=g_strdup(invite_message);
    - gboolean rv;
    - gint invite_ask=0;
    - PurpleConversation *gc;
    - g_free(message);
    -
    - gc=purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,inviter,account);
    -
    - rv=substitute(gc,account,inviter,&message,IGNORANCE_APPLY_INVITE | IGNORANCE_APPLY_USER | IGNORANCE_APPLY_CHAT);
    -
    - if(rv)
    - invite_ask=-1;
    -
    - return invite_ask;
    -}
    -
    -static void buddy_added_cb(PurpleBuddy *buddy, gpointer data){
    - GString *wlname=g_string_new("WL");
    - ignorance_level *wl=ignorance_get_level_name(wlname);
    - PurpleAccount *account=buddy->account;
    - gchar *name=(gchar*)buddy->name;
    -
    - purple_debug_info("ignorance","Caught buddy-added for %s%s\n",
    - purple_account_get_protocol_id(account), name);
    - purple_buddy_add(NULL,buddy,wl);
    -
    - g_string_free(wlname,TRUE);
    -}
    -
    -static void buddy_removed_cb(PurpleBuddy *buddy, gpointer data){
    - GString *tmp=NULL;
    - PurpleConversation *conv=NULL;
    - PurpleAccount *account=buddy->account;
    - gchar *name=(gchar*)buddy->name;
    -
    - tmp=g_string_new(purple_account_get_protocol_id(account));
    -
    - conv=purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,tmp->str,account);
    -
    - purple_debug_info("ignorance","Caught buddy-removed for %s%s\n",
    - purple_account_get_protocol_id(account), name);
    -
    - g_string_append(tmp,purple_normalize_nocase(account,name));
    -
    - ignorance_rm_user(conv,tmp->str);
    -
    - g_string_free(tmp,TRUE);
    -}
    -
    -static gboolean generate_default_levels() {
    - ignorance_level *tmplvl;
    - ignorance_rule *tmprule;
    - GString *tmpgs;
    -
    - tmplvl=ignorance_level_new();
    - tmplvl->name=g_string_new("Default");
    - ignorance_add_level(tmplvl);
    -
    - tmplvl=ignorance_level_new();
    - tmplvl->name=g_string_new("WL");
    - ignorance_add_level(tmplvl);
    -
    - tmplvl=ignorance_level_new();
    - tmplvl->name=g_string_new("BL");
    - ignorance_add_level(tmplvl);
    -
    - tmpgs=g_string_new("Everything");
    -#ifdef HAVE_REGEX_H
    - tmprule=ignorance_rule_newp(tmpgs,IGNORANCE_RULE_REGEX,".*",
    - IGNORANCE_FLAG_FILTER,
    - IGNORANCE_APPLY_CHAT | IGNORANCE_APPLY_IM,
    - TRUE,NULL,NULL,NULL);
    -#else
    - tmprule=ignorance_rule_newp(tmpgs,IGNORANCE_RULE_SIMPLETEXT,"",
    - IGNORANCE_FLAG_FILTER,
    - IGNORANCE_APPLY_CHAT | IGNORANCE_APPLY_IM,
    - TRUE,NULL,NULL,NULL);
    -#endif
    - ignorance_level_add_rule(tmplvl,tmprule);
    - g_string_free(tmpgs,TRUE);
    -
    - return TRUE;
    -}
    -
    -gboolean save_conf()
    -{
    - FILE *f;
    - gchar *name, tempfilename[BUF_LONG];
    - gint fd, i;
    -
    - name = g_build_filename(purple_user_dir(), "ignorance", NULL);
    - strcpy(tempfilename, name);
    - strcat(tempfilename,".XXXXXX");
    - fd = g_mkstemp(tempfilename);
    - if(fd<0) {
    - perror(tempfilename);
    - g_free(name);
    - return FALSE;
    - }
    - if (!(f = fdopen(fd, "w"))) {
    - perror("fdopen");
    - close(fd);
    - g_free(name);
    - return FALSE;
    - }
    -
    - fchmod(fd, S_IRUSR | S_IWUSR);
    -
    - for(i=0;i<levels->len;++i){
    - ignorance_level_write(g_ptr_array_index(levels,i),f);
    - }
    -
    - if(fclose(f)) {
    - purple_debug_error("ignorance",
    - "Error writing to %s: %m\n", tempfilename);
    - unlink(tempfilename);
    - g_free(name);
    - return FALSE;
    - }
    - rename(tempfilename, name);
    - g_free(name);
    - return TRUE;
    -}
    -
    -ignorance_level* ignorance_get_level_name(const GString *levelname){
    - gint i=0;
    - ignorance_level *il=NULL;
    -
    - for(i=0;i<levels->len;++i){
    - il=(ignorance_level*)g_ptr_array_index(levels,i);
    - if(g_string_equal(levelname,il->name))
    - return il;
    - }
    -
    - return NULL;
    -}
    -
    -gboolean ignorance_add_level(ignorance_level *level){
    - gboolean rv=FALSE;
    -
    - if(level){
    - g_ptr_array_add(levels,level);
    - rv=TRUE;
    - }
    -
    - return rv;
    -}
    -
    -gboolean ignorance_remove_level(const GString *levelname){
    - ignorance_level *level=ignorance_get_level_name(levelname);
    - gboolean rv=FALSE;
    -
    - if(level){
    - rv=g_ptr_array_remove(levels,level);
    - ignorance_level_free(level);
    - }
    -
    - return rv;
    -}
    -
    -static void
    -ignorance_signals_connect(PurplePlugin *plugin)
    -{
    - void *conv_handle, *blist_handle;
    -
    - conv_handle = purple_conversations_get_handle();
    - blist_handle = purple_blist_get_handle();
    -
    - purple_signal_connect(conv_handle, "receiving-im-msg", plugin,
    - PURPLE_CALLBACK (im_cb), NULL);
    - purple_signal_connect(conv_handle, "receiving-chat-msg", plugin,
    - PURPLE_CALLBACK (chat_cb), NULL);
    - purple_signal_connect(conv_handle,"chat-buddy-joining", plugin,
    - PURPLE_CALLBACK(chat_joinleave_cb),NULL);
    - purple_signal_connect(conv_handle,"chat-buddy-leaving", plugin,
    - PURPLE_CALLBACK(chat_joinleave_cb),NULL);
    - purple_signal_connect(conv_handle,"chat-invited", plugin,
    - PURPLE_CALLBACK(chat_invited_cb),NULL);
    - purple_signal_connect(blist_handle,"buddy-added", plugin,
    - PURPLE_CALLBACK(buddy_added_cb),NULL);
    - purple_signal_connect(blist_handle,"buddy-removed", plugin,
    - PURPLE_CALLBACK(buddy_removed_cb),NULL);
    -
    - return;
    -}
    -
    -static gboolean load_conf() {
    - gchar *buf, *ibuf;
    - gint pnt = 0;
    - gsize size;
    - FILE *conffile = NULL;
    - static ignorance_level *tmplvl = NULL;
    - static ignorance_rule *tmprule = NULL;
    - GString *tmpgs = NULL;
    -
    - buf = g_build_filename(purple_user_dir(), "ignorance", NULL);
    -
    - purple_debug_info("ignorance", "Attempting to load conf file %s\n",buf);
    -
    - levels = g_ptr_array_new();
    -
    - if(!(conffile = fopen(buf, "r"))) {
    - g_free(buf);
    -
    - buf=g_build_filename(IGNORANCE_CONFDIR,"ignorance.conf",NULL);
    -
    - if(!(conffile = fopen(buf,"r"))) {
    - purple_debug_info("ignorance",
    - "Unable to open local or global conf files; falling back to defaults\n");
    - generate_default_levels();
    - import_purple_list();
    - import_zinc_list();
    - import_curphoo_list();
    -
    - g_free(buf);
    - return FALSE;
    - }
    - }
    -
    - g_file_get_contents(buf, &ibuf, &size, NULL);
    - fclose(conffile);
    - g_free(buf);
    -
    - if(!ibuf) {
    - generate_default_levels();
    - import_purple_list();
    - import_zinc_list();
    - import_curphoo_list();
    - return FALSE;
    - }
    -
    - while(buf_get_line(ibuf, &buf, &pnt, size)) {
    - if((*buf) == '#'){
    - /* ignore */
    - } else if(strstr(buf, "level")){
    - if('\0' == buf[5]){
    - tmpgs = g_string_new("");
    -
    - for( ; buf != strstr(buf, "/level");
    - buf_get_line(ibuf, &buf, &pnt, size))
    - {
    - g_string_append(tmpgs, buf);
    - g_string_append(tmpgs, "\n");
    - }
    -
    - tmplvl = ignorance_level_read(tmpgs->str);
    -
    - g_string_free(tmpgs, TRUE);
    - } else
    - tmplvl=ignorance_level_read(buf);
    -
    - if(tmplvl) {
    - purple_debug_info("ignorance", "Adding level %s\n",
    - tmplvl->name->str);
    -
    - ignorance_add_level(tmplvl);
    - }
    - } else if(strstr(buf, "rule") && tmplvl) {
    - if('\0' == buf[4]) {
    - tmpgs = g_string_new("");
    -
    - for( ; buf != strstr(buf, "/rule");
    - buf_get_line(ibuf, &buf, &pnt, size))
    - {
    - g_string_append(tmpgs, buf);
    - g_string_append(tmpgs, "\n");
    - }
    -
    - purple_debug_info("ignorance", "Attempting to read rule %s\n",
    - tmpgs->str);
    -
    - tmprule = ignorance_rule_read(tmpgs->str);
    -
    - g_string_free(tmpgs, TRUE);
    - } else
    - tmprule = ignorance_rule_read(buf);
    - if(tmprule) {
    - purple_debug_info("ignorance", "Adding rule %s: %s\n",
    - tmprule->name->str, (gchar*)(tmprule->value));
    -
    - ignorance_level_add_rule(tmplvl, tmprule);
    - }
    - } else if(tmplvl) {
    - tmpgs = g_string_new(purple_normalize_nocase(NULL, buf));
    -
    - purple_debug_info("ignorance", "Adding denizen %s\n", buf);
    -
    - if(ignorance_get_user_level(tmpgs) == ignorance_get_default_level())
    - ignorance_level_add_denizen(tmplvl, tmpgs);
    -
    - g_string_free(tmpgs,TRUE);
    - }
    - }
    -
    - g_free(ibuf);
    -
    - import_purple_list();
    - import_zinc_list();
    - import_curphoo_list();
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -ignorance_load (PurplePlugin *plugin) {
    - purple_debug_info("ignorance", "Loading ignorance plugin");
    -
    - load_conf();
    -
    - ignorance_signals_connect(plugin);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin) {
    - purple_debug_info("ignorance", "Unloading ignorance plugin\n");
    - save_conf();
    -
    - return TRUE;
    -}
    -
    -static GtkWidget *get_config_frame(PurplePlugin *plugin) {
    - return create_uiinfo(levels);
    -}
    -
    -static PidginPluginUiInfo ui_info = {
    - get_config_frame,
    - 0,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static PurplePluginInfo ig_info = {
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD,
    - PIDGIN_PLUGIN_TYPE,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    - IGNORANCE_PLUGIN_ID,
    - NULL,
    - PP_VERSION,
    - NULL,
    - NULL,
    - "Peter Lawler <bleeter from users.sf.net>, "
    - "Levi Bard <taktaktaktaktaktaktaktaktaktak@gmail.com> (original author)",
    - "http://guifications.sourceforge.net",
    - ignorance_load,
    - plugin_unload,
    - NULL,
    - &ui_info,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -ignorance_init (PurplePlugin * plugin)
    -{
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - ig_info.name = _("Ignorance");
    - ig_info.summary =
    - _("Allows you to manage lists of users with various levels of allowable activity.");
    - ig_info.description =
    - _("Allows you to manage lists of users with various levels of allowable activity.");
    -}
    -
    -PURPLE_INIT_PLUGIN (ignorance, ignorance_init, ig_info)
    --- a/ignorance/ignorance.conf Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,20 +0,0 @@
    -level index="100" name="Default" upper_threshhold="10" lower_threshhold="-10" allow_passthrough="0"
    -rule name="link" type="1" score="1" value="http://" flags="3" enabled="0"
    -rule name="PM" type="2" score="1" value="." flags="2" enabled="1"
    -rule name="playing" type="2" score="1" value="[Pp]laying" flags="3" enabled="0"
    -rule name="enterleave" type="2" score="1" value="." flags="16" enabled="1"
    -rule name="invite" type="2" score="1" value="." flags="32" enabled="1"
    -rule name="profilebot" type="1" score="1" value="profile" flags="4" enabled="1"
    -level index="1" name="WL" upper_threshhold="10" lower_threshhold="-10" allow_passthrough="0"
    -level index="80" name="PM" upper_threshhold="10" lower_threshhold="-10" allow_passthrough="0"
    -rule name="enterleave" type="2" score="1" value="." flags="16" enabled="1"
    -level index="90" name="Link" upper_threshhold="10" lower_threshhold="-10" allow_passthrough="0"
    -rule name="PM" type="2" score="1" value="." flags="2" enabled="1"
    -rule name="enterleave" type="2" score="1" value="." flags="16" enabled="1"
    -rule name="playing" type="2" score="1" value="[Pp]laying" flags="3" enabled="0"
    -rule name="invite" type="2" score="1" value="." flags="32" enabled="1"
    -level index="1001" name="BL" upper_threshhold="10" lower_threshhold="-10" allow_passthrough="0"
    -rule name="everything" type="2" score="1" value="." flags="55" enabled="1"
    -level index="999" name="ZincBL" upper_threshhold="9" lower_threshhold="-9" allow_passthrough="0"
    -rule name="Everything" type="2" score="1" value="." flags="3" enabled="1"
    -level index="2" name="ZincWL" upper_threshhold="9" lower_threshhold="-9" allow_passthrough="0"
    --- a/ignorance/ignorance.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,45 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_H
    -#define IGNORANCE_H
    -
    -#ifdef HAVE_CONFIG_H
    -# include "../pp_config.h"
    -#endif
    -
    -#include "../common/pp_internal.h"
    -
    -#include "ignorance_level.h"
    -
    -#define IGNORANCE_APPLY_CHAT 1
    -#define IGNORANCE_APPLY_IM 2
    -#define IGNORANCE_APPLY_USER 4
    -#define IGNORANCE_APPLY_HOST 8
    -#define IGNORANCE_APPLY_ENTERLEAVE 16
    -#define IGNORANCE_APPLY_INVITE 32
    -
    -ignorance_level* ignorance_get_level_name(const GString *levelname);
    -
    -gboolean ignorance_add_level(ignorance_level *level);
    -
    -gboolean ignorance_remove_level(const GString *levelname);
    -
    -gint ignorance_get_new_level_index();
    -
    -gboolean save_conf();
    -
    -#endif
    --- a/ignorance/ignorance_denizen.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,62 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#include "ignorance_denizen.h"
    -#include "ignorance_internal.h"
    -
    -ignorance_denizen* ignorance_denizen_new(const gchar *newname) {
    - ignorance_denizen *id=(ignorance_denizen*)g_malloc(sizeof(ignorance_denizen));
    -
    - id->name=g_strdup(newname);
    - id->last_message=g_strdup("");
    - id->repeats=0;
    -
    - return id;
    -}
    -
    -void ignorance_denizen_free(ignorance_denizen *id) {
    - g_free(id->name);
    - g_free(id->last_message);
    - g_free(id);
    -}
    -
    -gchar *ignorance_denizen_get_name(ignorance_denizen *id) {
    - return id->name;
    -}
    -
    -gchar *ignorance_denizen_get_last_message(ignorance_denizen *id) {
    - return id->last_message;
    -}
    -
    -gint ignorance_denizen_get_repeats(ignorance_denizen *id) {
    - return id->repeats;
    -}
    -
    -gint ignorance_denizen_set_message(ignorance_denizen *id, const gchar *message) {
    - if(!strcasecmp(id->last_message,message)){
    - purple_debug_info("ignorance","Got repeat %d for message %s\n",
    - id->repeats+1,message);
    - id->repeats++;
    - } else {
    - purple_debug_info("ignorance","New message %s replacing old message %s",
    - message,id->last_message);
    - g_free(id->last_message);
    - id->last_message=g_strdup(message);
    - id->repeats=0;
    - }
    -
    - return id->repeats;
    -}
    --- a/ignorance/ignorance_denizen.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,42 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_DENIZEN_H
    -#define IGNORANCE_DENIZEN_H
    -
    -#include <string.h>
    -
    -#include "ignorance.h"
    -
    -typedef struct ignorance_denizen{
    - gchar *name;
    - gchar *last_message;
    - gint repeats;
    -} ignorance_denizen;
    -
    -ignorance_denizen* ignorance_denizen_new(const gchar *newname);
    -
    -void ignorance_denizen_free(ignorance_denizen *denizen);
    -
    -gchar* ignorance_denizen_get_name(ignorance_denizen *id);
    -
    -gchar* ignorance_denizen_get_last_message(ignorance_denizen *id);
    -
    -gint ignorance_denizen_get_repeats(ignorance_denizen *id);
    -
    -gint ignorance_denizen_set_message(ignorance_denizen *id, const gchar *message);
    -
    -#endif
    --- a/ignorance/ignorance_internal.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,72 +0,0 @@
    -/*
    - * Guifications - The end all, be all, toaster popup plugin
    - * Copyright (C) 2003-2005 Gary Kramlich
    - *
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -#ifndef IG_INTERNAL_H
    -#define IG_INTERNAL_H
    -
    -#include <glib.h>
    -
    -#define IGNORANCE_PLUGIN_ID "gtk-bleeter-ignorance"
    -#define SHORTDESC "Ignorance filter"
    -
    -#ifndef IGNORANCE_CONFDIR
    -# define IGNORANCE_CONFDIR purple_user_dir()
    -#endif
    -
    -#define EXEC_TIMEOUT 10
    -
    -#define GREATER(x,y) ((x)?((x)>(y)):(y))
    -
    -#if ((PURPLE_MAJOR_VERSION) < 2)
    -#define PURPLE_CONV_TYPE_CHAT PURPLE_CONV_CHAT
    -#define PURPLE_CONV_TYPE_IM PURPLE_CONV_IM
    -#endif
    -
    -#if GLIB_CHECK_VERSION(2,6,0)
    -# include <glib/gstdio.h>
    -#endif
    -
    -#ifdef _WIN32
    -# include <win32dep.h>
    -#endif
    -
    -#if !GLIB_CHECK_VERSION(2,6,0)
    -# define g_freopen freopen
    -# define g_fopen fopen
    -# define g_rmdir rmdir
    -# define g_remove remove
    -# define g_unlink unlink
    -# define g_lstat lstat
    -# define g_stat stat
    -# define g_mkdir mkdir
    -# define g_rename rename
    -# define g_open open
    -#endif
    -
    -#ifdef HAVE_ENDIAN_H
    -# include <endian.h>
    -#endif
    -
    -#define MSG_LEN 2048
    -/* The above should normally be the same as BUF_LEN,
    - * but just so we're explicitly asking for the max message
    - * length. */
    -#define BUF_LEN MSG_LEN
    -#define BUF_LONG BUF_LEN * 2
    -
    -#endif
    --- a/ignorance/ignorance_level.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,328 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#include "ignorance.h"
    -#include "ignorance_denizen.h"
    -#include "ignorance_internal.h"
    -#include "ignorance_level.h"
    -#include "ignorance_violation.h"
    -#include "regex.h"
    -
    -#include <string.h>
    -#include <strings.h>
    -
    -void ignorance_level_write_hashitem(gpointer key, gpointer value,
    - gpointer user_data);
    -void ignorance_hash_free(gpointer key, gpointer value,
    - gpointer user_data);
    -void ignorance_level_regex_hashitem(gpointer key, gpointer value,
    - gpointer user_data);
    -
    -gboolean assign_level_token(ignorance_level *lvl,const gchar *tokentxt);
    -gint g_string_compare(gconstpointer a, gconstpointer z);
    -
    -ignorance_level* ignorance_level_new() {
    - ignorance_level *il=(ignorance_level*)g_malloc(sizeof(ignorance_level));
    - il->name=g_string_new("Default");
    - il->denizens_hash=g_hash_table_new(g_str_hash,g_str_equal);
    - il->rules=g_ptr_array_new();
    -
    - return il;
    -}
    -
    -void ignorance_level_free(ignorance_level *il) {
    - g_string_free(il->name,TRUE);
    - g_hash_table_foreach(il->denizens_hash,ignorance_hash_free,NULL);
    - g_hash_table_destroy(il->denizens_hash);
    - g_ptr_array_foreach(il->rules,ignorance_rule_free_g,NULL);
    -}
    -
    -void ignorance_level_free_g(gpointer il,gpointer user_data) {
    - ignorance_level_free((ignorance_level*)il);
    -}
    -
    -gboolean ignorance_level_add_rule(ignorance_level *level,ignorance_rule *rule) {
    - g_ptr_array_add(level->rules,(gpointer)rule);
    -
    - return TRUE;
    -}
    -
    -ignorance_rule* ignorance_level_get_rule(ignorance_level *level,
    - const GString *rulename) {
    -
    - int i=0;
    - ignorance_rule *rule=NULL;
    -
    - for(i=0;i<level->rules->len;++i){
    - rule=g_ptr_array_index(level->rules,i);
    - if(g_string_equal(rulename,rule->name))
    - return g_ptr_array_index(level->rules,i);
    - }
    -
    - return NULL;
    -}
    -
    -gboolean ignorance_level_remove_rule(ignorance_level *level,
    - const GString *rulename) {
    - return g_ptr_array_remove_fast(level->rules,
    - ignorance_level_get_rule(level,rulename));
    -}
    -
    -gboolean ignorance_level_add_denizen(ignorance_level *level,
    - const GString *username) {
    -
    - if(!g_hash_table_lookup(level->denizens_hash,username->str)){
    - ignorance_denizen *id=ignorance_denizen_new(username->str);
    - g_hash_table_insert(level->denizens_hash,
    - ignorance_denizen_get_name(id),id);
    - }
    -
    - return TRUE;
    -}
    -
    -#ifdef HAVE_REGEX_H
    -gboolean ignorance_level_has_denizen_regex(ignorance_level *level,
    - const gchar *regex, GList **denizens) {
    -
    - regex_t reg;
    - gpointer udata[2];
    -
    - udata[0]=denizens;
    -
    - if(regcomp(&reg,regex,REG_EXTENDED | REG_NOSUB)) {
    - purple_debug_error("ignorance", "Error parsing regex %s\n",
    - regex);
    - regfree(&reg);
    - return FALSE;
    - }
    -
    - udata[1]=&reg;
    -
    - g_hash_table_foreach(level->denizens_hash,ignorance_level_regex_hashitem,
    - (gpointer)udata);
    -
    - regfree(&reg);
    -
    - return (denizens!=NULL);
    -}
    -#endif
    -
    -
    -gboolean ignorance_level_has_denizen(ignorance_level *level,
    - const GString *username) {
    - gboolean rv=FALSE;
    -
    - rv = (NULL != g_hash_table_lookup(level->denizens_hash,username->str));
    -
    - return rv;
    -}
    -
    -gint g_string_compare(gconstpointer a, gconstpointer z) {
    - const GString *gsa=(const GString*)a, *gsz=(const GString*)z;
    -
    - return g_ascii_strcasecmp(gsa->str, gsz->str);
    -}
    -
    -
    -gboolean ignorance_level_remove_denizen(ignorance_level *level,
    - const GString *username) {
    -
    - gpointer kptr=NULL, vptr=NULL;
    - gboolean rv=FALSE;
    -
    - rv=g_hash_table_lookup_extended(level->denizens_hash, username->str, &kptr,
    - &vptr);
    -
    - purple_debug_info("ignorance","Remove: found id %x\n",vptr);
    - if(rv){
    - purple_debug_info("ignorance","Removing from hash\n");
    - g_hash_table_remove(level->denizens_hash,username->str);
    -
    - purple_debug_info("ignorance","Freeing denizen\n");
    - ignorance_denizen_free((ignorance_denizen*)vptr);
    - purple_debug_info("ignorance","Done freeing denizen\n");
    - }
    -
    - return rv;
    -}
    -
    -gint ignorance_level_rulecheck(ignorance_level *level,
    - const GString *username, const GString *text,
    - gint flags, GList **violations) {
    - int i=0, totalscore=0, curscore;
    - ignorance_rule *cur;
    - ignorance_denizen *id;
    -
    - purple_debug_info("ignorance","Preparing to lookup %s\n", username->str);
    - id = g_hash_table_lookup(level->denizens_hash, username->str);
    - purple_debug_info("ignorance","Got denizen %x\n",id);
    - if(id){
    - purple_debug_info("ignorance","Making sure text isn't name\n");
    - if(strcasecmp(ignorance_denizen_get_name(id),text->str)){
    - purple_debug_info("ignorance","Setting new message to %s\n", text->str);
    - ignorance_denizen_set_message(id, text->str);
    - }
    - }
    -
    - for(i=0;i<level->rules->len;++i) {
    - cur=(ignorance_rule*)g_ptr_array_index(level->rules,i);
    - if(cur->flags & IGNORANCE_APPLY_USER) {
    - curscore=ignorance_rule_rulecheck(cur,username,flags);
    - totalscore|=curscore;
    - if(curscore){
    - if(curscore & IGNORANCE_FLAG_MESSAGE)
    - (*violations)=g_list_prepend(*violations,
    - ignorance_violation_newp(IGNORANCE_FLAG_MESSAGE,
    - cur->message));
    - if(curscore & IGNORANCE_FLAG_SOUND)
    - (*violations)=g_list_prepend(*violations,
    - ignorance_violation_newp(IGNORANCE_FLAG_SOUND,
    - cur->sound));
    - if(curscore & IGNORANCE_FLAG_EXECUTE)
    - (*violations)=g_list_prepend(*violations,
    - ignorance_violation_newp(IGNORANCE_FLAG_EXECUTE,
    - cur->command));
    - }
    - }
    -
    - curscore=ignorance_rule_rulecheck(cur, text,
    - flags & (~IGNORANCE_APPLY_USER));
    -
    - totalscore|=curscore;
    - if(curscore){
    - if(curscore & IGNORANCE_FLAG_MESSAGE)
    - (*violations)=g_list_prepend(*violations,
    - ignorance_violation_newp(IGNORANCE_FLAG_MESSAGE, cur->message));
    - if(curscore & IGNORANCE_FLAG_SOUND)
    - (*violations)=g_list_prepend(*violations,
    - ignorance_violation_newp(IGNORANCE_FLAG_SOUND, cur->sound));
    - if(curscore & IGNORANCE_FLAG_EXECUTE)
    - (*violations)=g_list_prepend(*violations,
    - ignorance_violation_newp(IGNORANCE_FLAG_EXECUTE, cur->command));
    - }
    - }
    -
    - return totalscore;
    -}
    -
    -
    -ignorance_level* ignorance_level_read_old(const gchar *lvltext) {
    - gchar *tokptr=strchr((gchar*)lvltext,' '), **tokens=NULL;
    - ignorance_level *lvl=ignorance_level_new();
    - int i=0;
    -
    - if(!tokptr){
    - ignorance_level_free(lvl);
    - return NULL;
    - }
    -
    - tokens=g_strsplit(lvltext," ",INT_MAX);
    -
    - for(i=0;tokens[i];++i)
    - assign_level_token(lvl,tokens[i]);
    -
    - g_strfreev(tokens);
    -
    - return lvl;
    -}
    -
    -ignorance_level* ignorance_level_read(const gchar *lvltext) {
    - gchar *tokptr=strchr((gchar*)lvltext,'\n'), **tokens=NULL;
    - ignorance_level *lvl=ignorance_level_new();
    - int i=0;
    -
    - if(!tokptr){
    - ignorance_level_free(lvl);
    - return ignorance_level_read_old(lvltext);
    - }
    - tokens=g_strsplit(lvltext,"\n",INT_MAX);
    - for(i=0;tokens[i];++i)
    - assign_level_token(lvl,tokens[i]);
    -
    - g_strfreev(tokens);
    -
    - return lvl;
    -}
    -
    -
    -/* Parses out a token of the form tokenname="value" and assigns it to a rulename
    - *
    - * rule is the rule to be updated
    - * tokentxt is the token string
    - * true returned if token is valid and successfully added to the rule
    - *
    - * level name1="value1" name2="value2" ...
    - */
    -gboolean assign_level_token(ignorance_level *lvl,const gchar *tokentxt) {
    - gchar *name=NULL, *value=NULL;
    - gboolean rv=TRUE;
    - gint cursor=0;
    -
    - value=strchr(tokentxt,'=');
    - if(value) {
    - (*value)='\0';
    - ++value;
    -
    - if('"'==(*value)) {
    - ++value;
    - cursor=strlen(value)-1;
    - if('"'==value[cursor])
    - value[cursor]='\0';
    - }
    - name=(gchar*)tokentxt;
    -
    - if(!strncasecmp(name,"name",BUFSIZ))
    - g_string_assign(lvl->name,value);
    - else
    - rv=FALSE;
    - } else
    - rv=FALSE;
    -
    - return rv;
    -}
    -
    -gboolean ignorance_level_write(ignorance_level *level,FILE *f) {
    - gint i;
    -
    - fprintf(f,"level\nname=\"%s\"\n/level\n", level->name->str);
    -
    - for(i=0;i<level->rules->len;++i)
    - ignorance_rule_write(g_ptr_array_index(level->rules,i),f);
    -
    - g_hash_table_foreach(level->denizens_hash,ignorance_level_write_hashitem,f);
    -
    - return TRUE;
    -}
    -
    -void ignorance_level_regex_hashitem(gpointer key, gpointer value,
    - gpointer user_data) {
    - gpointer *udata=(gpointer*)user_data;
    - GList **denizens=(GList**)udata[0];
    -
    - if(!(regexec((regex_t*)udata[1],(gchar*)key,1,NULL,0)))
    - (*denizens)=g_list_prepend(*denizens,g_string_new((gchar*)key));
    -}
    -
    -void ignorance_hash_free(gpointer key, gpointer value, gpointer user_data) {
    - ignorance_denizen_free((ignorance_denizen*)value);
    -}
    -
    -void ignorance_level_write_hashitem(gpointer key, gpointer value,
    - gpointer user_data){
    -
    - fprintf((FILE*)user_data,"%s\n",(gchar*)key);
    -
    -}
    --- a/ignorance/ignorance_level.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,99 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_LEVEL_H
    -#define IGNORANCE_LEVEL_H
    -
    -#include "ignorance_rule.h"
    -
    -/*
    - * describes an ignorance level
    - *
    - * index is an index for keeping track
    - * of levels' relative positions
    - * the idea is for 0 to be the "default" level,
    - * >0 to be "better" levels, and <0 to be "worse"
    - *
    - * allow_passthrough flags whether a user
    - * is allowed to be "passed through" this level
    - * to the next consecutive level, as in:
    - * if someone on my friends list does something
    - * that flags 12 of my rules, do I send them
    - * straight to /dev/null, or just
    - * bump them down a level?
    - *
    - * name - user's name for the level
    - *
    - * denizens - list of users in this level
    - * I may get rid of this in favor of a more global
    - * userlist
    - *
    - * rules - list of rules for this level
    - */
    -typedef struct ignorance_level{
    - GString *name;
    - /*GList *denizens;*/
    - GHashTable *denizens_hash;
    - GPtrArray *rules;
    -} ignorance_level;
    -
    -ignorance_level* ignorance_level_new();
    -void ignorance_level_free(ignorance_level *il);
    -void ignorance_level_free_g(gpointer il,gpointer user_data);
    -
    -gboolean ignorance_level_add_rule(ignorance_level *level,ignorance_rule *rule);
    -
    -ignorance_rule* ignorance_level_get_rule(ignorance_level *level,
    - const GString *rulename);
    -
    -gboolean ignorance_level_remove_rule(ignorance_level *level,
    - const GString *rulename);
    -
    -gboolean ignorance_level_add_denizen(ignorance_level *level,
    - const GString *username);
    -
    -gboolean ignorance_level_add_denizen_fast(ignorance_level *level,
    - const GString *username);
    -
    -gboolean ignorance_level_has_denizen(ignorance_level *level,
    - const GString *username);
    -
    -#ifdef HAVE_REGEX_H
    -gboolean ignorance_level_has_denizen_regex(ignorance_level *level,
    - const gchar *regex, GList **denizens);
    -#endif
    -
    -gboolean ignorance_level_remove_denizen(ignorance_level *level,
    - const GString *username);
    -
    -/*
    - * Determines whether a string violates one of
    - * the rules defined in an ignorance level
    - *
    - * il is the ignorance level
    - * text is the possibly offending string
    - * flags are the rule flags to match
    - * returns score
    - */
    -gint ignorance_level_rulecheck(ignorance_level *il,const GString *username,
    - const GString *text, gint flags,
    - GList **violations);
    -
    -ignorance_level* ignorance_level_read(const gchar *lvltext);
    -
    -gboolean ignorance_level_write(ignorance_level *level,FILE *f);
    -
    -#endif
    --- a/ignorance/ignorance_rule.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,243 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#include "ignorance_rule.h"
    -#include <string.h>
    -#include <strings.h>
    -#include "ignorance_internal.h"
    -
    -
    -gboolean assign_rule_token(ignorance_rule *rule, const char *tokentxt);
    -
    -ignorance_rule* ignorance_rule_new() {
    - ignorance_rule *ir=(ignorance_rule*)g_malloc(sizeof(ignorance_rule));
    -
    - ir->name=g_string_new("");
    - ir->type=IGNORANCE_RULE_SIMPLETEXT;
    - ir->score=0;
    - ir->flags=0;
    - ir->enabled=TRUE;
    - ir->value=NULL;
    - ir->message=NULL;
    - ir->sound=NULL;
    - ir->command=NULL;
    -
    - return ir;
    -}
    -
    -ignorance_rule* ignorance_rule_newp(const GString *name, gint type,
    - const gchar *value, gint score, gint flags,
    - gboolean enabled, const gchar *message,
    - const gchar *sound, const gchar *command) {
    - ignorance_rule *ir=(ignorance_rule*)g_malloc(sizeof(ignorance_rule));
    -
    - ir->name=g_string_new(name->str);
    - if(ignorance_rule_has_type(type))
    - ir->type=type;
    - else
    - ir->type=IGNORANCE_RULE_INVALID;
    - ir->value=g_strdup(value);
    - ir->score=score;
    - ir->flags=flags;
    - ir->enabled=enabled;
    - ir->message=g_strdup(message);
    - ir->sound=g_strdup(sound);
    - ir->command=g_strdup(command);
    -
    - return ir;
    -}
    -
    -void ignorance_rule_free(ignorance_rule *ir) {
    - g_string_free(ir->name,TRUE);
    - g_free(ir->value);
    - g_free(ir->message);
    - g_free(ir->sound);
    - g_free(ir->command);
    - g_free(ir);
    -}
    -
    -void ignorance_rule_free_g(gpointer ir,gpointer user_data) {
    - ignorance_rule_free((ignorance_rule*)ir);
    -}
    -
    -gboolean ignorance_rule_has_type(gint type) {
    - if((type>=IGNORANCE_RULE_MINVALID) || (type<=IGNORANCE_RULE_INVALID))
    - return FALSE;
    - return TRUE;
    -}
    -
    -
    -gint ignorance_rule_rulecheck(ignorance_rule *rule, const GString *text,
    - gint flags) {
    - if((flags & rule->flags) && rule->enabled){
    - switch(rule->type){
    - case IGNORANCE_RULE_SIMPLETEXT:
    - return simple_text_rulecheck(rule,text);
    -#ifdef HAVE_REGEX_H
    - case IGNORANCE_RULE_REGEX:
    - return regex_rulecheck(rule,text);
    -#endif
    - default:
    - return 0;
    - }
    - }
    -
    - return 0;
    -}
    -
    -gint simple_text_rulecheck(ignorance_rule *rule,const GString *text) {
    - const gchar *rulevalue=(const gchar*)(rule->value);
    -
    - if(NULL!=g_strstr_len(text->str,text->len,rulevalue))
    - return rule->score;
    -
    - return 0;
    -}
    -
    -#ifdef HAVE_REGEX_H
    -gint regex_rulecheck(ignorance_rule *rule, const GString *text) {
    - regex_t reg;
    - gint rv=0;
    -
    - if(regcomp(&reg,(const gchar*)rule->value,REG_EXTENDED | REG_NOSUB))
    - purple_debug_error("ignorance", "Error parsing regex %s\n",
    - (const gchar*)(rule->value));
    - else if(!regexec(&reg,text->str,1,NULL,0))
    - rv=rule->score;
    -
    - regfree(&reg);
    - return rv;
    -}
    -#endif
    -
    -gint repeat_rulecheck(ignorance_rule *rule, gint repeats) {
    - gint allowed_repeats = atoi((gchar*)(rule->value));
    - gint score=0;
    -
    - if(repeats >= allowed_repeats)
    - score=rule->score;
    -
    - return score;
    -}
    -
    -ignorance_rule* ignorance_rule_read_old(const gchar *ruletext) {
    - gchar *tokptr=strchr((gchar*)ruletext,' '), **tokens=NULL;
    - ignorance_rule *rule=ignorance_rule_new();
    - int i=0;
    -
    - if(!tokptr){
    - ignorance_rule_free(rule);
    - return NULL;
    - }
    -
    - tokens=g_strsplit(ruletext," ",INT_MAX);
    -
    - for(i=0;tokens[i];++i)
    - assign_rule_token(rule,tokens[i]);
    -
    - if(rule->score > 9 || rule->score < -9)
    - rule->score=IGNORANCE_FLAG_IGNORE;
    - else
    - rule->score=IGNORANCE_FLAG_FILTER;
    -
    - g_strfreev(tokens);
    -
    - return rule;
    -}
    -
    -ignorance_rule* ignorance_rule_read(const gchar *ruletext) {
    - gchar*tokptr=strchr((gchar*)ruletext,'\n'), **tokens;
    - ignorance_rule *rule=ignorance_rule_new();
    - int i=0;
    -
    - if(!tokptr){
    - ignorance_rule_free(rule);
    - return ignorance_rule_read_old(ruletext);
    - }
    -
    - tokens=g_strsplit(ruletext,"\n",INT_MAX);
    -
    - for(i=0;tokens[i];++i)
    - assign_rule_token(rule,tokens[i]);
    -
    - g_strfreev(tokens);
    -
    - return rule;
    -}
    -
    -/*
    - * Parses out a token of the form tokenname="value" and assigns it to a rulename
    - *
    - * rule is the rule to be updated
    - * tokentxt is the token string
    - * true returned if token is valid and successfully added to the rule
    - *
    - * level name1="value1" name2="value2" ...
    - */
    -gboolean assign_rule_token(ignorance_rule *rule,const gchar *tokentxt) {
    - gchar *name=(gchar*)tokentxt, *value=NULL;
    - gboolean rv=TRUE;
    - gint cursor=0;
    -
    - value=strchr(tokentxt,'=');
    - if(value) {
    - (*value)='\0';
    - ++value;
    -
    - if('"'==(*value)){
    - ++value;
    - cursor=strlen(value)-1;
    - if('"'==value[cursor])
    - value[cursor]='\0';
    - }
    -
    - if(!strncasecmp(name,"name",BUFSIZ))
    - g_string_assign(rule->name,value);
    - else if(!strncasecmp(name,"type",BUFSIZ))
    - rule->type=atoi(value);
    - else if(!strncasecmp(name,"value",BUFSIZ)) {
    - rule->value=(gchar*)g_malloc((strlen(value)+1)*sizeof(gchar));
    - strncpy(rule->value,value,strlen(value)+1);
    - }else if(!strncasecmp(name,"score",BUFSIZ))
    - rule->score=atoi(value);
    - else if(!strncasecmp(name,"flags",BUFSIZ))
    - rule->flags=atoi(value);
    - else if(!strncasecmp(name,"enabled",BUFSIZ))
    - rule->enabled=(gboolean)atoi(value);
    - else if(!strncasecmp(name,"message",BUFSIZ)) {
    - rule->message=(gchar*)g_malloc((strlen(value)+1)*sizeof(gchar));
    - strncpy(rule->message,value,strlen(value)+1);
    - } else if(!strncasecmp(name,"command",BUFSIZ)) {
    - rule->command=(gchar*)g_malloc((strlen(value)+1)*sizeof(gchar));
    - strncpy(rule->command,value,strlen(value)+1);
    - } else if(!strncasecmp(name,"sound",BUFSIZ)) {
    - rule->sound=(gchar*)g_malloc((strlen(value)+1)*sizeof(gchar));
    - strncpy(rule->sound,value,strlen(value)+1);
    - } else
    - rv=FALSE;
    - } else
    - rv=FALSE;
    -
    - return rv;
    -}
    -
    -gboolean ignorance_rule_write(ignorance_rule *rule,FILE *f){
    - fprintf(f,"rule\nname=\"%s\"\ntype=\"%d\"\nscore=\"%d\"\nvalue=\"%s\"\nflags=\"%d\"\nenabled=\"%d\"\nmessage=\"%s\"\ncommand=\"%s\"\nsound=\"%s\"\n/rule\n",
    - rule->name->str, rule->type, rule->score, (gchar*)(rule->value),
    - rule->flags, rule->enabled, rule->message, rule->command, rule->sound);
    -
    - return TRUE;
    -}
    --- a/ignorance/ignorance_rule.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,117 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_RULE_H
    -#define IGNORANCE_RULE_H
    -
    -#ifdef HAVE_CONFIG_H
    -# include "../pp_config.h"
    -#endif
    -
    -#include <stdlib.h>
    -#include <sys/types.h>
    -#include <glib.h>
    -#include <glib/gprintf.h>
    -
    -#include "debug.h"
    -
    -#define IGNORANCE_RULE_INVALID 0
    -#define IGNORANCE_RULE_MINVALID INT_MAX
    -
    -#define IGNORANCE_RULE_SIMPLETEXT 1
    -#define IGNORANCE_RULE_SIMPLETEXT_NUMTOKENS 6
    -
    -#ifdef HAVE_REGEX_H
    -#include <regex.h>
    -#define IGNORANCE_RULE_REGEX 2
    -#define IGNORANCE_RULE_REGEX_NUMTOKENS 6
    -#endif
    -
    -#define IGNORANCE_RULE_REPEAT 4
    -#define IGNORANCE_RULE_REPEAT_NUMTOKENS 6
    -
    -#define IGNORANCE_FLAG_FILTER 1
    -#define IGNORANCE_FLAG_IGNORE 2
    -#define IGNORANCE_FLAG_MESSAGE 4
    -#define IGNORANCE_FLAG_EXECUTE 8
    -#define IGNORANCE_FLAG_SOUND 16
    -
    -/*
    - * describes an ignorance rule
    - *
    - * name - user's name for the rule
    - * type - one of the ruletypes defined within this struct
    - *
    - * value - the actual value of the rule
    - * could be a string/regex, integer, other
    - * depending on the rule type
    - *
    - * score - an arbitrary (user-assigned) number
    - * that determines the severity of the rule
    - */
    -typedef struct ignorance_rule {
    - GString *name;
    - gint type;
    - gchar *value;
    - gint score;
    - gint flags;
    - gboolean enabled;
    - gchar *message,
    - *command,
    - *sound;
    -} ignorance_rule;
    -
    -ignorance_rule* ignorance_rule_new();
    -
    -ignorance_rule* ignorance_rule_newp(const GString *name, gint type,
    - const gchar *value, gint score, gint flags,
    - gboolean enabled, const gchar *message,
    - const gchar *sound, const gchar *command);
    -void ignorance_rule_free(ignorance_rule *ir);
    -void ignorance_rule_free_g(gpointer ir,gpointer user_data);
    -
    -gboolean ignorance_rule_has_type(gint type);
    -
    -/*
    - * Determines whether a string violates a rule
    - *
    - * rule is the rule
    - * text is the possibly offending string
    - * returns 0/1 boolean
    - */
    -gint ignorance_rule_rulecheck(ignorance_rule *rule, const GString *text,
    - gint flags);
    -
    -gint simple_text_rulecheck(ignorance_rule *rule, const GString *text);
    -
    -#ifdef HAVE_REGEX_H
    -gint regex_rulecheck(ignorance_rule *rule, const GString *text);
    -#endif
    -
    -gint repeat_rulecheck(ignorance_rule *rule, gint repeats);
    -
    -ignorance_rule* ignorance_rule_read(const gchar *ruletext);
    -
    -/*
    - * Writes out an ignorance rule to a file
    - *
    - * rule is the rule to write
    - * f is the file which will be written
    - * success/failure returned
    - */
    -gboolean ignorance_rule_write(ignorance_rule *rule, FILE *f);
    -
    -#endif
    --- a/ignorance/ignorance_violation.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,45 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#include "ignorance_violation.h"
    -#include "ignorance_internal.h"
    -
    -ignorance_violation* ignorance_violation_new() {
    - return ignorance_violation_newp(IGNORANCE_FLAG_MESSAGE,"");
    -}
    -
    -ignorance_violation* ignorance_violation_newp(gint newtype,
    - const gchar *newvalue){
    - ignorance_violation *iv=(ignorance_violation*)g_malloc(sizeof(ignorance_violation));
    -
    - if(iv){
    - iv->type=newtype;
    - iv->value=g_strdup(newvalue);
    - }
    -
    - return iv;
    -}
    -
    -void ignorance_violation_free(ignorance_violation *iv) {
    - if(iv){
    - g_free(iv->value);
    - g_free(iv);
    - }
    -}
    -
    -void ignorance_violation_free_g(gpointer iv, gpointer user_data) {
    - ignorance_violation_free((ignorance_violation*)iv);
    -}
    --- a/ignorance/ignorance_violation.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,32 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_VIOLATION_H
    -#define IGNORANCE_VIOLATION_H
    -
    -#include "ignorance.h"
    -
    -typedef struct ignorance_violation {
    - gint type;
    - gchar *value;
    -} ignorance_violation;
    -
    -ignorance_violation* ignorance_violation_new();
    -ignorance_violation* ignorance_violation_newp(gint newtype, const gchar *newvalue);
    -void ignorance_violation_free(ignorance_violation *iv);
    -void ignorance_violation_free_g(gpointer iv, gpointer user_data);
    -
    -#endif
    --- a/ignorance/interface.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,284 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#include <sys/types.h>
    -#include <sys/stat.h>
    -#include <unistd.h>
    -#include <string.h>
    -#include <stdio.h>
    -
    -#include <gdk/gdkkeysyms.h>
    -#include <gtk/gtk.h>
    -
    -#include <gtkutils.h>
    -
    -#include "callbacks.h"
    -#include "interface.h"
    -#include "support.h"
    -#include "ignorance.h"
    -#include "ignorance_internal.h"
    -
    -GtkWidget* create_uiinfo (GPtrArray *levels) {
    - GtkWidget *frame, *table, *hbox, *label;
    - GtkWidget *scrolledwindow, *levelView, *button;
    -
    - rule_selected=TRUE;
    - vbox1=gtk_vbox_new(FALSE, 0);
    - gtk_widget_show(vbox1);
    -
    - vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
    - gtk_widget_show(vbox2);
    - gtk_box_pack_start(GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0);
    -
    - scrolledwindow=gtk_scrolled_window_new (NULL, NULL);
    - gtk_widget_show(scrolledwindow);
    - gtk_box_pack_start(GTK_BOX (vbox2), scrolledwindow, TRUE, TRUE, 0);
    - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolledwindow),
    - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolledwindow),
    - GTK_SHADOW_IN);
    -
    - store=gtk_tree_store_new(NUM_COLUMNS,G_TYPE_STRING,G_TYPE_STRING);
    -
    - levelView=gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
    -
    - renderer=gtk_cell_renderer_text_new ();
    - column=gtk_tree_view_column_new_with_attributes ("Levels", renderer,
    - "text", LEVEL_COLUMN, NULL);
    - gtk_tree_view_append_column(GTK_TREE_VIEW (levelView), column);
    - column=gtk_tree_view_column_new_with_attributes("Rules", renderer, "text",
    - RULE_COLUMN,NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW (levelView), column);
    -
    - load_form_with_levels(GTK_TREE_VIEW(levelView), levels);
    -
    - gtk_widget_show(levelView);
    - gtk_container_add(GTK_CONTAINER(scrolledwindow), levelView);
    -
    - sel=gtk_tree_view_get_selection(GTK_TREE_VIEW(levelView));
    - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
    -
    - hbox = gtk_hbox_new (FALSE, 0);
    - gtk_widget_show (hbox);
    - gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
    -
    - button=pidgin_pixbuf_button_from_stock(_("Create new rule"),
    - GTK_STOCK_ADD, PIDGIN_BUTTON_HORIZONTAL);
    - gtk_widget_show (button);
    - gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
    - g_signal_connect((gpointer) button, "clicked",
    - G_CALLBACK (on_levelAdd_clicked), levelView);
    -
    - button=pidgin_pixbuf_button_from_stock(_("Create new group"),
    - GTK_STOCK_ADD, PIDGIN_BUTTON_HORIZONTAL);
    - gtk_widget_show (button);
    - gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
    - g_signal_connect ((gpointer) button, "clicked",
    - G_CALLBACK (on_groupAdd_clicked), levelView);
    -
    - button=pidgin_pixbuf_button_from_stock(_("Save changes"), GTK_STOCK_YES,
    - PIDGIN_BUTTON_HORIZONTAL);
    - gtk_widget_show (button);
    - gtk_box_pack_start(GTK_BOX (hbox), button, TRUE, TRUE, 0);
    - g_signal_connect((gpointer) button, "clicked",
    - G_CALLBACK (on_levelEdit_clicked), levelView);
    -
    -/* XXX: The stock-icon for levelDel doesn't show, because the text is
    - * set from callback.c. Can we do with just `Remove' for the text
    - * and not updating as the selection in the tree changes?
    - */
    - levelDel=pidgin_pixbuf_button_from_stock(_("Remove rule"), GTK_STOCK_REMOVE,
    - PIDGIN_BUTTON_HORIZONTAL);
    - gtk_widget_show (levelDel);
    - gtk_box_pack_start (GTK_BOX (hbox), levelDel, TRUE, TRUE, 0);
    - g_signal_connect((gpointer) levelDel, "clicked",
    - G_CALLBACK (on_levelDel_clicked), levelView);
    -
    - table=gtk_table_new(3, 2, FALSE);
    - gtk_container_set_border_width(GTK_CONTAINER(table), PIDGIN_HIG_BOX_SPACE);
    - gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
    - gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
    - gtk_widget_show (table);
    - gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, TRUE, 0);
    -
    - label=gtk_label_new_with_mnemonic(_("Name: "));
    - gtk_widget_show(label);
    - gtk_table_attach(GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
    -
    - rulename=gtk_entry_new ();
    - gtk_widget_show(rulename);
    - gtk_table_attach_defaults(GTK_TABLE (table), rulename, 1, 2, 0, 1);
    -
    - label=gtk_label_new_with_mnemonic(_("Filter: "));
    - gtk_widget_show(label);
    - gtk_table_attach(GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
    -
    - filtervalue=gtk_entry_new ();
    - gtk_widget_show(filtervalue);
    - gtk_table_attach_defaults(GTK_TABLE (table), filtervalue, 1, 2, 1, 2);
    -
    - hbox=gtk_hbox_new (FALSE, 0);
    - gtk_widget_show(hbox);
    - gtk_table_attach_defaults(GTK_TABLE (table), hbox, 0, 2, 2, 3);
    -
    - enabled_cb=gtk_check_button_new_with_mnemonic(_("Enabled"));
    - gtk_widget_show(enabled_cb);
    - gtk_box_pack_start(GTK_BOX(hbox),enabled_cb,FALSE,FALSE,0);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enabled_cb),TRUE);
    -
    -#ifdef HAVE_REGEX_H
    - regex_cb=gtk_check_button_new_with_mnemonic (_("Regular Expression"));
    - gtk_widget_show(regex_cb);
    - gtk_box_pack_start (GTK_BOX (hbox), regex_cb, FALSE, FALSE, 0);
    -#endif
    -
    - repeat_cb=gtk_check_button_new_with_mnemonic (_("Repeat"));
    - gtk_widget_set_sensitive(repeat_cb,FALSE);
    - gtk_widget_show(repeat_cb);
    - gtk_box_pack_start(GTK_BOX (hbox), repeat_cb, FALSE, FALSE, 0);
    - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (repeat_cb), FALSE);
    -
    - frame=gtk_frame_new (NULL);
    - gtk_widget_show(frame);
    - gtk_box_pack_start(GTK_BOX (vbox2), frame, FALSE, TRUE, 0);
    -
    - table=gtk_table_new(4, 2, FALSE);
    - gtk_container_set_border_width(GTK_CONTAINER(table), PIDGIN_HIG_BOX_SPACE);
    - gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
    - gtk_widget_show(table);
    - gtk_container_add(GTK_CONTAINER(frame), table);
    -
    - hbox=gtk_hbox_new(FALSE, 0);
    - gtk_widget_show(hbox);
    - gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 2, 0, 1);
    -
    - filter_cb=gtk_check_button_new_with_label(_("Filter"));
    - gtk_widget_show(filter_cb);
    - gtk_box_pack_start(GTK_BOX (hbox), filter_cb, FALSE, FALSE, 0);
    -
    - ignore_cb=gtk_check_button_new_with_label(_("Ignore"));
    - gtk_widget_show(ignore_cb);
    - gtk_box_pack_start(GTK_BOX (hbox), ignore_cb, FALSE, FALSE, 0);
    -
    - message_cb = gtk_check_button_new_with_label(_("Send Message"));
    - gtk_widget_show(message_cb);
    - gtk_table_attach(GTK_TABLE(table), message_cb, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
    -
    - message_entry=gtk_entry_new();
    - gtk_widget_set_sensitive(message_entry,FALSE);
    - gtk_widget_show(message_entry);
    - gtk_table_attach_defaults(GTK_TABLE(table), message_entry, 1, 2, 1, 2);
    -
    - sound_cb = gtk_check_button_new_with_label(_("Play sound"));
    - gtk_widget_show(sound_cb);
    - gtk_table_attach(GTK_TABLE(table), sound_cb, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
    -
    - hbox=gtk_hbox_new (FALSE, 0);
    - gtk_widget_show (hbox);
    - gtk_table_attach_defaults(GTK_TABLE(table), hbox, 1, 2, 2, 3);
    -
    - sound_entry=gtk_entry_new();
    - gtk_widget_set_sensitive(sound_entry,FALSE);
    - gtk_widget_show(sound_entry);
    - gtk_box_pack_start(GTK_BOX(hbox),sound_entry,FALSE,FALSE,0);
    -
    - sound_browse=gtk_button_new_with_label(_("Browse"));
    - gtk_widget_set_sensitive(sound_browse,FALSE);
    - gtk_widget_show(sound_browse);
    - gtk_box_pack_start(GTK_BOX(hbox),sound_browse,FALSE,FALSE,0);
    -
    - execute_cb=gtk_check_button_new_with_label(_("Execute command"));
    - gtk_widget_show(execute_cb);
    - gtk_table_attach(GTK_TABLE(table), execute_cb, 0, 1, 3, 4, GTK_FILL, 0, 0, 0);
    -
    - execute_entry=gtk_entry_new();
    - gtk_widget_set_sensitive(execute_entry,FALSE);
    - gtk_widget_show(execute_entry);
    - gtk_table_attach_defaults(GTK_TABLE(table), execute_entry, 1, 2, 3, 4);
    -
    - label=gtk_label_new(_("Take action"));
    - gtk_widget_show(label);
    - gtk_frame_set_label_widget(GTK_FRAME (frame), label);
    -
    - frame=gtk_frame_new (NULL);
    - gtk_widget_show(frame);
    - gtk_box_pack_start(GTK_BOX (vbox2), frame, FALSE, TRUE, 0);
    -
    - table=gtk_table_new (3, 2, FALSE);
    - gtk_widget_show(table);
    - gtk_container_set_border_width(GTK_CONTAINER(table), PIDGIN_HIG_BOX_SPACE);
    - gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
    - gtk_container_add(GTK_CONTAINER (frame), table);
    -
    - im_type_cb=gtk_check_button_new_with_mnemonic(_("IM Text"));
    - gtk_widget_show(im_type_cb);
    - gtk_table_attach(GTK_TABLE (table), im_type_cb, 0, 1, 0, 1,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
    -
    - chat_type_cb=gtk_check_button_new_with_mnemonic(_("Chat Text"));
    - gtk_widget_show(chat_type_cb);
    - gtk_table_attach(GTK_TABLE (table), chat_type_cb, 1, 2, 0, 1,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (chat_type_cb), TRUE);
    -
    - username_type_cb=gtk_check_button_new_with_mnemonic(_("User names"));
    - gtk_widget_show(username_type_cb);
    - gtk_table_attach(GTK_TABLE (table), username_type_cb, 0, 1, 1, 2,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
    -
    - enterleave_type_cb=gtk_check_button_new_with_mnemonic (_("Enter/Leave"));
    - gtk_widget_show(enterleave_type_cb);
    - gtk_table_attach(GTK_TABLE (table), enterleave_type_cb, 1, 2, 1, 2,
    - (GtkAttachOptions) (GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - invite_type_cb=gtk_check_button_new_with_mnemonic (_("Invitations"));
    - gtk_widget_show(invite_type_cb);
    - gtk_table_attach(GTK_TABLE (table), invite_type_cb, 0, 1, 2, 3,
    - (GtkAttachOptions) (GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - label=gtk_label_new (_("Filter"));
    - gtk_widget_show(label);
    - gtk_frame_set_label_widget(GTK_FRAME (frame), label);
    -
    - g_signal_connect(GTK_WIDGET(vbox1), "destroy", G_CALLBACK(save_conf),NULL);
    - g_signal_connect((gpointer)sel, "changed",
    - G_CALLBACK (on_levelView_row_activated), NULL);
    -
    - g_signal_connect((gpointer) filter_cb, "toggled",
    - G_CALLBACK (on_filter_cb_toggled), NULL);
    -
    - g_signal_connect((gpointer) ignore_cb, "toggled",
    - G_CALLBACK (on_ignore_cb_toggled), NULL);
    -
    - g_signal_connect((gpointer) message_cb, "toggled",
    - G_CALLBACK (on_message_cb_toggled), NULL);
    -
    - g_signal_connect((gpointer) sound_cb, "toggled",
    - G_CALLBACK (on_sound_cb_toggled), NULL);
    -
    - g_signal_connect((gpointer) execute_cb, "toggled",
    - G_CALLBACK (on_execute_cb_toggled), NULL);
    -
    - g_signal_connect((gpointer) sound_browse, "clicked",
    - G_CALLBACK (on_sound_browse_clicked), NULL);
    -
    - return vbox1;
    -}
    --- a/ignorance/interface.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,48 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_INTERFACE_H
    -#define IGNORANCE_INTERFACE_H
    -
    -GtkWidget *vbox1;
    -GtkWidget *vbox2;
    -GtkWidget *levelDel;
    -GtkWidget *rulename;
    -GtkWidget *filtervalue;
    -GtkWidget *repeat_cb;
    -GtkWidget *regex_cb;
    -GtkWidget *im_type_cb;
    -GtkWidget *chat_type_cb;
    -GtkWidget *username_type_cb;
    -GtkWidget *enterleave_type_cb;
    -GtkWidget *invite_type_cb;
    -GtkWidget *enabled_cb;
    -GtkWidget *filter_cb;
    -GtkWidget *ignore_cb;
    -GtkWidget *message_cb, *message_entry;
    -GtkWidget *sound_cb, *sound_entry, *sound_browse;
    -
    -GtkWidget *execute_cb, *execute_entry, *execute_browse;
    -
    -GtkCellRenderer *renderer;
    -GtkTreeViewColumn *column;
    -GtkTreeStore *store;
    -GtkTreeSelection *sel;
    -gboolean rule_selected;
    -
    -GtkWidget* create_uiinfo(GPtrArray *levels);
    -
    -#endif
    --- a/ignorance/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,26 +0,0 @@
    -if TYPES.contains('incomplete') and PIDGIN.found()
    - ignorance_confdir = join_paths(
    - get_option('prefix'),
    - get_option('sysconfdir'),
    - 'pidgin')
    -
    - install_data('ignorance.conf', install_dir : ignorance_confdir)
    -
    - ignorance = shared_module('ignorance',
    - 'ignorance.c',
    - 'ignorance_level.c',
    - 'ignorance_rule.c',
    - 'ignorance_violation.c',
    - 'ignorance_denizen.c',
    - 'callbacks.c',
    - 'interface.c',
    - 'support.c',
    - c_args : [
    - '-DIGNORANCE_CONFDIR="@0@"'.format(ignorance_confdir),
    - ],
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'ignorance'
    -endif
    --- a/ignorance/regex.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,4952 +0,0 @@
    -/* Extended regular expression matching and search library,
    - version 0.12.
    - (Implements POSIX draft P10003.2/D11.2, except for
    - internationalization features.)
    -
    - Copyright (C) 1993 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
    -
    -/* AIX requires this to be the first thing in the file. */
    -#if defined (_AIX) && !defined (REGEX_MALLOC)
    - #pragma alloca
    -#endif
    -
    -#define _GNU_SOURCE
    -
    -/* We need this for `regex.h', and perhaps for the Emacs include files. */
    -#include <sys/types.h>
    -
    -#ifdef HAVE_CONFIG_H
    -#include "../ig_config.h"
    -#endif
    -
    -/* The `emacs' switch turns on certain matching commands
    - that make sense only in Emacs. */
    -#ifdef emacs
    -
    -#include "lisp.h"
    -#include "buffer.h"
    -#include "syntax.h"
    -
    -/* Emacs uses `NULL' as a predicate. */
    -#undef NULL
    -
    -#else /* not emacs */
    -
    -/* We used to test for `BSTRING' here, but only GCC and Emacs define
    - `BSTRING', as far as I know, and neither of them use this code. */
    -#if HAVE_STRING_H || STDC_HEADERS
    -#include <string.h>
    -#ifndef bcmp
    -#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
    -#endif
    -#ifndef bcopy
    -#define bcopy(s, d, n) memcpy ((d), (s), (n))
    -#endif
    -#ifndef bzero
    -#define bzero(s, n) memset ((s), 0, (n))
    -#endif
    -#else
    -#include <strings.h>
    -#endif
    -
    -#ifdef STDC_HEADERS
    -#include <stdlib.h>
    -#else
    -char *malloc ();
    -char *realloc ();
    -#endif
    -
    -
    -/* Define the syntax stuff for \<, \>, etc. */
    -
    -/* This must be nonzero for the wordchar and notwordchar pattern
    - commands in re_match_2. */
    -#ifndef Sword
    -#define Sword 1
    -#endif
    -
    -#ifdef SYNTAX_TABLE
    -
    -extern char *re_syntax_table;
    -
    -#else /* not SYNTAX_TABLE */
    -
    -/* How many characters in the character set. */
    -#define CHAR_SET_SIZE 256
    -
    -static char re_syntax_table[CHAR_SET_SIZE];
    -
    -static void
    -init_syntax_once ()
    -{
    - register int c;
    - static int done = 0;
    -
    - if (done)
    - return;
    -
    - bzero (re_syntax_table, sizeof re_syntax_table);
    -
    - for (c = 'a'; c <= 'z'; c++)
    - re_syntax_table[c] = Sword;
    -
    - for (c = 'A'; c <= 'Z'; c++)
    - re_syntax_table[c] = Sword;
    -
    - for (c = '0'; c <= '9'; c++)
    - re_syntax_table[c] = Sword;
    -
    - re_syntax_table['_'] = Sword;
    -
    - done = 1;
    -}
    -
    -#endif /* not SYNTAX_TABLE */
    -
    -#define SYNTAX(c) re_syntax_table[c]
    -
    -#endif /* not emacs */
    -
    -/* Get the interface, including the syntax bits. */
    -#include "regex.h"
    -
    -/* isalpha etc. are used for the character classes. */
    -#include <ctype.h>
    -
    -#ifndef isascii
    -#define isascii(c) 1
    -#endif
    -
    -#ifdef isblank
    -#define ISBLANK(c) (isascii (c) && isblank (c))
    -#else
    -#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
    -#endif
    -#ifdef isgraph
    -#define ISGRAPH(c) (isascii (c) && isgraph (c))
    -#else
    -#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
    -#endif
    -
    -#define ISPRINT(c) (isascii (c) && isprint (c))
    -#define ISDIGIT(c) (isascii (c) && isdigit (c))
    -#define ISALNUM(c) (isascii (c) && isalnum (c))
    -#define ISALPHA(c) (isascii (c) && isalpha (c))
    -#define ISCNTRL(c) (isascii (c) && iscntrl (c))
    -#define ISLOWER(c) (isascii (c) && islower (c))
    -#define ISPUNCT(c) (isascii (c) && ispunct (c))
    -#define ISSPACE(c) (isascii (c) && isspace (c))
    -#define ISUPPER(c) (isascii (c) && isupper (c))
    -#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
    -
    -#ifndef NULL
    -#define NULL 0
    -#endif
    -
    -/* We remove any previous definition of `SIGN_EXTEND_CHAR',
    - since ours (we hope) works properly with all combinations of
    - machines, compilers, `char' and `unsigned char' argument types.
    - (Per Bothner suggested the basic approach.) */
    -#undef SIGN_EXTEND_CHAR
    -#if __STDC__
    -#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
    -#else /* not __STDC__ */
    -/* As in Harbison and Steele. */
    -#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
    -#endif
    -
    -/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
    - use `alloca' instead of `malloc'. This is because using malloc in
    - re_search* or re_match* could cause memory leaks when C-g is used in
    - Emacs; also, malloc is slower and causes storage fragmentation. On
    - the other hand, malloc is more portable, and easier to debug.
    -
    - Because we sometimes use alloca, some routines have to be macros,
    - not functions -- `alloca'-allocated space disappears at the end of the
    - function it is called in. */
    -
    -#ifdef REGEX_MALLOC
    -
    -#define REGEX_ALLOCATE malloc
    -#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
    -
    -#else /* not REGEX_MALLOC */
    -
    -/* Emacs already defines alloca, sometimes. */
    -#ifndef alloca
    -
    -/* Make alloca work the best possible way. */
    -#ifdef __GNUC__
    -#define alloca __builtin_alloca
    -#else /* not __GNUC__ */
    -#if HAVE_ALLOCA_H
    -#include <alloca.h>
    -#else /* not __GNUC__ or HAVE_ALLOCA_H */
    -#ifndef _AIX /* Already did AIX, up at the top. */
    -char *alloca ();
    -#endif /* not _AIX */
    -#endif /* not HAVE_ALLOCA_H */
    -#endif /* not __GNUC__ */
    -
    -#endif /* not alloca */
    -
    -#define REGEX_ALLOCATE alloca
    -
    -/* Assumes a `char *destination' variable. */
    -#define REGEX_REALLOCATE(source, osize, nsize) \
    - (destination = (char *) alloca (nsize), \
    - bcopy (source, destination, osize), \
    - destination)
    -
    -#endif /* not REGEX_MALLOC */
    -
    -
    -/* True if `size1' is non-NULL and PTR is pointing anywhere inside
    - `string1' or just past its end. This works if PTR is NULL, which is
    - a good thing. */
    -#define FIRST_STRING_P(ptr) \
    - (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
    -
    -/* (Re)Allocate N items of type T using malloc, or fail. */
    -#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
    -#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
    -#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
    -
    -#define BYTEWIDTH 8 /* In bits. */
    -
    -#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
    -
    -#define MAX(a, b) ((a) > (b) ? (a) : (b))
    -#define MIN(a, b) ((a) < (b) ? (a) : (b))
    -
    -typedef char boolean;
    -#define false 0
    -#define true 1
    -
    -/* These are the command codes that appear in compiled regular
    - expressions. Some opcodes are followed by argument bytes. A
    - command code can specify any interpretation whatsoever for its
    - arguments. Zero bytes may appear in the compiled regular expression.
    -
    - The value of `exactn' is needed in search.c (search_buffer) in Emacs.
    - So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
    - `exactn' we use here must also be 1. */
    -
    -typedef enum
    -{
    - no_op = 0,
    -
    - /* Followed by one byte giving n, then by n literal bytes. */
    - exactn = 1,
    -
    - /* Matches any (more or less) character. */
    - anychar,
    -
    - /* Matches any one char belonging to specified set. First
    - following byte is number of bitmap bytes. Then come bytes
    - for a bitmap saying which chars are in. Bits in each byte
    - are ordered low-bit-first. A character is in the set if its
    - bit is 1. A character too large to have a bit in the map is
    - automatically not in the set. */
    - charset,
    -
    - /* Same parameters as charset, but match any character that is
    - not one of those specified. */
    - charset_not,
    -
    - /* Start remembering the text that is matched, for storing in a
    - register. Followed by one byte with the register number, in
    - the range 0 to one less than the pattern buffer's re_nsub
    - field. Then followed by one byte with the number of groups
    - inner to this one. (This last has to be part of the
    - start_memory only because we need it in the on_failure_jump
    - of re_match_2.) */
    - start_memory,
    -
    - /* Stop remembering the text that is matched and store it in a
    - memory register. Followed by one byte with the register
    - number, in the range 0 to one less than `re_nsub' in the
    - pattern buffer, and one byte with the number of inner groups,
    - just like `start_memory'. (We need the number of inner
    - groups here because we don't have any easy way of finding the
    - corresponding start_memory when we're at a stop_memory.) */
    - stop_memory,
    -
    - /* Match a duplicate of something remembered. Followed by one
    - byte containing the register number. */
    - duplicate,
    -
    - /* Fail unless at beginning of line. */
    - begline,
    -
    - /* Fail unless at end of line. */
    - endline,
    -
    - /* Succeeds if at beginning of buffer (if emacs) or at beginning
    - of string to be matched (if not). */
    - begbuf,
    -
    - /* Analogously, for end of buffer/string. */
    - endbuf,
    -
    - /* Followed by two byte relative address to which to jump. */
    - jump,
    -
    - /* Same as jump, but marks the end of an alternative. */
    - jump_past_alt,
    -
    - /* Followed by two-byte relative address of place to resume at
    - in case of failure. */
    - on_failure_jump,
    -
    - /* Like on_failure_jump, but pushes a placeholder instead of the
    - current string position when executed. */
    - on_failure_keep_string_jump,
    -
    - /* Throw away latest failure point and then jump to following
    - two-byte relative address. */
    - pop_failure_jump,
    -
    - /* Change to pop_failure_jump if know won't have to backtrack to
    - match; otherwise change to jump. This is used to jump
    - back to the beginning of a repeat. If what follows this jump
    - clearly won't match what the repeat does, such that we can be
    - sure that there is no use backtracking out of repetitions
    - already matched, then we change it to a pop_failure_jump.
    - Followed by two-byte address. */
    - maybe_pop_jump,
    -
    - /* Jump to following two-byte address, and push a dummy failure
    - point. This failure point will be thrown away if an attempt
    - is made to use it for a failure. A `+' construct makes this
    - before the first repeat. Also used as an intermediary kind
    - of jump when compiling an alternative. */
    - dummy_failure_jump,
    -
    - /* Push a dummy failure point and continue. Used at the end of
    - alternatives. */
    - push_dummy_failure,
    -
    - /* Followed by two-byte relative address and two-byte number n.
    - After matching N times, jump to the address upon failure. */
    - succeed_n,
    -
    - /* Followed by two-byte relative address, and two-byte number n.
    - Jump to the address N times, then fail. */
    - jump_n,
    -
    - /* Set the following two-byte relative address to the
    - subsequent two-byte number. The address *includes* the two
    - bytes of number. */
    - set_number_at,
    -
    - wordchar, /* Matches any word-constituent character. */
    - notwordchar, /* Matches any char that is not a word-constituent. */
    -
    - wordbeg, /* Succeeds if at word beginning. */
    - wordend, /* Succeeds if at word end. */
    -
    - wordbound, /* Succeeds if at a word boundary. */
    - notwordbound /* Succeeds if not at a word boundary. */
    -
    -#ifdef emacs
    - ,before_dot, /* Succeeds if before point. */
    - at_dot, /* Succeeds if at point. */
    - after_dot, /* Succeeds if after point. */
    -
    - /* Matches any character whose syntax is specified. Followed by
    - a byte which contains a syntax code, e.g., Sword. */
    - syntaxspec,
    -
    - /* Matches any character whose syntax is not that specified. */
    - notsyntaxspec
    -#endif /* emacs */
    -} re_opcode_t;
    -
    -/* Common operations on the compiled pattern. */
    -
    -/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
    -
    -#define STORE_NUMBER(destination, number) \
    - do { \
    - (destination)[0] = (number) & 0377; \
    - (destination)[1] = (number) >> 8; \
    - } while (0)
    -
    -/* Same as STORE_NUMBER, except increment DESTINATION to
    - the byte after where the number is stored. Therefore, DESTINATION
    - must be an lvalue. */
    -
    -#define STORE_NUMBER_AND_INCR(destination, number) \
    - do { \
    - STORE_NUMBER (destination, number); \
    - (destination) += 2; \
    - } while (0)
    -
    -/* Put into DESTINATION a number stored in two contiguous bytes starting
    - at SOURCE. */
    -
    -#define EXTRACT_NUMBER(destination, source) \
    - do { \
    - (destination) = *(source) & 0377; \
    - (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
    - } while (0)
    -
    -#ifdef DEBUG
    -static void
    -extract_number (dest, source)
    - int *dest;
    - unsigned char *source;
    -{
    - int temp = SIGN_EXTEND_CHAR (*(source + 1));
    - *dest = *source & 0377;
    - *dest += temp << 8;
    -}
    -
    -#ifndef EXTRACT_MACROS /* To debug the macros. */
    -#undef EXTRACT_NUMBER
    -#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
    -#endif /* not EXTRACT_MACROS */
    -
    -#endif /* DEBUG */
    -
    -/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
    - SOURCE must be an lvalue. */
    -
    -#define EXTRACT_NUMBER_AND_INCR(destination, source) \
    - do { \
    - EXTRACT_NUMBER (destination, source); \
    - (source) += 2; \
    - } while (0)
    -
    -#ifdef DEBUG
    -static void
    -extract_number_and_incr (destination, source)
    - int *destination;
    - unsigned char **source;
    -{
    - extract_number (destination, *source);
    - *source += 2;
    -}
    -
    -#ifndef EXTRACT_MACROS
    -#undef EXTRACT_NUMBER_AND_INCR
    -#define EXTRACT_NUMBER_AND_INCR(dest, src) \
    - extract_number_and_incr (&dest, &src)
    -#endif /* not EXTRACT_MACROS */
    -
    -#endif /* DEBUG */
    -
    -/* If DEBUG is defined, Regex prints many voluminous messages about what
    - it is doing (if the variable `debug' is nonzero). If linked with the
    - main program in `iregex.c', you can enter patterns and strings
    - interactively. And if linked with the main program in `main.c' and
    - the other test files, you can run the already-written tests. */
    -
    -#ifdef DEBUG
    -
    -/* We use standard I/O for debugging. */
    -#include <stdio.h>
    -
    -/* It is useful to test things that ``must'' be true when debugging. */
    -#include <assert.h>
    -
    -static int debug = 0;
    -
    -#define DEBUG_STATEMENT(e) e
    -#define DEBUG_PRINT1(x) if (debug) printf (x)
    -#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
    -#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
    -#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
    -#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \
    - if (debug) print_partial_compiled_pattern (s, e)
    -#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \
    - if (debug) print_double_string (w, s1, sz1, s2, sz2)
    -
    -
    -extern void printchar ();
    -
    -/* Print the fastmap in human-readable form. */
    -
    -void
    -print_fastmap (fastmap)
    - char *fastmap;
    -{
    - unsigned was_a_range = 0;
    - unsigned i = 0;
    -
    - while (i < (1 << BYTEWIDTH))
    - {
    - if (fastmap[i++])
    - {
    - was_a_range = 0;
    - printchar (i - 1);
    - while (i < (1 << BYTEWIDTH) && fastmap[i])
    - {
    - was_a_range = 1;
    - i++;
    - }
    - if (was_a_range)
    - {
    - printf ("-");
    - printchar (i - 1);
    - }
    - }
    - }
    - putchar ('\n');
    -}
    -
    -
    -/* Print a compiled pattern string in human-readable form, starting at
    - the START pointer into it and ending just before the pointer END. */
    -
    -void
    -print_partial_compiled_pattern (start, end)
    - unsigned char *start;
    - unsigned char *end;
    -{
    - int mcnt, mcnt2;
    - unsigned char *p = start;
    - unsigned char *pend = end;
    -
    - if (start == NULL)
    - {
    - printf ("(null)\n");
    - return;
    - }
    -
    - /* Loop over pattern commands. */
    - while (p < pend)
    - {
    - switch ((re_opcode_t) *p++)
    - {
    - case no_op:
    - printf ("/no_op");
    - break;
    -
    - case exactn:
    - mcnt = *p++;
    - printf ("/exactn/%d", mcnt);
    - do
    - {
    - putchar ('/');
    - printchar (*p++);
    - }
    - while (--mcnt);
    - break;
    -
    - case start_memory:
    - mcnt = *p++;
    - printf ("/start_memory/%d/%d", mcnt, *p++);
    - break;
    -
    - case stop_memory:
    - mcnt = *p++;
    - printf ("/stop_memory/%d/%d", mcnt, *p++);
    - break;
    -
    - case duplicate:
    - printf ("/duplicate/%d", *p++);
    - break;
    -
    - case anychar:
    - printf ("/anychar");
    - break;
    -
    - case charset:
    - case charset_not:
    - {
    - register int c;
    -
    - printf ("/charset%s",
    - (re_opcode_t) *(p - 1) == charset_not ? "_not" : "");
    -
    - assert (p + *p < pend);
    -
    - for (c = 0; c < *p; c++)
    - {
    - unsigned bit;
    - unsigned char map_byte = p[1 + c];
    -
    - putchar ('/');
    -
    - for (bit = 0; bit < BYTEWIDTH; bit++)
    - if (map_byte & (1 << bit))
    - printchar (c * BYTEWIDTH + bit);
    - }
    - p += 1 + *p;
    - break;
    - }
    -
    - case begline:
    - printf ("/begline");
    - break;
    -
    - case endline:
    - printf ("/endline");
    - break;
    -
    - case on_failure_jump:
    - extract_number_and_incr (&mcnt, &p);
    - printf ("/on_failure_jump/0/%d", mcnt);
    - break;
    -
    - case on_failure_keep_string_jump:
    - extract_number_and_incr (&mcnt, &p);
    - printf ("/on_failure_keep_string_jump/0/%d", mcnt);
    - break;
    -
    - case dummy_failure_jump:
    - extract_number_and_incr (&mcnt, &p);
    - printf ("/dummy_failure_jump/0/%d", mcnt);
    - break;
    -
    - case push_dummy_failure:
    - printf ("/push_dummy_failure");
    - break;
    -
    - case maybe_pop_jump:
    - extract_number_and_incr (&mcnt, &p);
    - printf ("/maybe_pop_jump/0/%d", mcnt);
    - break;
    -
    - case pop_failure_jump:
    - extract_number_and_incr (&mcnt, &p);
    - printf ("/pop_failure_jump/0/%d", mcnt);
    - break;
    -
    - case jump_past_alt:
    - extract_number_and_incr (&mcnt, &p);
    - printf ("/jump_past_alt/0/%d", mcnt);
    - break;
    -
    - case jump:
    - extract_number_and_incr (&mcnt, &p);
    - printf ("/jump/0/%d", mcnt);
    - break;
    -
    - case succeed_n:
    - extract_number_and_incr (&mcnt, &p);
    - extract_number_and_incr (&mcnt2, &p);
    - printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
    - break;
    -
    - case jump_n:
    - extract_number_and_incr (&mcnt, &p);
    - extract_number_and_incr (&mcnt2, &p);
    - printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2);
    - break;
    -
    - case set_number_at:
    - extract_number_and_incr (&mcnt, &p);
    - extract_number_and_incr (&mcnt2, &p);
    - printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
    - break;
    -
    - case wordbound:
    - printf ("/wordbound");
    - break;
    -
    - case notwordbound:
    - printf ("/notwordbound");
    - break;
    -
    - case wordbeg:
    - printf ("/wordbeg");
    - break;
    -
    - case wordend:
    - printf ("/wordend");
    -
    -#ifdef emacs
    - case before_dot:
    - printf ("/before_dot");
    - break;
    -
    - case at_dot:
    - printf ("/at_dot");
    - break;
    -
    - case after_dot:
    - printf ("/after_dot");
    - break;
    -
    - case syntaxspec:
    - printf ("/syntaxspec");
    - mcnt = *p++;
    - printf ("/%d", mcnt);
    - break;
    -
    - case notsyntaxspec:
    - printf ("/notsyntaxspec");
    - mcnt = *p++;
    - printf ("/%d", mcnt);
    - break;
    -#endif /* emacs */
    -
    - case wordchar:
    - printf ("/wordchar");
    - break;
    -
    - case notwordchar:
    - printf ("/notwordchar");
    - break;
    -
    - case begbuf:
    - printf ("/begbuf");
    - break;
    -
    - case endbuf:
    - printf ("/endbuf");
    - break;
    -
    - default:
    - printf ("?%d", *(p-1));
    - }
    - }
    - printf ("/\n");
    -}
    -
    -
    -void
    -print_compiled_pattern (bufp)
    - struct re_pattern_buffer *bufp;
    -{
    - unsigned char *buffer = bufp->buffer;
    -
    - print_partial_compiled_pattern (buffer, buffer + bufp->used);
    - printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
    -
    - if (bufp->fastmap_accurate && bufp->fastmap)
    - {
    - printf ("fastmap: ");
    - print_fastmap (bufp->fastmap);
    - }
    -
    - printf ("re_nsub: %d\t", bufp->re_nsub);
    - printf ("regs_alloc: %d\t", bufp->regs_allocated);
    - printf ("can_be_null: %d\t", bufp->can_be_null);
    - printf ("newline_anchor: %d\n", bufp->newline_anchor);
    - printf ("no_sub: %d\t", bufp->no_sub);
    - printf ("not_bol: %d\t", bufp->not_bol);
    - printf ("not_eol: %d\t", bufp->not_eol);
    - printf ("syntax: %d\n", bufp->syntax);
    - /* Perhaps we should print the translate table? */
    -}
    -
    -
    -void
    -print_double_string (where, string1, size1, string2, size2)
    - const char *where;
    - const char *string1;
    - const char *string2;
    - int size1;
    - int size2;
    -{
    - unsigned this_char;
    -
    - if (where == NULL)
    - printf ("(null)");
    - else
    - {
    - if (FIRST_STRING_P (where))
    - {
    - for (this_char = where - string1; this_char < size1; this_char++)
    - printchar (string1[this_char]);
    -
    - where = string2;
    - }
    -
    - for (this_char = where - string2; this_char < size2; this_char++)
    - printchar (string2[this_char]);
    - }
    -}
    -
    -#else /* not DEBUG */
    -
    -#undef assert
    -#define assert(e)
    -
    -#define DEBUG_STATEMENT(e)
    -#define DEBUG_PRINT1(x)
    -#define DEBUG_PRINT2(x1, x2)
    -#define DEBUG_PRINT3(x1, x2, x3)
    -#define DEBUG_PRINT4(x1, x2, x3, x4)
    -#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
    -#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
    -
    -#endif /* not DEBUG */
    -
    -/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
    - also be assigned to arbitrarily: each pattern buffer stores its own
    - syntax, so it can be changed between regex compilations. */
    -reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
    -
    -
    -/* Specify the precise syntax of regexps for compilation. This provides
    - for compatibility for various utilities which historically have
    - different, incompatible syntaxes.
    -
    - The argument SYNTAX is a bit mask comprised of the various bits
    - defined in regex.h. We return the old syntax. */
    -
    -reg_syntax_t
    -re_set_syntax (syntax)
    - reg_syntax_t syntax;
    -{
    - reg_syntax_t ret = re_syntax_options;
    -
    - re_syntax_options = syntax;
    - return ret;
    -}
    -
    -/* This table gives an error message for each of the error codes listed
    - in regex.h. Obviously the order here has to be same as there. */
    -
    -static const char *re_error_msg[] =
    - { NULL, /* REG_NOERROR */
    - "No match", /* REG_NOMATCH */
    - "Invalid regular expression", /* REG_BADPAT */
    - "Invalid collation character", /* REG_ECOLLATE */
    - "Invalid character class name", /* REG_ECTYPE */
    - "Trailing backslash", /* REG_EESCAPE */
    - "Invalid back reference", /* REG_ESUBREG */
    - "Unmatched [ or [^", /* REG_EBRACK */
    - "Unmatched ( or \\(", /* REG_EPAREN */
    - "Unmatched \\{", /* REG_EBRACE */
    - "Invalid content of \\{\\}", /* REG_BADBR */
    - "Invalid range end", /* REG_ERANGE */
    - "Memory exhausted", /* REG_ESPACE */
    - "Invalid preceding regular expression", /* REG_BADRPT */
    - "Premature end of regular expression", /* REG_EEND */
    - "Regular expression too big", /* REG_ESIZE */
    - "Unmatched ) or \\)", /* REG_ERPAREN */
    - };
    -
    -/* Subroutine declarations and macros for regex_compile. */
    -
    -static void store_op1 (), store_op2 ();
    -static void insert_op1 (), insert_op2 ();
    -static boolean at_begline_loc_p (), at_endline_loc_p ();
    -static boolean group_in_compile_stack ();
    -static reg_errcode_t compile_range ();
    -
    -/* Fetch the next character in the uncompiled pattern---translating it
    - if necessary. Also cast from a signed character in the constant
    - string passed to us by the user to an unsigned char that we can use
    - as an array index (in, e.g., `translate'). */
    -#define PATFETCH(c) \
    - do {if (p == pend) return REG_EEND; \
    - c = (unsigned char) *p++; \
    - if (translate) c = translate[c]; \
    - } while (0)
    -
    -/* Fetch the next character in the uncompiled pattern, with no
    - translation. */
    -#define PATFETCH_RAW(c) \
    - do {if (p == pend) return REG_EEND; \
    - c = (unsigned char) *p++; \
    - } while (0)
    -
    -/* Go backwards one character in the pattern. */
    -#define PATUNFETCH p--
    -
    -
    -/* If `translate' is non-null, return translate[D], else just D. We
    - cast the subscript to translate because some data is declared as
    - `char *', to avoid warnings when a string constant is passed. But
    - when we use a character as a subscript we must make it unsigned. */
    -#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
    -
    -
    -/* Macros for outputting the compiled pattern into `buffer'. */
    -
    -/* If the buffer isn't allocated when it comes in, use this. */
    -#define INIT_BUF_SIZE 32
    -
    -/* Make sure we have at least N more bytes of space in buffer. */
    -#define GET_BUFFER_SPACE(n) \
    - while (b - bufp->buffer + (n) > bufp->allocated) \
    - EXTEND_BUFFER ()
    -
    -/* Make sure we have one more byte of buffer space and then add C to it. */
    -#define BUF_PUSH(c) \
    - do { \
    - GET_BUFFER_SPACE (1); \
    - *b++ = (unsigned char) (c); \
    - } while (0)
    -
    -
    -/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
    -#define BUF_PUSH_2(c1, c2) \
    - do { \
    - GET_BUFFER_SPACE (2); \
    - *b++ = (unsigned char) (c1); \
    - *b++ = (unsigned char) (c2); \
    - } while (0)
    -
    -
    -/* As with BUF_PUSH_2, except for three bytes. */
    -#define BUF_PUSH_3(c1, c2, c3) \
    - do { \
    - GET_BUFFER_SPACE (3); \
    - *b++ = (unsigned char) (c1); \
    - *b++ = (unsigned char) (c2); \
    - *b++ = (unsigned char) (c3); \
    - } while (0)
    -
    -
    -/* Store a jump with opcode OP at LOC to location TO. We store a
    - relative address offset by the three bytes the jump itself occupies. */
    -#define STORE_JUMP(op, loc, to) \
    - store_op1 (op, loc, (to) - (loc) - 3)
    -
    -/* Likewise, for a two-argument jump. */
    -#define STORE_JUMP2(op, loc, to, arg) \
    - store_op2 (op, loc, (to) - (loc) - 3, arg)
    -
    -/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
    -#define INSERT_JUMP(op, loc, to) \
    - insert_op1 (op, loc, (to) - (loc) - 3, b)
    -
    -/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
    -#define INSERT_JUMP2(op, loc, to, arg) \
    - insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
    -
    -
    -/* This is not an arbitrary limit: the arguments which represent offsets
    - into the pattern are two bytes long. So if 2^16 bytes turns out to
    - be too small, many things would have to change. */
    -#define MAX_BUF_SIZE (1L << 16)
    -
    -
    -/* Extend the buffer by twice its current size via realloc and
    - reset the pointers that pointed into the old block to point to the
    - correct places in the new one. If extending the buffer results in it
    - being larger than MAX_BUF_SIZE, then flag memory exhausted. */
    -#define EXTEND_BUFFER() \
    - do { \
    - unsigned char *old_buffer = bufp->buffer; \
    - if (bufp->allocated == MAX_BUF_SIZE) \
    - return REG_ESIZE; \
    - bufp->allocated <<= 1; \
    - if (bufp->allocated > MAX_BUF_SIZE) \
    - bufp->allocated = MAX_BUF_SIZE; \
    - bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
    - if (bufp->buffer == NULL) \
    - return REG_ESPACE; \
    - /* If the buffer moved, move all the pointers into it. */ \
    - if (old_buffer != bufp->buffer) \
    - { \
    - b = (b - old_buffer) + bufp->buffer; \
    - begalt = (begalt - old_buffer) + bufp->buffer; \
    - if (fixup_alt_jump) \
    - fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
    - if (laststart) \
    - laststart = (laststart - old_buffer) + bufp->buffer; \
    - if (pending_exact) \
    - pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
    - } \
    - } while (0)
    -
    -
    -/* Since we have one byte reserved for the register number argument to
    - {start,stop}_memory, the maximum number of groups we can report
    - things about is what fits in that byte. */
    -#define MAX_REGNUM 255
    -
    -/* But patterns can have more than `MAX_REGNUM' registers. We just
    - ignore the excess. */
    -typedef unsigned regnum_t;
    -
    -
    -/* Macros for the compile stack. */
    -
    -/* Since offsets can go either forwards or backwards, this type needs to
    - be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
    -typedef int pattern_offset_t;
    -
    -typedef struct
    -{
    - pattern_offset_t begalt_offset;
    - pattern_offset_t fixup_alt_jump;
    - pattern_offset_t inner_group_offset;
    - pattern_offset_t laststart_offset;
    - regnum_t regnum;
    -} compile_stack_elt_t;
    -
    -
    -typedef struct
    -{
    - compile_stack_elt_t *stack;
    - unsigned size;
    - unsigned avail; /* Offset of next open position. */
    -} compile_stack_type;
    -
    -
    -#define INIT_COMPILE_STACK_SIZE 32
    -
    -#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
    -#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
    -
    -/* The next available element. */
    -#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
    -
    -
    -/* Set the bit for character C in a list. */
    -#define SET_LIST_BIT(c) \
    - (b[((unsigned char) (c)) / BYTEWIDTH] \
    - |= 1 << (((unsigned char) c) % BYTEWIDTH))
    -
    -
    -/* Get the next unsigned number in the uncompiled pattern. */
    -#define GET_UNSIGNED_NUMBER(num) \
    - { if (p != pend) \
    - { \
    - PATFETCH (c); \
    - while (ISDIGIT (c)) \
    - { \
    - if (num < 0) \
    - num = 0; \
    - num = num * 10 + c - '0'; \
    - if (p == pend) \
    - break; \
    - PATFETCH (c); \
    - } \
    - } \
    - }
    -
    -#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
    -
    -#define IS_CHAR_CLASS(string) \
    - (STREQ (string, "alpha") || STREQ (string, "upper") \
    - || STREQ (string, "lower") || STREQ (string, "digit") \
    - || STREQ (string, "alnum") || STREQ (string, "xdigit") \
    - || STREQ (string, "space") || STREQ (string, "print") \
    - || STREQ (string, "punct") || STREQ (string, "graph") \
    - || STREQ (string, "cntrl") || STREQ (string, "blank"))
    -
    -/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
    - Returns one of error codes defined in `regex.h', or zero for success.
    -
    - Assumes the `allocated' (and perhaps `buffer') and `translate'
    - fields are set in BUFP on entry.
    -
    - If it succeeds, results are put in BUFP (if it returns an error, the
    - contents of BUFP are undefined):
    - `buffer' is the compiled pattern;
    - `syntax' is set to SYNTAX;
    - `used' is set to the length of the compiled pattern;
    - `fastmap_accurate' is zero;
    - `re_nsub' is the number of subexpressions in PATTERN;
    - `not_bol' and `not_eol' are zero;
    -
    - The `fastmap' and `newline_anchor' fields are neither
    - examined nor set. */
    -
    -static reg_errcode_t
    -regex_compile (pattern, size, syntax, bufp)
    - const char *pattern;
    - int size;
    - reg_syntax_t syntax;
    - struct re_pattern_buffer *bufp;
    -{
    - /* We fetch characters from PATTERN here. Even though PATTERN is
    - `char *' (i.e., signed), we declare these variables as unsigned, so
    - they can be reliably used as array indices. */
    - register unsigned char c, c1;
    -
    - /* A random tempory spot in PATTERN. */
    - const char *p1;
    -
    - /* Points to the end of the buffer, where we should append. */
    - register unsigned char *b;
    -
    - /* Keeps track of unclosed groups. */
    - compile_stack_type compile_stack;
    -
    - /* Points to the current (ending) position in the pattern. */
    - const char *p = pattern;
    - const char *pend = pattern + size;
    -
    - /* How to translate the characters in the pattern. */
    - char *translate = bufp->translate;
    -
    - /* Address of the count-byte of the most recently inserted `exactn'
    - command. This makes it possible to tell if a new exact-match
    - character can be added to that command or if the character requires
    - a new `exactn' command. */
    - unsigned char *pending_exact = 0;
    -
    - /* Address of start of the most recently finished expression.
    - This tells, e.g., postfix * where to find the start of its
    - operand. Reset at the beginning of groups and alternatives. */
    - unsigned char *laststart = 0;
    -
    - /* Address of beginning of regexp, or inside of last group. */
    - unsigned char *begalt;
    -
    - /* Place in the uncompiled pattern (i.e., the {) to
    - which to go back if the interval is invalid. */
    - const char *beg_interval;
    -
    - /* Address of the place where a forward jump should go to the end of
    - the containing expression. Each alternative of an `or' -- except the
    - last -- ends with a forward jump of this sort. */
    - unsigned char *fixup_alt_jump = 0;
    -
    - /* Counts open-groups as they are encountered. Remembered for the
    - matching close-group on the compile stack, so the same register
    - number is put in the stop_memory as the start_memory. */
    - regnum_t regnum = 0;
    -
    -#ifdef DEBUG
    - DEBUG_PRINT1 ("\nCompiling pattern: ");
    - if (debug)
    - {
    - unsigned debug_count;
    -
    - for (debug_count = 0; debug_count < size; debug_count++)
    - printchar (pattern[debug_count]);
    - putchar ('\n');
    - }
    -#endif /* DEBUG */
    -
    - /* Initialize the compile stack. */
    - compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
    - if (compile_stack.stack == NULL)
    - return REG_ESPACE;
    -
    - compile_stack.size = INIT_COMPILE_STACK_SIZE;
    - compile_stack.avail = 0;
    -
    - /* Initialize the pattern buffer. */
    - bufp->syntax = syntax;
    - bufp->fastmap_accurate = 0;
    - bufp->not_bol = bufp->not_eol = 0;
    -
    - /* Set `used' to zero, so that if we return an error, the pattern
    - printer (for debugging) will think there's no pattern. We reset it
    - at the end. */
    - bufp->used = 0;
    -
    - /* Always count groups, whether or not bufp->no_sub is set. */
    - bufp->re_nsub = 0;
    -
    -#if !defined (emacs) && !defined (SYNTAX_TABLE)
    - /* Initialize the syntax table. */
    - init_syntax_once ();
    -#endif
    -
    - if (bufp->allocated == 0)
    - {
    - if (bufp->buffer)
    - { /* If zero allocated, but buffer is non-null, try to realloc
    - enough space. This loses if buffer's address is bogus, but
    - that is the user's responsibility. */
    - RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
    - }
    - else
    - { /* Caller did not allocate a buffer. Do it for them. */
    - bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
    - }
    - if (!bufp->buffer) return REG_ESPACE;
    -
    - bufp->allocated = INIT_BUF_SIZE;
    - }
    -
    - begalt = b = bufp->buffer;
    -
    - /* Loop through the uncompiled pattern until we're at the end. */
    - while (p != pend)
    - {
    - PATFETCH (c);
    -
    - switch (c)
    - {
    - case '^':
    - {
    - if ( /* If at start of pattern, it's an operator. */
    - p == pattern + 1
    - /* If context independent, it's an operator. */
    - || syntax & RE_CONTEXT_INDEP_ANCHORS
    - /* Otherwise, depends on what's come before. */
    - || at_begline_loc_p (pattern, p, syntax))
    - BUF_PUSH (begline);
    - else
    - goto normal_char;
    - }
    - break;
    -
    -
    - case '$':
    - {
    - if ( /* If at end of pattern, it's an operator. */
    - p == pend
    - /* If context independent, it's an operator. */
    - || syntax & RE_CONTEXT_INDEP_ANCHORS
    - /* Otherwise, depends on what's next. */
    - || at_endline_loc_p (p, pend, syntax))
    - BUF_PUSH (endline);
    - else
    - goto normal_char;
    - }
    - break;
    -
    -
    - case '+':
    - case '?':
    - if ((syntax & RE_BK_PLUS_QM)
    - || (syntax & RE_LIMITED_OPS))
    - goto normal_char;
    - handle_plus:
    - case '*':
    - /* If there is no previous pattern... */
    - if (!laststart)
    - {
    - if (syntax & RE_CONTEXT_INVALID_OPS)
    - return REG_BADRPT;
    - else if (!(syntax & RE_CONTEXT_INDEP_OPS))
    - goto normal_char;
    - }
    -
    - {
    - /* Are we optimizing this jump? */
    - boolean keep_string_p = false;
    -
    - /* 1 means zero (many) matches is allowed. */
    - char zero_times_ok = 0, many_times_ok = 0;
    -
    - /* If there is a sequence of repetition chars, collapse it
    - down to just one (the right one). We can't combine
    - interval operators with these because of, e.g., `a{2}*',
    - which should only match an even number of `a's. */
    -
    - for (;;)
    - {
    - zero_times_ok |= c != '+';
    - many_times_ok |= c != '?';
    -
    - if (p == pend)
    - break;
    -
    - PATFETCH (c);
    -
    - if (c == '*'
    - || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
    - ;
    -
    - else if (syntax & RE_BK_PLUS_QM && c == '\\')
    - {
    - if (p == pend) return REG_EESCAPE;
    -
    - PATFETCH (c1);
    - if (!(c1 == '+' || c1 == '?'))
    - {
    - PATUNFETCH;
    - PATUNFETCH;
    - break;
    - }
    -
    - c = c1;
    - }
    - else
    - {
    - PATUNFETCH;
    - break;
    - }
    -
    - /* If we get here, we found another repeat character. */
    - }
    -
    - /* Star, etc. applied to an empty pattern is equivalent
    - to an empty pattern. */
    - if (!laststart)
    - break;
    -
    - /* Now we know whether or not zero matches is allowed
    - and also whether or not two or more matches is allowed. */
    - if (many_times_ok)
    - { /* More than one repetition is allowed, so put in at the
    - end a backward relative jump from `b' to before the next
    - jump we're going to put in below (which jumps from
    - laststart to after this jump).
    -
    - But if we are at the `*' in the exact sequence `.*\n',
    - insert an unconditional jump backwards to the .,
    - instead of the beginning of the loop. This way we only
    - push a failure point once, instead of every time
    - through the loop. */
    - assert (p - 1 > pattern);
    -
    - /* Allocate the space for the jump. */
    - GET_BUFFER_SPACE (3);
    -
    - /* We know we are not at the first character of the pattern,
    - because laststart was nonzero. And we've already
    - incremented `p', by the way, to be the character after
    - the `*'. Do we have to do something analogous here
    - for null bytes, because of RE_DOT_NOT_NULL? */
    - if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
    - && zero_times_ok
    - && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
    - && !(syntax & RE_DOT_NEWLINE))
    - { /* We have .*\n. */
    - STORE_JUMP (jump, b, laststart);
    - keep_string_p = true;
    - }
    - else
    - /* Anything else. */
    - STORE_JUMP (maybe_pop_jump, b, laststart - 3);
    -
    - /* We've added more stuff to the buffer. */
    - b += 3;
    - }
    -
    - /* On failure, jump from laststart to b + 3, which will be the
    - end of the buffer after this jump is inserted. */
    - GET_BUFFER_SPACE (3);
    - INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
    - : on_failure_jump,
    - laststart, b + 3);
    - pending_exact = 0;
    - b += 3;
    -
    - if (!zero_times_ok)
    - {
    - /* At least one repetition is required, so insert a
    - `dummy_failure_jump' before the initial
    - `on_failure_jump' instruction of the loop. This
    - effects a skip over that instruction the first time
    - we hit that loop. */
    - GET_BUFFER_SPACE (3);
    - INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
    - b += 3;
    - }
    - }
    - break;
    -
    -
    - case '.':
    - laststart = b;
    - BUF_PUSH (anychar);
    - break;
    -
    -
    - case '[':
    - {
    - boolean had_char_class = false;
    -
    - if (p == pend) return REG_EBRACK;
    -
    - /* Ensure that we have enough space to push a charset: the
    - opcode, the length count, and the bitset; 34 bytes in all. */
    - GET_BUFFER_SPACE (34);
    -
    - laststart = b;
    -
    - /* We test `*p == '^' twice, instead of using an if
    - statement, so we only need one BUF_PUSH. */
    - BUF_PUSH (*p == '^' ? charset_not : charset);
    - if (*p == '^')
    - p++;
    -
    - /* Remember the first position in the bracket expression. */
    - p1 = p;
    -
    - /* Push the number of bytes in the bitmap. */
    - BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
    -
    - /* Clear the whole map. */
    - bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
    -
    - /* charset_not matches newline according to a syntax bit. */
    - if ((re_opcode_t) b[-2] == charset_not
    - && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
    - SET_LIST_BIT ('\n');
    -
    - /* Read in characters and ranges, setting map bits. */
    - for (;;)
    - {
    - if (p == pend) return REG_EBRACK;
    -
    - PATFETCH (c);
    -
    - /* \ might escape characters inside [...] and [^...]. */
    - if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
    - {
    - if (p == pend) return REG_EESCAPE;
    -
    - PATFETCH (c1);
    - SET_LIST_BIT (c1);
    - continue;
    - }
    -
    - /* Could be the end of the bracket expression. If it's
    - not (i.e., when the bracket expression is `[]' so
    - far), the ']' character bit gets set way below. */
    - if (c == ']' && p != p1 + 1)
    - break;
    -
    - /* Look ahead to see if it's a range when the last thing
    - was a character class. */
    - if (had_char_class && c == '-' && *p != ']')
    - return REG_ERANGE;
    -
    - /* Look ahead to see if it's a range when the last thing
    - was a character: if this is a hyphen not at the
    - beginning or the end of a list, then it's the range
    - operator. */
    - if (c == '-'
    - && !(p - 2 >= pattern && p[-2] == '[')
    - && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
    - && *p != ']')
    - {
    - reg_errcode_t ret
    - = compile_range (&p, pend, translate, syntax, b);
    - if (ret != REG_NOERROR) return ret;
    - }
    -
    - else if (p[0] == '-' && p[1] != ']')
    - { /* This handles ranges made up of characters only. */
    - reg_errcode_t ret;
    -
    - /* Move past the `-'. */
    - PATFETCH (c1);
    -
    - ret = compile_range (&p, pend, translate, syntax, b);
    - if (ret != REG_NOERROR) return ret;
    - }
    -
    - /* See if we're at the beginning of a possible character
    - class. */
    -
    - else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
    - { /* Leave room for the null. */
    - char str[CHAR_CLASS_MAX_LENGTH + 1];
    -
    - PATFETCH (c);
    - c1 = 0;
    -
    - /* If pattern is `[[:'. */
    - if (p == pend) return REG_EBRACK;
    -
    - for (;;)
    - {
    - PATFETCH (c);
    - if (c == ':' || c == ']' || p == pend
    - || c1 == CHAR_CLASS_MAX_LENGTH)
    - break;
    - str[c1++] = c;
    - }
    - str[c1] = '\0';
    -
    - /* If isn't a word bracketed by `[:' and:`]':
    - undo the ending character, the letters, and leave
    - the leading `:' and `[' (but set bits for them). */
    - if (c == ':' && *p == ']')
    - {
    - int ch;
    - boolean is_alnum = STREQ (str, "alnum");
    - boolean is_alpha = STREQ (str, "alpha");
    - boolean is_blank = STREQ (str, "blank");
    - boolean is_cntrl = STREQ (str, "cntrl");
    - boolean is_digit = STREQ (str, "digit");
    - boolean is_graph = STREQ (str, "graph");
    - boolean is_lower = STREQ (str, "lower");
    - boolean is_print = STREQ (str, "print");
    - boolean is_punct = STREQ (str, "punct");
    - boolean is_space = STREQ (str, "space");
    - boolean is_upper = STREQ (str, "upper");
    - boolean is_xdigit = STREQ (str, "xdigit");
    -
    - if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
    -
    - /* Throw away the ] at the end of the character
    - class. */
    - PATFETCH (c);
    -
    - if (p == pend) return REG_EBRACK;
    -
    - for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
    - {
    - if ( (is_alnum && ISALNUM (ch))
    - || (is_alpha && ISALPHA (ch))
    - || (is_blank && ISBLANK (ch))
    - || (is_cntrl && ISCNTRL (ch))
    - || (is_digit && ISDIGIT (ch))
    - || (is_graph && ISGRAPH (ch))
    - || (is_lower && ISLOWER (ch))
    - || (is_print && ISPRINT (ch))
    - || (is_punct && ISPUNCT (ch))
    - || (is_space && ISSPACE (ch))
    - || (is_upper && ISUPPER (ch))
    - || (is_xdigit && ISXDIGIT (ch)))
    - SET_LIST_BIT (ch);
    - }
    - had_char_class = true;
    - }
    - else
    - {
    - c1++;
    - while (c1--)
    - PATUNFETCH;
    - SET_LIST_BIT ('[');
    - SET_LIST_BIT (':');
    - had_char_class = false;
    - }
    - }
    - else
    - {
    - had_char_class = false;
    - SET_LIST_BIT (c);
    - }
    - }
    -
    - /* Discard any (non)matching list bytes that are all 0 at the
    - end of the map. Decrease the map-length byte too. */
    - while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
    - b[-1]--;
    - b += b[-1];
    - }
    - break;
    -
    -
    - case '(':
    - if (syntax & RE_NO_BK_PARENS)
    - goto handle_open;
    - else
    - goto normal_char;
    -
    -
    - case ')':
    - if (syntax & RE_NO_BK_PARENS)
    - goto handle_close;
    - else
    - goto normal_char;
    -
    -
    - case '\n':
    - if (syntax & RE_NEWLINE_ALT)
    - goto handle_alt;
    - else
    - goto normal_char;
    -
    -
    - case '|':
    - if (syntax & RE_NO_BK_VBAR)
    - goto handle_alt;
    - else
    - goto normal_char;
    -
    -
    - case '{':
    - if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
    - goto handle_interval;
    - else
    - goto normal_char;
    -
    -
    - case '\\':
    - if (p == pend) return REG_EESCAPE;
    -
    - /* Do not translate the character after the \, so that we can
    - distinguish, e.g., \B from \b, even if we normally would
    - translate, e.g., B to b. */
    - PATFETCH_RAW (c);
    -
    - switch (c)
    - {
    - case '(':
    - if (syntax & RE_NO_BK_PARENS)
    - goto normal_backslash;
    -
    - handle_open:
    - bufp->re_nsub++;
    - regnum++;
    -
    - if (COMPILE_STACK_FULL)
    - {
    - RETALLOC (compile_stack.stack, compile_stack.size << 1,
    - compile_stack_elt_t);
    - if (compile_stack.stack == NULL) return REG_ESPACE;
    -
    - compile_stack.size <<= 1;
    - }
    -
    - /* These are the values to restore when we hit end of this
    - group. They are all relative offsets, so that if the
    - whole pattern moves because of realloc, they will still
    - be valid. */
    - COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
    - COMPILE_STACK_TOP.fixup_alt_jump
    - = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
    - COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
    - COMPILE_STACK_TOP.regnum = regnum;
    -
    - /* We will eventually replace the 0 with the number of
    - groups inner to this one. But do not push a
    - start_memory for groups beyond the last one we can
    - represent in the compiled pattern. */
    - if (regnum <= MAX_REGNUM)
    - {
    - COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
    - BUF_PUSH_3 (start_memory, regnum, 0);
    - }
    -
    - compile_stack.avail++;
    -
    - fixup_alt_jump = 0;
    - laststart = 0;
    - begalt = b;
    - /* If we've reached MAX_REGNUM groups, then this open
    - won't actually generate any code, so we'll have to
    - clear pending_exact explicitly. */
    - pending_exact = 0;
    - break;
    -
    -
    - case ')':
    - if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
    -
    - if (COMPILE_STACK_EMPTY){
    - if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD){
    - goto normal_backslash;
    - }else{
    - return REG_ERPAREN;
    - }
    - }
    -
    - handle_close:
    - if (fixup_alt_jump)
    - { /* Push a dummy failure point at the end of the
    - alternative for a possible future
    - `pop_failure_jump' to pop. See comments at
    - `push_dummy_failure' in `re_match_2'. */
    - BUF_PUSH (push_dummy_failure);
    -
    - /* We allocated space for this jump when we assigned
    - to `fixup_alt_jump', in the `handle_alt' case below. */
    - STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
    - }
    -
    - /* See similar code for backslashed left paren above. */
    - if (COMPILE_STACK_EMPTY){
    - if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD){
    - goto normal_char;
    - }else{
    - return REG_ERPAREN;
    - }
    - }
    -
    - /* Since we just checked for an empty stack above, this
    - ``can't happen''. */
    - assert (compile_stack.avail != 0);
    - {
    - /* We don't just want to restore into `regnum', because
    - later groups should continue to be numbered higher,
    - as in `(ab)c(de)' -- the second group is #2. */
    - regnum_t this_group_regnum;
    -
    - compile_stack.avail--;
    - begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
    - fixup_alt_jump
    - = COMPILE_STACK_TOP.fixup_alt_jump
    - ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
    - : 0;
    - laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
    - this_group_regnum = COMPILE_STACK_TOP.regnum;
    - /* If we've reached MAX_REGNUM groups, then this open
    - won't actually generate any code, so we'll have to
    - clear pending_exact explicitly. */
    - pending_exact = 0;
    -
    - /* We're at the end of the group, so now we know how many
    - groups were inside this one. */
    - if (this_group_regnum <= MAX_REGNUM)
    - {
    - unsigned char *inner_group_loc
    - = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
    -
    - *inner_group_loc = regnum - this_group_regnum;
    - BUF_PUSH_3 (stop_memory, this_group_regnum,
    - regnum - this_group_regnum);
    - }
    - }
    - break;
    -
    -
    - case '|': /* `\|'. */
    - if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
    - goto normal_backslash;
    - handle_alt:
    - if (syntax & RE_LIMITED_OPS)
    - goto normal_char;
    -
    - /* Insert before the previous alternative a jump which
    - jumps to this alternative if the former fails. */
    - GET_BUFFER_SPACE (3);
    - INSERT_JUMP (on_failure_jump, begalt, b + 6);
    - pending_exact = 0;
    - b += 3;
    -
    - /* The alternative before this one has a jump after it
    - which gets executed if it gets matched. Adjust that
    - jump so it will jump to this alternative's analogous
    - jump (put in below, which in turn will jump to the next
    - (if any) alternative's such jump, etc.). The last such
    - jump jumps to the correct final destination. A picture:
    - _____ _____
    - | | | |
    - | v | v
    - a | b | c
    -
    - If we are at `b', then fixup_alt_jump right now points to a
    - three-byte space after `a'. We'll put in the jump, set
    - fixup_alt_jump to right after `b', and leave behind three
    - bytes which we'll fill in when we get to after `c'. */
    -
    - if (fixup_alt_jump)
    - STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
    -
    - /* Mark and leave space for a jump after this alternative,
    - to be filled in later either by next alternative or
    - when know we're at the end of a series of alternatives. */
    - fixup_alt_jump = b;
    - GET_BUFFER_SPACE (3);
    - b += 3;
    -
    - laststart = 0;
    - begalt = b;
    - break;
    -
    -
    - case '{':
    - /* If \{ is a literal. */
    - if (!(syntax & RE_INTERVALS)
    - /* If we're at `\{' and it's not the open-interval
    - operator. */
    - || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
    - || (p - 2 == pattern && p == pend))
    - goto normal_backslash;
    -
    - handle_interval:
    - {
    - /* If got here, then the syntax allows intervals. */
    -
    - /* At least (most) this many matches must be made. */
    - int lower_bound = -1, upper_bound = -1;
    -
    - beg_interval = p - 1;
    -
    - if (p == pend)
    - {
    - if (syntax & RE_NO_BK_BRACES)
    - goto unfetch_interval;
    - else
    - return REG_EBRACE;
    - }
    -
    - GET_UNSIGNED_NUMBER (lower_bound);
    -
    - if (c == ',')
    - {
    - GET_UNSIGNED_NUMBER (upper_bound);
    - if (upper_bound < 0) upper_bound = RE_DUP_MAX;
    - }
    - else
    - /* Interval such as `{1}' => match exactly once. */
    - upper_bound = lower_bound;
    -
    - if (lower_bound < 0 || upper_bound > RE_DUP_MAX
    - || lower_bound > upper_bound)
    - {
    - if (syntax & RE_NO_BK_BRACES)
    - goto unfetch_interval;
    - else
    - return REG_BADBR;
    - }
    -
    - if (!(syntax & RE_NO_BK_BRACES))
    - {
    - if (c != '\\') return REG_EBRACE;
    -
    - PATFETCH (c);
    - }
    -
    - if (c != '}')
    - {
    - if (syntax & RE_NO_BK_BRACES)
    - goto unfetch_interval;
    - else
    - return REG_BADBR;
    - }
    -
    - /* We just parsed a valid interval. */
    -
    - /* If it's invalid to have no preceding re. */
    - if (!laststart)
    - {
    - if (syntax & RE_CONTEXT_INVALID_OPS)
    - return REG_BADRPT;
    - else if (syntax & RE_CONTEXT_INDEP_OPS)
    - laststart = b;
    - else
    - goto unfetch_interval;
    - }
    -
    - /* If the upper bound is zero, don't want to succeed at
    - all; jump from `laststart' to `b + 3', which will be
    - the end of the buffer after we insert the jump. */
    - if (upper_bound == 0)
    - {
    - GET_BUFFER_SPACE (3);
    - INSERT_JUMP (jump, laststart, b + 3);
    - b += 3;
    - }
    -
    - /* Otherwise, we have a nontrivial interval. When
    - we're all done, the pattern will look like:
    - set_number_at <jump count> <upper bound>
    - set_number_at <succeed_n count> <lower bound>
    - succeed_n <after jump addr> <succed_n count>
    - <body of loop>
    - jump_n <succeed_n addr> <jump count>
    - (The upper bound and `jump_n' are omitted if
    - `upper_bound' is 1, though.) */
    - else
    - { /* If the upper bound is > 1, we need to insert
    - more at the end of the loop. */
    - unsigned nbytes = 10 + (upper_bound > 1) * 10;
    -
    - GET_BUFFER_SPACE (nbytes);
    -
    - /* Initialize lower bound of the `succeed_n', even
    - though it will be set during matching by its
    - attendant `set_number_at' (inserted next),
    - because `re_compile_fastmap' needs to know.
    - Jump to the `jump_n' we might insert below. */
    - INSERT_JUMP2 (succeed_n, laststart,
    - b + 5 + (upper_bound > 1) * 5,
    - lower_bound);
    - b += 5;
    -
    - /* Code to initialize the lower bound. Insert
    - before the `succeed_n'. The `5' is the last two
    - bytes of this `set_number_at', plus 3 bytes of
    - the following `succeed_n'. */
    - insert_op2 (set_number_at, laststart, 5, lower_bound, b);
    - b += 5;
    -
    - if (upper_bound > 1)
    - { /* More than one repetition is allowed, so
    - append a backward jump to the `succeed_n'
    - that starts this interval.
    -
    - When we've reached this during matching,
    - we'll have matched the interval once, so
    - jump back only `upper_bound - 1' times. */
    - STORE_JUMP2 (jump_n, b, laststart + 5,
    - upper_bound - 1);
    - b += 5;
    -
    - /* The location we want to set is the second
    - parameter of the `jump_n'; that is `b-2' as
    - an absolute address. `laststart' will be
    - the `set_number_at' we're about to insert;
    - `laststart+3' the number to set, the source
    - for the relative address. But we are
    - inserting into the middle of the pattern --
    - so everything is getting moved up by 5.
    - Conclusion: (b - 2) - (laststart + 3) + 5,
    - i.e., b - laststart.
    -
    - We insert this at the beginning of the loop
    - so that if we fail during matching, we'll
    - reinitialize the bounds. */
    - insert_op2 (set_number_at, laststart, b - laststart,
    - upper_bound - 1, b);
    - b += 5;
    - }
    - }
    - pending_exact = 0;
    - beg_interval = NULL;
    - }
    - break;
    -
    - unfetch_interval:
    - /* If an invalid interval, match the characters as literals. */
    - assert (beg_interval);
    - p = beg_interval;
    - beg_interval = NULL;
    -
    - /* normal_char and normal_backslash need `c'. */
    - PATFETCH (c);
    -
    - if (!(syntax & RE_NO_BK_BRACES))
    - {
    - if (p > pattern && p[-1] == '\\')
    - goto normal_backslash;
    - }
    - goto normal_char;
    -
    -#ifdef emacs
    - /* There is no way to specify the before_dot and after_dot
    - operators. rms says this is ok. --karl */
    - case '=':
    - BUF_PUSH (at_dot);
    - break;
    -
    - case 's':
    - laststart = b;
    - PATFETCH (c);
    - BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
    - break;
    -
    - case 'S':
    - laststart = b;
    - PATFETCH (c);
    - BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
    - break;
    -#endif /* emacs */
    -
    -
    - case 'w':
    - laststart = b;
    - BUF_PUSH (wordchar);
    - break;
    -
    -
    - case 'W':
    - laststart = b;
    - BUF_PUSH (notwordchar);
    - break;
    -
    -
    - case '<':
    - BUF_PUSH (wordbeg);
    - break;
    -
    - case '>':
    - BUF_PUSH (wordend);
    - break;
    -
    - case 'b':
    - BUF_PUSH (wordbound);
    - break;
    -
    - case 'B':
    - BUF_PUSH (notwordbound);
    - break;
    -
    - case '`':
    - BUF_PUSH (begbuf);
    - break;
    -
    - case '\'':
    - BUF_PUSH (endbuf);
    - break;
    -
    - case '1': case '2': case '3': case '4': case '5':
    - case '6': case '7': case '8': case '9':
    - if (syntax & RE_NO_BK_REFS)
    - goto normal_char;
    -
    - c1 = c - '0';
    -
    - if (c1 > regnum)
    - return REG_ESUBREG;
    -
    - /* Can't back reference to a subexpression if inside of it. */
    - if (group_in_compile_stack (compile_stack, c1))
    - goto normal_char;
    -
    - laststart = b;
    - BUF_PUSH_2 (duplicate, c1);
    - break;
    -
    -
    - case '+':
    - case '?':
    - if (syntax & RE_BK_PLUS_QM)
    - goto handle_plus;
    - else
    - goto normal_backslash;
    -
    - default:
    - normal_backslash:
    - /* You might think it would be useful for \ to mean
    - not to translate; but if we don't translate it
    - it will never match anything. */
    - c = TRANSLATE (c);
    - goto normal_char;
    - }
    - break;
    -
    -
    - default:
    - /* Expects the character in `c'. */
    - normal_char:
    - /* If no exactn currently being built. */
    - if (!pending_exact
    -
    - /* If last exactn not at current position. */
    - || pending_exact + *pending_exact + 1 != b
    -
    - /* We have only one byte following the exactn for the count. */
    - || *pending_exact == (1 << BYTEWIDTH) - 1
    -
    - /* If followed by a repetition operator. */
    - || *p == '*' || *p == '^'
    - || ((syntax & RE_BK_PLUS_QM)
    - ? *p == '\\' && (p[1] == '+' || p[1] == '?')
    - : (*p == '+' || *p == '?'))
    - || ((syntax & RE_INTERVALS)
    - && ((syntax & RE_NO_BK_BRACES)
    - ? *p == '{'
    - : (p[0] == '\\' && p[1] == '{'))))
    - {
    - /* Start building a new exactn. */
    -
    - laststart = b;
    -
    - BUF_PUSH_2 (exactn, 0);
    - pending_exact = b - 1;
    - }
    -
    - BUF_PUSH (c);
    - (*pending_exact)++;
    - break;
    - } /* switch (c) */
    - } /* while p != pend */
    -
    -
    - /* Through the pattern now. */
    -
    - if (fixup_alt_jump)
    - STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
    -
    - if (!COMPILE_STACK_EMPTY)
    - return REG_EPAREN;
    -
    - free (compile_stack.stack);
    -
    - /* We have succeeded; set the length of the buffer. */
    - bufp->used = b - bufp->buffer;
    -
    -#ifdef DEBUG
    - if (debug)
    - {
    - DEBUG_PRINT1 ("\nCompiled pattern: ");
    - print_compiled_pattern (bufp);
    - }
    -#endif /* DEBUG */
    -
    - return REG_NOERROR;
    -} /* regex_compile */
    -
    -/* Subroutines for `regex_compile'. */
    -
    -/* Store OP at LOC followed by two-byte integer parameter ARG. */
    -
    -static void
    -store_op1 (op, loc, arg)
    - re_opcode_t op;
    - unsigned char *loc;
    - int arg;
    -{
    - *loc = (unsigned char) op;
    - STORE_NUMBER (loc + 1, arg);
    -}
    -
    -
    -/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
    -
    -static void
    -store_op2 (op, loc, arg1, arg2)
    - re_opcode_t op;
    - unsigned char *loc;
    - int arg1, arg2;
    -{
    - *loc = (unsigned char) op;
    - STORE_NUMBER (loc + 1, arg1);
    - STORE_NUMBER (loc + 3, arg2);
    -}
    -
    -
    -/* Copy the bytes from LOC to END to open up three bytes of space at LOC
    - for OP followed by two-byte integer parameter ARG. */
    -
    -static void
    -insert_op1 (op, loc, arg, end)
    - re_opcode_t op;
    - unsigned char *loc;
    - int arg;
    - unsigned char *end;
    -{
    - register unsigned char *pfrom = end;
    - register unsigned char *pto = end + 3;
    -
    - while (pfrom != loc)
    - *--pto = *--pfrom;
    -
    - store_op1 (op, loc, arg);
    -}
    -
    -
    -/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
    -
    -static void
    -insert_op2 (op, loc, arg1, arg2, end)
    - re_opcode_t op;
    - unsigned char *loc;
    - int arg1, arg2;
    - unsigned char *end;
    -{
    - register unsigned char *pfrom = end;
    - register unsigned char *pto = end + 5;
    -
    - while (pfrom != loc)
    - *--pto = *--pfrom;
    -
    - store_op2 (op, loc, arg1, arg2);
    -}
    -
    -
    -/* P points to just after a ^ in PATTERN. Return true if that ^ comes
    - after an alternative or a begin-subexpression. We assume there is at
    - least one character before the ^. */
    -
    -static boolean
    -at_begline_loc_p (pattern, p, syntax)
    - const char *pattern, *p;
    - reg_syntax_t syntax;
    -{
    - const char *prev = p - 2;
    - boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
    -
    - return
    - /* After a subexpression? */
    - (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
    - /* After an alternative? */
    - || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
    -}
    -
    -
    -/* The dual of at_begline_loc_p. This one is for $. We assume there is
    - at least one character after the $, i.e., `P < PEND'. */
    -
    -static boolean
    -at_endline_loc_p (p, pend, syntax)
    - const char *p, *pend;
    - int syntax;
    -{
    - const char *next = p;
    - boolean next_backslash = *next == '\\';
    - const char *next_next = p + 1 < pend ? p + 1 : NULL;
    -
    - return
    - /* Before a subexpression? */
    - (syntax & RE_NO_BK_PARENS ? *next == ')'
    - : next_backslash && next_next && *next_next == ')')
    - /* Before an alternative? */
    - || (syntax & RE_NO_BK_VBAR ? *next == '|'
    - : next_backslash && next_next && *next_next == '|');
    -}
    -
    -
    -/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
    - false if it's not. */
    -
    -static boolean
    -group_in_compile_stack (compile_stack, regnum)
    - compile_stack_type compile_stack;
    - regnum_t regnum;
    -{
    - int this_element;
    -
    - for (this_element = compile_stack.avail - 1;
    - this_element >= 0;
    - this_element--)
    - if (compile_stack.stack[this_element].regnum == regnum)
    - return true;
    -
    - return false;
    -}
    -
    -
    -/* Read the ending character of a range (in a bracket expression) from the
    - uncompiled pattern *P_PTR (which ends at PEND). We assume the
    - starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
    - Then we set the translation of all bits between the starting and
    - ending characters (inclusive) in the compiled pattern B.
    -
    - Return an error code.
    -
    - We use these short variable names so we can use the same macros as
    - `regex_compile' itself. */
    -
    -static reg_errcode_t
    -compile_range (p_ptr, pend, translate, syntax, b)
    - const char **p_ptr, *pend;
    - char *translate;
    - reg_syntax_t syntax;
    - unsigned char *b;
    -{
    - unsigned this_char;
    -
    - const char *p = *p_ptr;
    - int range_start, range_end;
    -
    - if (p == pend)
    - return REG_ERANGE;
    -
    - /* Even though the pattern is a signed `char *', we need to fetch
    - with unsigned char *'s; if the high bit of the pattern character
    - is set, the range endpoints will be negative if we fetch using a
    - signed char *.
    -
    - We also want to fetch the endpoints without translating them; the
    - appropriate translation is done in the bit-setting loop below. */
    - range_start = ((unsigned char *) p)[-2];
    - range_end = ((unsigned char *) p)[0];
    -
    - /* Have to increment the pointer into the pattern string, so the
    - caller isn't still at the ending character. */
    - (*p_ptr)++;
    -
    - /* If the start is after the end, the range is empty. */
    - if (range_start > range_end)
    - return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
    -
    - /* Here we see why `this_char' has to be larger than an `unsigned
    - char' -- the range is inclusive, so if `range_end' == 0xff
    - (assuming 8-bit characters), we would otherwise go into an infinite
    - loop, since all characters <= 0xff. */
    - for (this_char = range_start; this_char <= range_end; this_char++)
    - {
    - SET_LIST_BIT (TRANSLATE (this_char));
    - }
    -
    - return REG_NOERROR;
    -}
    -
    -/* Failure stack declarations and macros; both re_compile_fastmap and
    - re_match_2 use a failure stack. These have to be macros because of
    - REGEX_ALLOCATE. */
    -
    -
    -/* Number of failure points for which to initially allocate space
    - when matching. If this number is exceeded, we allocate more
    - space, so it is not a hard limit. */
    -#ifndef INIT_FAILURE_ALLOC
    -#define INIT_FAILURE_ALLOC 5
    -#endif
    -
    -/* Roughly the maximum number of failure points on the stack. Would be
    - exactly that if always used MAX_FAILURE_SPACE each time we failed.
    - This is a variable only so users of regex can assign to it; we never
    - change it ourselves. */
    -int re_max_failures = 2000;
    -
    -typedef const unsigned char *fail_stack_elt_t;
    -
    -typedef struct
    -{
    - fail_stack_elt_t *stack;
    - unsigned size;
    - unsigned avail; /* Offset of next open position. */
    -} fail_stack_type;
    -
    -#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
    -#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
    -#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
    -#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
    -
    -
    -/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
    -
    -#define INIT_FAIL_STACK() \
    - do { \
    - fail_stack.stack = (fail_stack_elt_t *) \
    - REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
    - \
    - if (fail_stack.stack == NULL) \
    - return -2; \
    - \
    - fail_stack.size = INIT_FAILURE_ALLOC; \
    - fail_stack.avail = 0; \
    - } while (0)
    -
    -
    -/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
    -
    - Return 1 if succeeds, and 0 if either ran out of memory
    - allocating space for it or it was already too large.
    -
    - REGEX_REALLOCATE requires `destination' be declared. */
    -
    -#define DOUBLE_FAIL_STACK(fail_stack) \
    - ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
    - ? 0 \
    - : ((fail_stack).stack = (fail_stack_elt_t *) \
    - REGEX_REALLOCATE ((fail_stack).stack, \
    - (fail_stack).size * sizeof (fail_stack_elt_t), \
    - ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
    - \
    - (fail_stack).stack == NULL \
    - ? 0 \
    - : ((fail_stack).size <<= 1, \
    - 1)))
    -
    -
    -/* Push PATTERN_OP on FAIL_STACK.
    -
    - Return 1 if was able to do so and 0 if ran out of memory allocating
    - space to do so. */
    -#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
    - ((FAIL_STACK_FULL () \
    - && !DOUBLE_FAIL_STACK (fail_stack)) \
    - ? 0 \
    - : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
    - 1))
    -
    -/* This pushes an item onto the failure stack. Must be a four-byte
    - value. Assumes the variable `fail_stack'. Probably should only
    - be called from within `PUSH_FAILURE_POINT'. */
    -#define PUSH_FAILURE_ITEM(item) \
    - fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
    -
    -/* The complement operation. Assumes `fail_stack' is nonempty. */
    -#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
    -
    -/* Used to omit pushing failure point id's when we're not debugging. */
    -#ifdef DEBUG
    -#define DEBUG_PUSH PUSH_FAILURE_ITEM
    -#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
    -#else
    -#define DEBUG_PUSH(item)
    -#define DEBUG_POP(item_addr)
    -#endif
    -
    -
    -/* Push the information about the state we will need
    - if we ever fail back to it.
    -
    - Requires variables fail_stack, regstart, regend, reg_info, and
    - num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
    - declared.
    -
    - Does `return FAILURE_CODE' if runs out of memory. */
    -
    -#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
    - do { \
    - char *destination; \
    - /* Must be int, so when we don't save any registers, the arithmetic \
    - of 0 + -1 isn't done as unsigned. */ \
    - int this_reg; \
    - \
    - DEBUG_STATEMENT (failure_id++); \
    - DEBUG_STATEMENT (nfailure_points_pushed++); \
    - DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
    - DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
    - DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
    - \
    - DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
    - DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
    - \
    - /* Ensure we have enough space allocated for what we will push. */ \
    - while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
    - { \
    - if (!DOUBLE_FAIL_STACK (fail_stack)) \
    - return failure_code; \
    - \
    - DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
    - (fail_stack).size); \
    - DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
    - } \
    - \
    - /* Push the info, starting with the registers. */ \
    - DEBUG_PRINT1 ("\n"); \
    - \
    - for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
    - this_reg++) \
    - { \
    - DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
    - DEBUG_STATEMENT (num_regs_pushed++); \
    - \
    - DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
    - PUSH_FAILURE_ITEM (regstart[this_reg]); \
    - \
    - DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
    - PUSH_FAILURE_ITEM (regend[this_reg]); \
    - \
    - DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
    - DEBUG_PRINT2 (" match_null=%d", \
    - REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
    - DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
    - DEBUG_PRINT2 (" matched_something=%d", \
    - MATCHED_SOMETHING (reg_info[this_reg])); \
    - DEBUG_PRINT2 (" ever_matched=%d", \
    - EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
    - DEBUG_PRINT1 ("\n"); \
    - PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
    - } \
    - \
    - DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
    - PUSH_FAILURE_ITEM (lowest_active_reg); \
    - \
    - DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
    - PUSH_FAILURE_ITEM (highest_active_reg); \
    - \
    - DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
    - DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
    - PUSH_FAILURE_ITEM (pattern_place); \
    - \
    - DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
    - DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
    - size2); \
    - DEBUG_PRINT1 ("'\n"); \
    - PUSH_FAILURE_ITEM (string_place); \
    - \
    - DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
    - DEBUG_PUSH (failure_id); \
    - } while (0)
    -
    -/* This is the number of items that are pushed and popped on the stack
    - for each register. */
    -#define NUM_REG_ITEMS 3
    -
    -/* Individual items aside from the registers. */
    -#ifdef DEBUG
    -#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
    -#else
    -#define NUM_NONREG_ITEMS 4
    -#endif
    -
    -/* We push at most this many items on the stack. */
    -#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
    -
    -/* We actually push this many items. */
    -#define NUM_FAILURE_ITEMS \
    - ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
    - + NUM_NONREG_ITEMS)
    -
    -/* How many items can still be added to the stack without overflowing it. */
    -#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
    -
    -
    -/* Pops what PUSH_FAIL_STACK pushes.
    -
    - We restore into the parameters, all of which should be lvalues:
    - STR -- the saved data position.
    - PAT -- the saved pattern position.
    - LOW_REG, HIGH_REG -- the highest and lowest active registers.
    - REGSTART, REGEND -- arrays of string positions.
    - REG_INFO -- array of information about each subexpression.
    -
    - Also assumes the variables `fail_stack' and (if debugging), `bufp',
    - `pend', `string1', `size1', `string2', and `size2'. */
    -
    -#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
    -{ \
    - DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
    - int this_reg; \
    - const unsigned char *string_temp; \
    - \
    - assert (!FAIL_STACK_EMPTY ()); \
    - \
    - /* Remove failure points and point to how many regs pushed. */ \
    - DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
    - DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
    - DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
    - \
    - assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
    - \
    - DEBUG_POP (&failure_id); \
    - DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
    - \
    - /* If the saved string location is NULL, it came from an \
    - on_failure_keep_string_jump opcode, and we want to throw away the \
    - saved NULL, thus retaining our current position in the string. */ \
    - string_temp = POP_FAILURE_ITEM (); \
    - if (string_temp != NULL) \
    - str = (const char *) string_temp; \
    - \
    - DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
    - DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
    - DEBUG_PRINT1 ("'\n"); \
    - \
    - pat = (unsigned char *) POP_FAILURE_ITEM (); \
    - DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
    - DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
    - \
    - /* Restore register info. */ \
    - high_reg = (unsigned) POP_FAILURE_ITEM (); \
    - DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
    - \
    - low_reg = (unsigned) POP_FAILURE_ITEM (); \
    - DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
    - \
    - for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
    - { \
    - DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
    - \
    - reg_info[this_reg].word = POP_FAILURE_ITEM (); \
    - DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
    - \
    - regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
    - DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
    - \
    - regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
    - DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
    - } \
    - \
    - DEBUG_STATEMENT (nfailure_points_popped++); \
    -} /* POP_FAILURE_POINT */
    -
    -/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
    - BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
    - characters can start a string that matches the pattern. This fastmap
    - is used by re_search to skip quickly over impossible starting points.
    -
    - The caller must supply the address of a (1 << BYTEWIDTH)-byte data
    - area as BUFP->fastmap.
    -
    - We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
    - the pattern buffer.
    -
    - Returns 0 if we succeed, -2 if an internal error. */
    -
    -int
    -re_compile_fastmap (bufp)
    - struct re_pattern_buffer *bufp;
    -{
    - int j, k;
    - fail_stack_type fail_stack;
    -#ifndef REGEX_MALLOC
    - char *destination;
    -#endif
    - /* We don't push any register information onto the failure stack. */
    - unsigned num_regs = 0;
    -
    - register char *fastmap = bufp->fastmap;
    - unsigned char *pattern = bufp->buffer;
    - unsigned long size = bufp->used;
    - const unsigned char *p = pattern;
    - register unsigned char *pend = pattern + size;
    -
    - /* Assume that each path through the pattern can be null until
    - proven otherwise. We set this false at the bottom of switch
    - statement, to which we get only if a particular path doesn't
    - match the empty string. */
    - boolean path_can_be_null = true;
    -
    - /* We aren't doing a `succeed_n' to begin with. */
    - boolean succeed_n_p = false;
    -
    - assert (fastmap != NULL && p != NULL);
    -
    - INIT_FAIL_STACK ();
    - bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
    - bufp->fastmap_accurate = 1; /* It will be when we're done. */
    - bufp->can_be_null = 0;
    -
    - while (p != pend || !FAIL_STACK_EMPTY ())
    - {
    - if (p == pend)
    - {
    - bufp->can_be_null |= path_can_be_null;
    -
    - /* Reset for next path. */
    - path_can_be_null = true;
    -
    - p = fail_stack.stack[--fail_stack.avail];
    - }
    -
    - /* We should never be about to go beyond the end of the pattern. */
    - assert (p < pend);
    -
    -#ifdef SWITCH_ENUM_BUG
    - switch ((int) ((re_opcode_t) *p++))
    -#else
    - switch ((re_opcode_t) *p++)
    -#endif
    - {
    -
    - /* I guess the idea here is to simply not bother with a fastmap
    - if a backreference is used, since it's too hard to figure out
    - the fastmap for the corresponding group. Setting
    - `can_be_null' stops `re_search_2' from using the fastmap, so
    - that is all we do. */
    - case duplicate:
    - bufp->can_be_null = 1;
    - return 0;
    -
    -
    - /* Following are the cases which match a character. These end
    - with `break'. */
    -
    - case exactn:
    - fastmap[p[1]] = 1;
    - break;
    -
    -
    - case charset:
    - for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
    - if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
    - fastmap[j] = 1;
    - break;
    -
    -
    - case charset_not:
    - /* Chars beyond end of map must be allowed. */
    - for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
    - fastmap[j] = 1;
    -
    - for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
    - if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
    - fastmap[j] = 1;
    - break;
    -
    -
    - case wordchar:
    - for (j = 0; j < (1 << BYTEWIDTH); j++)
    - if (SYNTAX (j) == Sword)
    - fastmap[j] = 1;
    - break;
    -
    -
    - case notwordchar:
    - for (j = 0; j < (1 << BYTEWIDTH); j++)
    - if (SYNTAX (j) != Sword)
    - fastmap[j] = 1;
    - break;
    -
    -
    - case anychar:
    - /* `.' matches anything ... */
    - for (j = 0; j < (1 << BYTEWIDTH); j++)
    - fastmap[j] = 1;
    -
    - /* ... except perhaps newline. */
    - if (!(bufp->syntax & RE_DOT_NEWLINE))
    - fastmap['\n'] = 0;
    -
    - /* Return if we have already set `can_be_null'; if we have,
    - then the fastmap is irrelevant. Something's wrong here. */
    - else if (bufp->can_be_null)
    - return 0;
    -
    - /* Otherwise, have to check alternative paths. */
    - break;
    -
    -
    -#ifdef emacs
    - case syntaxspec:
    - k = *p++;
    - for (j = 0; j < (1 << BYTEWIDTH); j++)
    - if (SYNTAX (j) == (enum syntaxcode) k)
    - fastmap[j] = 1;
    - break;
    -
    -
    - case notsyntaxspec:
    - k = *p++;
    - for (j = 0; j < (1 << BYTEWIDTH); j++)
    - if (SYNTAX (j) != (enum syntaxcode) k)
    - fastmap[j] = 1;
    - break;
    -
    -
    - /* All cases after this match the empty string. These end with
    - `continue'. */
    -
    -
    - case before_dot:
    - case at_dot:
    - case after_dot:
    - continue;
    -#endif /* not emacs */
    -
    -
    - case no_op:
    - case begline:
    - case endline:
    - case begbuf:
    - case endbuf:
    - case wordbound:
    - case notwordbound:
    - case wordbeg:
    - case wordend:
    - case push_dummy_failure:
    - continue;
    -
    -
    - case jump_n:
    - case pop_failure_jump:
    - case maybe_pop_jump:
    - case jump:
    - case jump_past_alt:
    - case dummy_failure_jump:
    - EXTRACT_NUMBER_AND_INCR (j, p);
    - p += j;
    - if (j > 0)
    - continue;
    -
    - /* Jump backward implies we just went through the body of a
    - loop and matched nothing. Opcode jumped to should be
    - `on_failure_jump' or `succeed_n'. Just treat it like an
    - ordinary jump. For a * loop, it has pushed its failure
    - point already; if so, discard that as redundant. */
    - if ((re_opcode_t) *p != on_failure_jump
    - && (re_opcode_t) *p != succeed_n)
    - continue;
    -
    - p++;
    - EXTRACT_NUMBER_AND_INCR (j, p);
    - p += j;
    -
    - /* If what's on the stack is where we are now, pop it. */
    - if (!FAIL_STACK_EMPTY ()
    - && fail_stack.stack[fail_stack.avail - 1] == p)
    - fail_stack.avail--;
    -
    - continue;
    -
    -
    - case on_failure_jump:
    - case on_failure_keep_string_jump:
    - handle_on_failure_jump:
    - EXTRACT_NUMBER_AND_INCR (j, p);
    -
    - /* For some patterns, e.g., `(a?)?', `p+j' here points to the
    - end of the pattern. We don't want to push such a point,
    - since when we restore it above, entering the switch will
    - increment `p' past the end of the pattern. We don't need
    - to push such a point since we obviously won't find any more
    - fastmap entries beyond `pend'. Such a pattern can match
    - the null string, though. */
    - if (p + j < pend)
    - {
    - if (!PUSH_PATTERN_OP (p + j, fail_stack))
    - return -2;
    - }
    - else
    - bufp->can_be_null = 1;
    -
    - if (succeed_n_p)
    - {
    - EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
    - succeed_n_p = false;
    - }
    -
    - continue;
    -
    -
    - case succeed_n:
    - /* Get to the number of times to succeed. */
    - p += 2;
    -
    - /* Increment p past the n for when k != 0. */
    - EXTRACT_NUMBER_AND_INCR (k, p);
    - if (k == 0)
    - {
    - p -= 4;
    - succeed_n_p = true; /* Spaghetti code alert. */
    - goto handle_on_failure_jump;
    - }
    - continue;
    -
    -
    - case set_number_at:
    - p += 4;
    - continue;
    -
    -
    - case start_memory:
    - case stop_memory:
    - p += 2;
    - continue;
    -
    -
    - default:
    - abort (); /* We have listed all the cases. */
    - } /* switch *p++ */
    -
    - /* Getting here means we have found the possible starting
    - characters for one path of the pattern -- and that the empty
    - string does not match. We need not follow this path further.
    - Instead, look at the next alternative (remembered on the
    - stack), or quit if no more. The test at the top of the loop
    - does these things. */
    - path_can_be_null = false;
    - p = pend;
    - } /* while p */
    -
    - /* Set `can_be_null' for the last path (also the first path, if the
    - pattern is empty). */
    - bufp->can_be_null |= path_can_be_null;
    - return 0;
    -} /* re_compile_fastmap */
    -
    -/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
    - ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
    - this memory for recording register information. STARTS and ENDS
    - must be allocated using the malloc library routine, and must each
    - be at least NUM_REGS * sizeof (regoff_t) bytes long.
    -
    - If NUM_REGS == 0, then subsequent matches should allocate their own
    - register data.
    -
    - Unless this function is called, the first search or match using
    - PATTERN_BUFFER will allocate its own register data, without
    - freeing the old data. */
    -
    -void
    -re_set_registers (bufp, regs, num_regs, starts, ends)
    - struct re_pattern_buffer *bufp;
    - struct re_registers *regs;
    - unsigned num_regs;
    - regoff_t *starts, *ends;
    -{
    - if (num_regs)
    - {
    - bufp->regs_allocated = REGS_REALLOCATE;
    - regs->num_regs = num_regs;
    - regs->start = starts;
    - regs->end = ends;
    - }
    - else
    - {
    - bufp->regs_allocated = REGS_UNALLOCATED;
    - regs->num_regs = 0;
    - regs->start = regs->end = (regoff_t) 0;
    - }
    -}
    -
    -/* Searching routines. */
    -
    -/* Like re_search_2, below, but only one string is specified, and
    - doesn't let you say where to stop matching. */
    -
    -int
    -re_search (bufp, string, size, startpos, range, regs)
    - struct re_pattern_buffer *bufp;
    - const char *string;
    - int size, startpos, range;
    - struct re_registers *regs;
    -{
    - return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
    - regs, size);
    -}
    -
    -
    -/* Using the compiled pattern in BUFP->buffer, first tries to match the
    - virtual concatenation of STRING1 and STRING2, starting first at index
    - STARTPOS, then at STARTPOS + 1, and so on.
    -
    - STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
    -
    - RANGE is how far to scan while trying to match. RANGE = 0 means try
    - only at STARTPOS; in general, the last start tried is STARTPOS +
    - RANGE.
    -
    - In REGS, return the indices of the virtual concatenation of STRING1
    - and STRING2 that matched the entire BUFP->buffer and its contained
    - subexpressions.
    -
    - Do not consider matching one past the index STOP in the virtual
    - concatenation of STRING1 and STRING2.
    -
    - We return either the position in the strings at which the match was
    - found, -1 if no match, or -2 if error (such as failure
    - stack overflow). */
    -
    -int
    -re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
    - struct re_pattern_buffer *bufp;
    - const char *string1, *string2;
    - int size1, size2;
    - int startpos;
    - int range;
    - struct re_registers *regs;
    - int stop;
    -{
    - int val;
    - register char *fastmap = bufp->fastmap;
    - register char *translate = bufp->translate;
    - int total_size = size1 + size2;
    - int endpos = startpos + range;
    -
    - /* Check for out-of-range STARTPOS. */
    - if (startpos < 0 || startpos > total_size)
    - return -1;
    -
    - /* Fix up RANGE if it might eventually take us outside
    - the virtual concatenation of STRING1 and STRING2. */
    - if (endpos < -1)
    - range = -1 - startpos;
    - else if (endpos > total_size)
    - range = total_size - startpos;
    -
    - /* If the search isn't to be a backwards one, don't waste time in a
    - search for a pattern that must be anchored. */
    - if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
    - {
    - if (startpos > 0)
    - return -1;
    - else
    - range = 1;
    - }
    -
    - /* Update the fastmap now if not correct already. */
    - if (fastmap && !bufp->fastmap_accurate)
    - if (re_compile_fastmap (bufp) == -2)
    - return -2;
    -
    - /* Loop through the string, looking for a place to start matching. */
    - for (;;)
    - {
    - /* If a fastmap is supplied, skip quickly over characters that
    - cannot be the start of a match. If the pattern can match the
    - null string, however, we don't need to skip characters; we want
    - the first null string. */
    - if (fastmap && startpos < total_size && !bufp->can_be_null)
    - {
    - if (range > 0) /* Searching forwards. */
    - {
    - register const char *d;
    - register int lim = 0;
    - int irange = range;
    -
    - if (startpos < size1 && startpos + range >= size1)
    - lim = range - (size1 - startpos);
    -
    - d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
    -
    - /* Written out as an if-else to avoid testing `translate'
    - inside the loop. */
    - if (translate)
    - while (range > lim
    - && !fastmap[(unsigned char)
    - translate[(unsigned char) *d++]])
    - range--;
    - else
    - while (range > lim && !fastmap[(unsigned char) *d++])
    - range--;
    -
    - startpos += irange - range;
    - }
    - else /* Searching backwards. */
    - {
    - register char c = (size1 == 0 || startpos >= size1
    - ? string2[startpos - size1]
    - : string1[startpos]);
    -
    - if (!fastmap[(unsigned char) TRANSLATE (c)])
    - goto advance;
    - }
    - }
    -
    - /* If can't match the null string, and that's all we have left, fail. */
    - if (range >= 0 && startpos == total_size && fastmap
    - && !bufp->can_be_null)
    - return -1;
    -
    - val = re_match_2 (bufp, string1, size1, string2, size2,
    - startpos, regs, stop);
    - if (val >= 0)
    - return startpos;
    -
    - if (val == -2)
    - return -2;
    -
    - advance:
    - if (!range)
    - break;
    - else if (range > 0)
    - {
    - range--;
    - startpos++;
    - }
    - else
    - {
    - range++;
    - startpos--;
    - }
    - }
    - return -1;
    -} /* re_search_2 */
    -
    -/* Declarations and macros for re_match_2. */
    -
    -static int bcmp_translate ();
    -static boolean alt_match_null_string_p (),
    - common_op_match_null_string_p (),
    - group_match_null_string_p ();
    -
    -/* Structure for per-register (a.k.a. per-group) information.
    - This must not be longer than one word, because we push this value
    - onto the failure stack. Other register information, such as the
    - starting and ending positions (which are addresses), and the list of
    - inner groups (which is a bits list) are maintained in separate
    - variables.
    -
    - We are making a (strictly speaking) nonportable assumption here: that
    - the compiler will pack our bit fields into something that fits into
    - the type of `word', i.e., is something that fits into one item on the
    - failure stack. */
    -typedef union
    -{
    - fail_stack_elt_t word;
    - struct
    - {
    - /* This field is one if this group can match the empty string,
    - zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
    -#define MATCH_NULL_UNSET_VALUE 3
    - unsigned match_null_string_p : 2;
    - unsigned is_active : 1;
    - unsigned matched_something : 1;
    - unsigned ever_matched_something : 1;
    - } bits;
    -} register_info_type;
    -
    -#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
    -#define IS_ACTIVE(R) ((R).bits.is_active)
    -#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
    -#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
    -
    -
    -/* Call this when have matched a real character; it sets `matched' flags
    - for the subexpressions which we are currently inside. Also records
    - that those subexprs have matched. */
    -#define SET_REGS_MATCHED() \
    - do \
    - { \
    - unsigned r; \
    - for (r = lowest_active_reg; r <= highest_active_reg; r++) \
    - { \
    - MATCHED_SOMETHING (reg_info[r]) \
    - = EVER_MATCHED_SOMETHING (reg_info[r]) \
    - = 1; \
    - } \
    - } \
    - while (0)
    -
    -
    -/* This converts PTR, a pointer into one of the search strings `string1'
    - and `string2' into an offset from the beginning of that string. */
    -#define POINTER_TO_OFFSET(ptr) \
    - (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
    -
    -/* Registers are set to a sentinel when they haven't yet matched. */
    -#define REG_UNSET_VALUE ((char *) -1)
    -#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
    -
    -
    -/* Macros for dealing with the split strings in re_match_2. */
    -
    -#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
    -
    -/* Call before fetching a character with *d. This switches over to
    - string2 if necessary. */
    -#define PREFETCH() \
    - while (d == dend) \
    - { \
    - /* End of string2 => fail. */ \
    - if (dend == end_match_2) \
    - goto fail; \
    - /* End of string1 => advance to string2. */ \
    - d = string2; \
    - dend = end_match_2; \
    - }
    -
    -
    -/* Test if at very beginning or at very end of the virtual concatenation
    - of `string1' and `string2'. If only one string, it's `string2'. */
    -#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
    -#define AT_STRINGS_END(d) ((d) == end2)
    -
    -
    -/* Test if D points to a character which is word-constituent. We have
    - two special cases to check for: if past the end of string1, look at
    - the first character in string2; and if before the beginning of
    - string2, look at the last character in string1. */
    -#define WORDCHAR_P(d) \
    - (SYNTAX ((d) == end1 ? *string2 \
    - : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
    - == Sword)
    -
    -/* Test if the character before D and the one at D differ with respect
    - to being word-constituent. */
    -#define AT_WORD_BOUNDARY(d) \
    - (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
    - || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
    -
    -
    -/* Free everything we malloc. */
    -#ifdef REGEX_MALLOC
    -#define FREE_VAR(var) if (var) free (var); var = NULL
    -#define FREE_VARIABLES() \
    - do { \
    - FREE_VAR (fail_stack.stack); \
    - FREE_VAR (regstart); \
    - FREE_VAR (regend); \
    - FREE_VAR (old_regstart); \
    - FREE_VAR (old_regend); \
    - FREE_VAR (best_regstart); \
    - FREE_VAR (best_regend); \
    - FREE_VAR (reg_info); \
    - FREE_VAR (reg_dummy); \
    - FREE_VAR (reg_info_dummy); \
    - } while (0)
    -#else /* not REGEX_MALLOC */
    -/* Some MIPS systems (at least) want this to free alloca'd storage. */
    -#define FREE_VARIABLES() alloca (0)
    -#endif /* not REGEX_MALLOC */
    -
    -
    -/* These values must meet several constraints. They must not be valid
    - register values; since we have a limit of 255 registers (because
    - we use only one byte in the pattern for the register number), we can
    - use numbers larger than 255. They must differ by 1, because of
    - NUM_FAILURE_ITEMS above. And the value for the lowest register must
    - be larger than the value for the highest register, so we do not try
    - to actually save any registers when none are active. */
    -#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
    -#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
    -
    -/* Matching routines. */
    -
    -#ifndef emacs /* Emacs never uses this. */
    -/* re_match is like re_match_2 except it takes only a single string. */
    -
    -int
    -re_match (bufp, string, size, pos, regs)
    - struct re_pattern_buffer *bufp;
    - const char *string;
    - int size, pos;
    - struct re_registers *regs;
    - {
    - return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
    -}
    -#endif /* not emacs */
    -
    -
    -/* re_match_2 matches the compiled pattern in BUFP against the
    - the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
    - and SIZE2, respectively). We start matching at POS, and stop
    - matching at STOP.
    -
    - If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
    - store offsets for the substring each group matched in REGS. See the
    - documentation for exactly how many groups we fill.
    -
    - We return -1 if no match, -2 if an internal error (such as the
    - failure stack overflowing). Otherwise, we return the length of the
    - matched substring. */
    -
    -int
    -re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
    - struct re_pattern_buffer *bufp;
    - const char *string1, *string2;
    - int size1, size2;
    - int pos;
    - struct re_registers *regs;
    - int stop;
    -{
    - /* General temporaries. */
    - int mcnt;
    - unsigned char *p1;
    -
    - /* Just past the end of the corresponding string. */
    - const char *end1, *end2;
    -
    - /* Pointers into string1 and string2, just past the last characters in
    - each to consider matching. */
    - const char *end_match_1, *end_match_2;
    -
    - /* Where we are in the data, and the end of the current string. */
    - const char *d, *dend;
    -
    - /* Where we are in the pattern, and the end of the pattern. */
    - unsigned char *p = bufp->buffer;
    - register unsigned char *pend = p + bufp->used;
    -
    - /* We use this to map every character in the string. */
    - char *translate = bufp->translate;
    -
    - /* Failure point stack. Each place that can handle a failure further
    - down the line pushes a failure point on this stack. It consists of
    - restart, regend, and reg_info for all registers corresponding to
    - the subexpressions we're currently inside, plus the number of such
    - registers, and, finally, two char *'s. The first char * is where
    - to resume scanning the pattern; the second one is where to resume
    - scanning the strings. If the latter is zero, the failure point is
    - a ``dummy''; if a failure happens and the failure point is a dummy,
    - it gets discarded and the next next one is tried. */
    - fail_stack_type fail_stack;
    -#ifdef DEBUG
    - static unsigned failure_id = 0;
    - unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
    -#endif
    -
    - /* We fill all the registers internally, independent of what we
    - return, for use in backreferences. The number here includes
    - an element for register zero. */
    - unsigned num_regs = bufp->re_nsub + 1;
    -
    - /* The currently active registers. */
    - unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
    - unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
    -
    - /* Information on the contents of registers. These are pointers into
    - the input strings; they record just what was matched (on this
    - attempt) by a subexpression part of the pattern, that is, the
    - regnum-th regstart pointer points to where in the pattern we began
    - matching and the regnum-th regend points to right after where we
    - stopped matching the regnum-th subexpression. (The zeroth register
    - keeps track of what the whole pattern matches.) */
    - const char **regstart=NULL, **regend=NULL;
    -
    - /* If a group that's operated upon by a repetition operator fails to
    - match anything, then the register for its start will need to be
    - restored because it will have been set to wherever in the string we
    - are when we last see its open-group operator. Similarly for a
    - register's end. */
    - const char **old_regstart=NULL, **old_regend=NULL;
    -
    - /* The is_active field of reg_info helps us keep track of which (possibly
    - nested) subexpressions we are currently in. The matched_something
    - field of reg_info[reg_num] helps us tell whether or not we have
    - matched any of the pattern so far this time through the reg_num-th
    - subexpression. These two fields get reset each time through any
    - loop their register is in. */
    - register_info_type *reg_info=NULL;
    -
    - /* The following record the register info as found in the above
    - variables when we find a match better than any we've seen before.
    - This happens as we backtrack through the failure points, which in
    - turn happens only if we have not yet matched the entire string. */
    - unsigned best_regs_set = false;
    - const char **best_regstart=NULL, **best_regend=NULL;
    -
    - /* Logically, this is `best_regend[0]'. But we don't want to have to
    - allocate space for that if we're not allocating space for anything
    - else (see below). Also, we never need info about register 0 for
    - any of the other register vectors, and it seems rather a kludge to
    - treat `best_regend' differently than the rest. So we keep track of
    - the end of the best match so far in a separate variable. We
    - initialize this to NULL so that when we backtrack the first time
    - and need to test it, it's not garbage. */
    - const char *match_end = NULL;
    -
    - /* Used when we pop values we don't care about. */
    - const char **reg_dummy=NULL;
    - register_info_type *reg_info_dummy=NULL;
    -
    -#ifdef DEBUG
    - /* Counts the total number of registers pushed. */
    - unsigned num_regs_pushed = 0;
    -#endif
    -
    - DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
    -
    - INIT_FAIL_STACK ();
    -
    - /* Do not bother to initialize all the register variables if there are
    - no groups in the pattern, as it takes a fair amount of time. If
    - there are groups, we include space for register 0 (the whole
    - pattern), even though we never use it, since it simplifies the
    - array indexing. We should fix this. */
    - if (bufp->re_nsub)
    - {
    - regstart = REGEX_TALLOC (num_regs, const char *);
    - regend = REGEX_TALLOC (num_regs, const char *);
    - old_regstart = REGEX_TALLOC (num_regs, const char *);
    - old_regend = REGEX_TALLOC (num_regs, const char *);
    - best_regstart = REGEX_TALLOC (num_regs, const char *);
    - best_regend = REGEX_TALLOC (num_regs, const char *);
    - reg_info = REGEX_TALLOC (num_regs, register_info_type);
    - reg_dummy = REGEX_TALLOC (num_regs, const char *);
    - reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
    -
    - if (!(regstart && regend && old_regstart && old_regend && reg_info
    - && best_regstart && best_regend && reg_dummy && reg_info_dummy))
    - {
    - FREE_VARIABLES ();
    - return -2;
    - }
    - }
    -#ifdef REGEX_MALLOC
    - else
    - {
    - /* We must initialize all our variables to NULL, so that
    - `FREE_VARIABLES' doesn't try to free them. */
    - regstart = regend = old_regstart = old_regend = best_regstart
    - = best_regend = reg_dummy = NULL;
    - reg_info = reg_info_dummy = (register_info_type *) NULL;
    - }
    -#endif /* REGEX_MALLOC */
    -
    - /* The starting position is bogus. */
    - if (pos < 0 || pos > size1 + size2)
    - {
    - FREE_VARIABLES ();
    - return -1;
    - }
    -
    - /* Initialize subexpression text positions to -1 to mark ones that no
    - start_memory/stop_memory has been seen for. Also initialize the
    - register information struct. */
    - for (mcnt = 1; mcnt < num_regs; mcnt++)
    - {
    - regstart[mcnt] = regend[mcnt]
    - = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
    -
    - REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
    - IS_ACTIVE (reg_info[mcnt]) = 0;
    - MATCHED_SOMETHING (reg_info[mcnt]) = 0;
    - EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
    - }
    -
    - /* We move `string1' into `string2' if the latter's empty -- but not if
    - `string1' is null. */
    - if (size2 == 0 && string1 != NULL)
    - {
    - string2 = string1;
    - size2 = size1;
    - string1 = 0;
    - size1 = 0;
    - }
    - end1 = string1 + size1;
    - end2 = string2 + size2;
    -
    - /* Compute where to stop matching, within the two strings. */
    - if (stop <= size1)
    - {
    - end_match_1 = string1 + stop;
    - end_match_2 = string2;
    - }
    - else
    - {
    - end_match_1 = end1;
    - end_match_2 = string2 + stop - size1;
    - }
    -
    - /* `p' scans through the pattern as `d' scans through the data.
    - `dend' is the end of the input string that `d' points within. `d'
    - is advanced into the following input string whenever necessary, but
    - this happens before fetching; therefore, at the beginning of the
    - loop, `d' can be pointing at the end of a string, but it cannot
    - equal `string2'. */
    - if (size1 > 0 && pos <= size1)
    - {
    - d = string1 + pos;
    - dend = end_match_1;
    - }
    - else
    - {
    - d = string2 + pos - size1;
    - dend = end_match_2;
    - }
    -
    - DEBUG_PRINT1 ("The compiled pattern is: ");
    - DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
    - DEBUG_PRINT1 ("The string to match is: `");
    - DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
    - DEBUG_PRINT1 ("'\n");
    -
    - /* This loops over pattern commands. It exits by returning from the
    - function if the match is complete, or it drops through if the match
    - fails at this starting point in the input data. */
    - for (;;)
    - {
    - DEBUG_PRINT2 ("\n0x%x: ", p);
    -
    - if (p == pend)
    - { /* End of pattern means we might have succeeded. */
    - DEBUG_PRINT1 ("end of pattern ... ");
    -
    - /* If we haven't matched the entire string, and we want the
    - longest match, try backtracking. */
    - if (d != end_match_2)
    - {
    - DEBUG_PRINT1 ("backtracking.\n");
    -
    - if (!FAIL_STACK_EMPTY ())
    - { /* More failure points to try. */
    - boolean same_str_p = (FIRST_STRING_P (match_end)
    - == MATCHING_IN_FIRST_STRING);
    -
    - /* If exceeds best match so far, save it. */
    - if (!best_regs_set
    - || (same_str_p && d > match_end)
    - || (!same_str_p && !MATCHING_IN_FIRST_STRING))
    - {
    - best_regs_set = true;
    - match_end = d;
    -
    - DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
    -
    - for (mcnt = 1; mcnt < num_regs; mcnt++)
    - {
    - best_regstart[mcnt] = regstart[mcnt];
    - best_regend[mcnt] = regend[mcnt];
    - }
    - }
    - goto fail;
    - }
    -
    - /* If no failure points, don't restore garbage. */
    - else if (best_regs_set)
    - {
    - restore_best_regs:
    - /* Restore best match. It may happen that `dend ==
    - end_match_1' while the restored d is in string2.
    - For example, the pattern `x.*y.*z' against the
    - strings `x-' and `y-z-', if the two strings are
    - not consecutive in memory. */
    - DEBUG_PRINT1 ("Restoring best registers.\n");
    -
    - d = match_end;
    - dend = ((d >= string1 && d <= end1)
    - ? end_match_1 : end_match_2);
    -
    - for (mcnt = 1; mcnt < num_regs; mcnt++)
    - {
    - regstart[mcnt] = best_regstart[mcnt];
    - regend[mcnt] = best_regend[mcnt];
    - }
    - }
    - } /* d != end_match_2 */
    -
    - DEBUG_PRINT1 ("Accepting match.\n");
    -
    - /* If caller wants register contents data back, do it. */
    - if (regs && !bufp->no_sub)
    - {
    - /* Have the register data arrays been allocated? */
    - if (bufp->regs_allocated == REGS_UNALLOCATED)
    - { /* No. So allocate them with malloc. We need one
    - extra element beyond `num_regs' for the `-1' marker
    - GNU code uses. */
    - regs->num_regs = MAX (RE_NREGS, num_regs + 1);
    - regs->start = TALLOC (regs->num_regs, regoff_t);
    - regs->end = TALLOC (regs->num_regs, regoff_t);
    - if (regs->start == NULL || regs->end == NULL)
    - return -2;
    - bufp->regs_allocated = REGS_REALLOCATE;
    - }
    - else if (bufp->regs_allocated == REGS_REALLOCATE)
    - { /* Yes. If we need more elements than were already
    - allocated, reallocate them. If we need fewer, just
    - leave it alone. */
    - if (regs->num_regs < num_regs + 1)
    - {
    - regs->num_regs = num_regs + 1;
    - RETALLOC (regs->start, regs->num_regs, regoff_t);
    - RETALLOC (regs->end, regs->num_regs, regoff_t);
    - if (regs->start == NULL || regs->end == NULL)
    - return -2;
    - }
    - }
    - else
    - assert (bufp->regs_allocated == REGS_FIXED);
    -
    - /* Convert the pointer data in `regstart' and `regend' to
    - indices. Register zero has to be set differently,
    - since we haven't kept track of any info for it. */
    - if (regs->num_regs > 0)
    - {
    - regs->start[0] = pos;
    - regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
    - : d - string2 + size1);
    - }
    -
    - /* Go through the first `min (num_regs, regs->num_regs)'
    - registers, since that is all we initialized. */
    - for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
    - {
    - if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
    - regs->start[mcnt] = regs->end[mcnt] = -1;
    - else
    - {
    - regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
    - regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
    - }
    - }
    -
    - /* If the regs structure we return has more elements than
    - were in the pattern, set the extra elements to -1. If
    - we (re)allocated the registers, this is the case,
    - because we always allocate enough to have at least one
    - -1 at the end. */
    - for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
    - regs->start[mcnt] = regs->end[mcnt] = -1;
    - } /* regs && !bufp->no_sub */
    -
    - FREE_VARIABLES ();
    - DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
    - nfailure_points_pushed, nfailure_points_popped,
    - nfailure_points_pushed - nfailure_points_popped);
    - DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
    -
    - mcnt = d - pos - (MATCHING_IN_FIRST_STRING
    - ? string1
    - : string2 - size1);
    -
    - DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
    -
    - return mcnt;
    - }
    -
    - /* Otherwise match next pattern command. */
    -#ifdef SWITCH_ENUM_BUG
    - switch ((int) ((re_opcode_t) *p++))
    -#else
    - switch ((re_opcode_t) *p++)
    -#endif
    - {
    - /* Ignore these. Used to ignore the n of succeed_n's which
    - currently have n == 0. */
    - case no_op:
    - DEBUG_PRINT1 ("EXECUTING no_op.\n");
    - break;
    -
    -
    - /* Match the next n pattern characters exactly. The following
    - byte in the pattern defines n, and the n bytes after that
    - are the characters to match. */
    - case exactn:
    - mcnt = *p++;
    - DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
    -
    - /* This is written out as an if-else so we don't waste time
    - testing `translate' inside the loop. */
    - if (translate)
    - {
    - do
    - {
    - PREFETCH ();
    - if (translate[(unsigned char) *d++] != (char) *p++)
    - goto fail;
    - }
    - while (--mcnt);
    - }
    - else
    - {
    - do
    - {
    - PREFETCH ();
    - if (*d++ != (char) *p++) goto fail;
    - }
    - while (--mcnt);
    - }
    - SET_REGS_MATCHED ();
    - break;
    -
    -
    - /* Match any character except possibly a newline or a null. */
    - case anychar:
    - DEBUG_PRINT1 ("EXECUTING anychar.\n");
    -
    - PREFETCH ();
    -
    - if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
    - || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
    - goto fail;
    -
    - SET_REGS_MATCHED ();
    - DEBUG_PRINT2 (" Matched `%d'.\n", *d);
    - d++;
    - break;
    -
    -
    - case charset:
    - case charset_not:
    - {
    - register unsigned char c;
    - boolean not = (re_opcode_t) *(p - 1) == charset_not;
    -
    - DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
    -
    - PREFETCH ();
    - c = TRANSLATE (*d); /* The character to match. */
    -
    - /* Cast to `unsigned' instead of `unsigned char' in case the
    - bit list is a full 32 bytes long. */
    - if (c < (unsigned) (*p * BYTEWIDTH)
    - && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
    - not = !not;
    -
    - p += 1 + *p;
    -
    - if (!not) goto fail;
    -
    - SET_REGS_MATCHED ();
    - d++;
    - break;
    - }
    -
    -
    - /* The beginning of a group is represented by start_memory.
    - The arguments are the register number in the next byte, and the
    - number of groups inner to this one in the next. The text
    - matched within the group is recorded (in the internal
    - registers data structure) under the register number. */
    - case start_memory:
    - DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
    -
    - /* Find out if this group can match the empty string. */
    - p1 = p; /* To send to group_match_null_string_p. */
    -
    - if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
    - REG_MATCH_NULL_STRING_P (reg_info[*p])
    - = group_match_null_string_p (&p1, pend, reg_info);
    -
    - /* Save the position in the string where we were the last time
    - we were at this open-group operator in case the group is
    - operated upon by a repetition operator, e.g., with `(a*)*b'
    - against `ab'; then we want to ignore where we are now in
    - the string in case this attempt to match fails. */
    - old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
    - ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
    - : regstart[*p];
    - DEBUG_PRINT2 (" old_regstart: %d\n",
    - POINTER_TO_OFFSET (old_regstart[*p]));
    -
    - regstart[*p] = d;
    - DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
    -
    - IS_ACTIVE (reg_info[*p]) = 1;
    - MATCHED_SOMETHING (reg_info[*p]) = 0;
    -
    - /* This is the new highest active register. */
    - highest_active_reg = *p;
    -
    - /* If nothing was active before, this is the new lowest active
    - register. */
    - if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
    - lowest_active_reg = *p;
    -
    - /* Move past the register number and inner group count. */
    - p += 2;
    - break;
    -
    -
    - /* The stop_memory opcode represents the end of a group. Its
    - arguments are the same as start_memory's: the register
    - number, and the number of inner groups. */
    - case stop_memory:
    - DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
    -
    - /* We need to save the string position the last time we were at
    - this close-group operator in case the group is operated
    - upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
    - against `aba'; then we want to ignore where we are now in
    - the string in case this attempt to match fails. */
    - old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
    - ? REG_UNSET (regend[*p]) ? d : regend[*p]
    - : regend[*p];
    - DEBUG_PRINT2 (" old_regend: %d\n",
    - POINTER_TO_OFFSET (old_regend[*p]));
    -
    - regend[*p] = d;
    - DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
    -
    - /* This register isn't active anymore. */
    - IS_ACTIVE (reg_info[*p]) = 0;
    -
    - /* If this was the only register active, nothing is active
    - anymore. */
    - if (lowest_active_reg == highest_active_reg)
    - {
    - lowest_active_reg = NO_LOWEST_ACTIVE_REG;
    - highest_active_reg = NO_HIGHEST_ACTIVE_REG;
    - }
    - else
    - { /* We must scan for the new highest active register, since
    - it isn't necessarily one less than now: consider
    - (a(b)c(d(e)f)g). When group 3 ends, after the f), the
    - new highest active register is 1. */
    - unsigned char r = *p - 1;
    - while (r > 0 && !IS_ACTIVE (reg_info[r]))
    - r--;
    -
    - /* If we end up at register zero, that means that we saved
    - the registers as the result of an `on_failure_jump', not
    - a `start_memory', and we jumped to past the innermost
    - `stop_memory'. For example, in ((.)*) we save
    - registers 1 and 2 as a result of the *, but when we pop
    - back to the second ), we are at the stop_memory 1.
    - Thus, nothing is active. */
    - if (r == 0)
    - {
    - lowest_active_reg = NO_LOWEST_ACTIVE_REG;
    - highest_active_reg = NO_HIGHEST_ACTIVE_REG;
    - }
    - else
    - highest_active_reg = r;
    - }
    -
    - /* If just failed to match something this time around with a
    - group that's operated on by a repetition operator, try to
    - force exit from the ``loop'', and restore the register
    - information for this group that we had before trying this
    - last match. */
    - if ((!MATCHED_SOMETHING (reg_info[*p])
    - || (re_opcode_t) p[-3] == start_memory)
    - && (p + 2) < pend)
    - {
    - boolean is_a_jump_n = false;
    -
    - p1 = p + 2;
    - mcnt = 0;
    - switch ((re_opcode_t) *p1++)
    - {
    - case jump_n:
    - is_a_jump_n = true;
    - case pop_failure_jump:
    - case maybe_pop_jump:
    - case jump:
    - case dummy_failure_jump:
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    - if (is_a_jump_n)
    - p1 += 2;
    - break;
    -
    - default:
    - /* do nothing */ ;
    - }
    - p1 += mcnt;
    -
    - /* If the next operation is a jump backwards in the pattern
    - to an on_failure_jump right before the start_memory
    - corresponding to this stop_memory, exit from the loop
    - by forcing a failure after pushing on the stack the
    - on_failure_jump's jump in the pattern, and d. */
    - if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
    - && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
    - {
    - /* If this group ever matched anything, then restore
    - what its registers were before trying this last
    - failed match, e.g., with `(a*)*b' against `ab' for
    - regstart[1], and, e.g., with `((a*)*(b*)*)*'
    - against `aba' for regend[3].
    -
    - Also restore the registers for inner groups for,
    - e.g., `((a*)(b*))*' against `aba' (register 3 would
    - otherwise get trashed). */
    -
    - if (EVER_MATCHED_SOMETHING (reg_info[*p]))
    - {
    - unsigned r;
    -
    - EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
    -
    - /* Restore this and inner groups' (if any) registers. */
    - for (r = *p; r < *p + *(p + 1); r++)
    - {
    - regstart[r] = old_regstart[r];
    -
    - /* xx why this test? */
    - if ((int) old_regend[r] >= (int) regstart[r])
    - regend[r] = old_regend[r];
    - }
    - }
    - p1++;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    - PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
    -
    - goto fail;
    - }
    - }
    -
    - /* Move past the register number and the inner group count. */
    - p += 2;
    - break;
    -
    -
    - /* \<digit> has been turned into a `duplicate' command which is
    - followed by the numeric value of <digit> as the register number. */
    - case duplicate:
    - {
    - register const char *d2, *dend2;
    - int regno = *p++; /* Get which register to match against. */
    - DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
    -
    - /* Can't back reference a group which we've never matched. */
    - if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
    - goto fail;
    -
    - /* Where in input to try to start matching. */
    - d2 = regstart[regno];
    -
    - /* Where to stop matching; if both the place to start and
    - the place to stop matching are in the same string, then
    - set to the place to stop, otherwise, for now have to use
    - the end of the first string. */
    -
    - dend2 = ((FIRST_STRING_P (regstart[regno])
    - == FIRST_STRING_P (regend[regno]))
    - ? regend[regno] : end_match_1);
    - for (;;)
    - {
    - /* If necessary, advance to next segment in register
    - contents. */
    - while (d2 == dend2)
    - {
    - if (dend2 == end_match_2) break;
    - if (dend2 == regend[regno]) break;
    -
    - /* End of string1 => advance to string2. */
    - d2 = string2;
    - dend2 = regend[regno];
    - }
    - /* At end of register contents => success */
    - if (d2 == dend2) break;
    -
    - /* If necessary, advance to next segment in data. */
    - PREFETCH ();
    -
    - /* How many characters left in this segment to match. */
    - mcnt = dend - d;
    -
    - /* Want how many consecutive characters we can match in
    - one shot, so, if necessary, adjust the count. */
    - if (mcnt > dend2 - d2)
    - mcnt = dend2 - d2;
    -
    - /* Compare that many; failure if mismatch, else move
    - past them. */
    - if (translate
    - ? bcmp_translate (d, d2, mcnt, translate)
    - : bcmp (d, d2, mcnt))
    - goto fail;
    - d += mcnt, d2 += mcnt;
    - }
    - }
    - break;
    -
    -
    - /* begline matches the empty string at the beginning of the string
    - (unless `not_bol' is set in `bufp'), and, if
    - `newline_anchor' is set, after newlines. */
    - case begline:
    - DEBUG_PRINT1 ("EXECUTING begline.\n");
    -
    - if (AT_STRINGS_BEG (d))
    - {
    - if (!bufp->not_bol) break;
    - }
    - else if (d[-1] == '\n' && bufp->newline_anchor)
    - {
    - break;
    - }
    - /* In all other cases, we fail. */
    - goto fail;
    -
    -
    - /* endline is the dual of begline. */
    - case endline:
    - DEBUG_PRINT1 ("EXECUTING endline.\n");
    -
    - if (AT_STRINGS_END (d))
    - {
    - if (!bufp->not_eol) break;
    - }
    -
    - /* We have to ``prefetch'' the next character. */
    - else if ((d == end1 ? *string2 : *d) == '\n'
    - && bufp->newline_anchor)
    - {
    - break;
    - }
    - goto fail;
    -
    -
    - /* Match at the very beginning of the data. */
    - case begbuf:
    - DEBUG_PRINT1 ("EXECUTING begbuf.\n");
    - if (AT_STRINGS_BEG (d))
    - break;
    - goto fail;
    -
    -
    - /* Match at the very end of the data. */
    - case endbuf:
    - DEBUG_PRINT1 ("EXECUTING endbuf.\n");
    - if (AT_STRINGS_END (d))
    - break;
    - goto fail;
    -
    -
    - /* on_failure_keep_string_jump is used to optimize `.*\n'. It
    - pushes NULL as the value for the string on the stack. Then
    - `pop_failure_point' will keep the current value for the
    - string, instead of restoring it. To see why, consider
    - matching `foo\nbar' against `.*\n'. The .* matches the foo;
    - then the . fails against the \n. But the next thing we want
    - to do is match the \n against the \n; if we restored the
    - string value, we would be back at the foo.
    -
    - Because this is used only in specific cases, we don't need to
    - check all the things that `on_failure_jump' does, to make
    - sure the right things get saved on the stack. Hence we don't
    - share its code. The only reason to push anything on the
    - stack at all is that otherwise we would have to change
    - `anychar's code to do something besides goto fail in this
    - case; that seems worse than this. */
    - case on_failure_keep_string_jump:
    - DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
    -
    - EXTRACT_NUMBER_AND_INCR (mcnt, p);
    - DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
    -
    - PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
    - break;
    -
    -
    - /* Uses of on_failure_jump:
    -
    - Each alternative starts with an on_failure_jump that points
    - to the beginning of the next alternative. Each alternative
    - except the last ends with a jump that in effect jumps past
    - the rest of the alternatives. (They really jump to the
    - ending jump of the following alternative, because tensioning
    - these jumps is a hassle.)
    -
    - Repeats start with an on_failure_jump that points past both
    - the repetition text and either the following jump or
    - pop_failure_jump back to this on_failure_jump. */
    - case on_failure_jump:
    - on_failure:
    - DEBUG_PRINT1 ("EXECUTING on_failure_jump");
    -
    - EXTRACT_NUMBER_AND_INCR (mcnt, p);
    - DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
    -
    - /* If this on_failure_jump comes right before a group (i.e.,
    - the original * applied to a group), save the information
    - for that group and all inner ones, so that if we fail back
    - to this point, the group's information will be correct.
    - For example, in \(a*\)*\1, we need the preceding group,
    - and in \(\(a*\)b*\)\2, we need the inner group. */
    -
    - /* We can't use `p' to check ahead because we push
    - a failure point to `p + mcnt' after we do this. */
    - p1 = p;
    -
    - /* We need to skip no_op's before we look for the
    - start_memory in case this on_failure_jump is happening as
    - the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
    - against aba. */
    - while (p1 < pend && (re_opcode_t) *p1 == no_op)
    - p1++;
    -
    - if (p1 < pend && (re_opcode_t) *p1 == start_memory)
    - {
    - /* We have a new highest active register now. This will
    - get reset at the start_memory we are about to get to,
    - but we will have saved all the registers relevant to
    - this repetition op, as described above. */
    - highest_active_reg = *(p1 + 1) + *(p1 + 2);
    - if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
    - lowest_active_reg = *(p1 + 1);
    - }
    -
    - DEBUG_PRINT1 (":\n");
    - PUSH_FAILURE_POINT (p + mcnt, d, -2);
    - break;
    -
    -
    - /* A smart repeat ends with `maybe_pop_jump'.
    - We change it to either `pop_failure_jump' or `jump'. */
    - case maybe_pop_jump:
    - EXTRACT_NUMBER_AND_INCR (mcnt, p);
    - DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
    - {
    - register unsigned char *p2 = p;
    -
    - /* Compare the beginning of the repeat with what in the
    - pattern follows its end. If we can establish that there
    - is nothing that they would both match, i.e., that we
    - would have to backtrack because of (as in, e.g., `a*a')
    - then we can change to pop_failure_jump, because we'll
    - never have to backtrack.
    -
    - This is not true in the case of alternatives: in
    - `(a|ab)*' we do need to backtrack to the `ab' alternative
    - (e.g., if the string was `ab'). But instead of trying to
    - detect that here, the alternative has put on a dummy
    - failure point which is what we will end up popping. */
    -
    - /* Skip over open/close-group commands. */
    - while (p2 + 2 < pend
    - && ((re_opcode_t) *p2 == stop_memory
    - || (re_opcode_t) *p2 == start_memory))
    - p2 += 3; /* Skip over args, too. */
    -
    - /* If we're at the end of the pattern, we can change. */
    - if (p2 == pend)
    - {
    - /* Consider what happens when matching ":\(.*\)"
    - against ":/". I don't really understand this code
    - yet. */
    - p[-3] = (unsigned char) pop_failure_jump;
    - DEBUG_PRINT1
    - (" End of pattern: change to `pop_failure_jump'.\n");
    - }
    -
    - else if ((re_opcode_t) *p2 == exactn
    - || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
    - {
    - register unsigned char c
    - = *p2 == (unsigned char) endline ? '\n' : p2[2];
    - p1 = p + mcnt;
    -
    - /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
    - to the `maybe_finalize_jump' of this case. Examine what
    - follows. */
    - if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
    - {
    - p[-3] = (unsigned char) pop_failure_jump;
    - DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
    - c, p1[5]);
    - }
    -
    - else if ((re_opcode_t) p1[3] == charset
    - || (re_opcode_t) p1[3] == charset_not)
    - {
    - int not = (re_opcode_t) p1[3] == charset_not;
    -
    - if (c < (unsigned char) (p1[4] * BYTEWIDTH)
    - && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
    - not = !not;
    -
    - /* `not' is equal to 1 if c would match, which means
    - that we can't change to pop_failure_jump. */
    - if (!not)
    - {
    - p[-3] = (unsigned char) pop_failure_jump;
    - DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
    - }
    - }
    - }
    - }
    - p -= 2; /* Point at relative address again. */
    - if ((re_opcode_t) p[-1] != pop_failure_jump)
    - {
    - p[-1] = (unsigned char) jump;
    - DEBUG_PRINT1 (" Match => jump.\n");
    - goto unconditional_jump;
    - }
    - /* Note fall through. */
    -
    -
    - /* The end of a simple repeat has a pop_failure_jump back to
    - its matching on_failure_jump, where the latter will push a
    - failure point. The pop_failure_jump takes off failure
    - points put on by this pop_failure_jump's matching
    - on_failure_jump; we got through the pattern to here from the
    - matching on_failure_jump, so didn't fail. */
    - case pop_failure_jump:
    - {
    - /* We need to pass separate storage for the lowest and
    - highest registers, even though we don't care about the
    - actual values. Otherwise, we will restore only one
    - register from the stack, since lowest will == highest in
    - `pop_failure_point'. */
    - unsigned dummy_low_reg, dummy_high_reg;
    - unsigned char *pdummy;
    - const char *sdummy;
    -
    - DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
    - POP_FAILURE_POINT (sdummy, pdummy,
    - dummy_low_reg, dummy_high_reg,
    - reg_dummy, reg_dummy, reg_info_dummy);
    - }
    - /* Note fall through. */
    -
    -
    - /* Unconditionally jump (without popping any failure points). */
    - case jump:
    - unconditional_jump:
    - EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
    - DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
    - p += mcnt; /* Do the jump. */
    - DEBUG_PRINT2 ("(to 0x%x).\n", p);
    - break;
    -
    -
    - /* We need this opcode so we can detect where alternatives end
    - in `group_match_null_string_p' et al. */
    - case jump_past_alt:
    - DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
    - goto unconditional_jump;
    -
    -
    - /* Normally, the on_failure_jump pushes a failure point, which
    - then gets popped at pop_failure_jump. We will end up at
    - pop_failure_jump, also, and with a pattern of, say, `a+', we
    - are skipping over the on_failure_jump, so we have to push
    - something meaningless for pop_failure_jump to pop. */
    - case dummy_failure_jump:
    - DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
    - /* It doesn't matter what we push for the string here. What
    - the code at `fail' tests is the value for the pattern. */
    - PUSH_FAILURE_POINT (0, 0, -2);
    - goto unconditional_jump;
    -
    -
    - /* At the end of an alternative, we need to push a dummy failure
    - point in case we are followed by a `pop_failure_jump', because
    - we don't want the failure point for the alternative to be
    - popped. For example, matching `(a|ab)*' against `aab'
    - requires that we match the `ab' alternative. */
    - case push_dummy_failure:
    - DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
    - /* See comments just above at `dummy_failure_jump' about the
    - two zeroes. */
    - PUSH_FAILURE_POINT (0, 0, -2);
    - break;
    -
    - /* Have to succeed matching what follows at least n times.
    - After that, handle like `on_failure_jump'. */
    - case succeed_n:
    - EXTRACT_NUMBER (mcnt, p + 2);
    - DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
    -
    - assert (mcnt >= 0);
    - /* Originally, this is how many times we HAVE to succeed. */
    - if (mcnt > 0)
    - {
    - mcnt--;
    - p += 2;
    - STORE_NUMBER_AND_INCR (p, mcnt);
    - DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt);
    - }
    - else if (mcnt == 0)
    - {
    - DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
    - p[2] = (unsigned char) no_op;
    - p[3] = (unsigned char) no_op;
    - goto on_failure;
    - }
    - break;
    -
    - case jump_n:
    - EXTRACT_NUMBER (mcnt, p + 2);
    - DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
    -
    - /* Originally, this is how many times we CAN jump. */
    - if (mcnt)
    - {
    - mcnt--;
    - STORE_NUMBER (p + 2, mcnt);
    - goto unconditional_jump;
    - }
    - /* If don't have to jump any more, skip over the rest of command. */
    - else
    - p += 4;
    - break;
    -
    - case set_number_at:
    - {
    - DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
    -
    - EXTRACT_NUMBER_AND_INCR (mcnt, p);
    - p1 = p + mcnt;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p);
    - DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt);
    - STORE_NUMBER (p1, mcnt);
    - break;
    - }
    -
    - case wordbound:
    - DEBUG_PRINT1 ("EXECUTING wordbound.\n");
    - if (AT_WORD_BOUNDARY (d))
    - break;
    - goto fail;
    -
    - case notwordbound:
    - DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
    - if (AT_WORD_BOUNDARY (d))
    - goto fail;
    - break;
    -
    - case wordbeg:
    - DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
    - if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
    - break;
    - goto fail;
    -
    - case wordend:
    - DEBUG_PRINT1 ("EXECUTING wordend.\n");
    - if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
    - && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
    - break;
    - goto fail;
    -
    -#ifdef emacs
    -#ifdef emacs19
    - case before_dot:
    - DEBUG_PRINT1 ("EXECUTING before_dot.\n");
    - if (PTR_CHAR_POS ((unsigned char *) d) >= point)
    - goto fail;
    - break;
    -
    - case at_dot:
    - DEBUG_PRINT1 ("EXECUTING at_dot.\n");
    - if (PTR_CHAR_POS ((unsigned char *) d) != point)
    - goto fail;
    - break;
    -
    - case after_dot:
    - DEBUG_PRINT1 ("EXECUTING after_dot.\n");
    - if (PTR_CHAR_POS ((unsigned char *) d) <= point)
    - goto fail;
    - break;
    -#else /* not emacs19 */
    - case at_dot:
    - DEBUG_PRINT1 ("EXECUTING at_dot.\n");
    - if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
    - goto fail;
    - break;
    -#endif /* not emacs19 */
    -
    - case syntaxspec:
    - DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
    - mcnt = *p++;
    - goto matchsyntax;
    -
    - case wordchar:
    - DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
    - mcnt = (int) Sword;
    - matchsyntax:
    - PREFETCH ();
    - if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
    - goto fail;
    - SET_REGS_MATCHED ();
    - break;
    -
    - case notsyntaxspec:
    - DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
    - mcnt = *p++;
    - goto matchnotsyntax;
    -
    - case notwordchar:
    - DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
    - mcnt = (int) Sword;
    - matchnotsyntax:
    - PREFETCH ();
    - if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
    - goto fail;
    - SET_REGS_MATCHED ();
    - break;
    -
    -#else /* not emacs */
    - case wordchar:
    - DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
    - PREFETCH ();
    - if (!WORDCHAR_P (d))
    - goto fail;
    - SET_REGS_MATCHED ();
    - d++;
    - break;
    -
    - case notwordchar:
    - DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
    - PREFETCH ();
    - if (WORDCHAR_P (d))
    - goto fail;
    - SET_REGS_MATCHED ();
    - d++;
    - break;
    -#endif /* not emacs */
    -
    - default:
    - abort ();
    - }
    - continue; /* Successfully executed one pattern command; keep going. */
    -
    -
    - /* We goto here if a matching operation fails. */
    - fail:
    - if (!FAIL_STACK_EMPTY ())
    - { /* A restart point is known. Restore to that state. */
    - DEBUG_PRINT1 ("\nFAIL:\n");
    - POP_FAILURE_POINT (d, p,
    - lowest_active_reg, highest_active_reg,
    - regstart, regend, reg_info);
    -
    - /* If this failure point is a dummy, try the next one. */
    - if (!p)
    - goto fail;
    -
    - /* If we failed to the end of the pattern, don't examine *p. */
    - assert (p <= pend);
    - if (p < pend)
    - {
    - boolean is_a_jump_n = false;
    -
    - /* If failed to a backwards jump that's part of a repetition
    - loop, need to pop this failure point and use the next one. */
    - switch ((re_opcode_t) *p)
    - {
    - case jump_n:
    - is_a_jump_n = true;
    - case maybe_pop_jump:
    - case pop_failure_jump:
    - case jump:
    - p1 = p + 1;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    - p1 += mcnt;
    -
    - if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
    - || (!is_a_jump_n
    - && (re_opcode_t) *p1 == on_failure_jump))
    - goto fail;
    - break;
    - default:
    - /* do nothing */ ;
    - }
    - }
    -
    - if (d >= string1 && d <= end1)
    - dend = end_match_1;
    - }
    - else
    - break; /* Matching at this starting point really fails. */
    - } /* for (;;) */
    -
    - if (best_regs_set)
    - goto restore_best_regs;
    -
    - FREE_VARIABLES ();
    -
    - return -1; /* Failure to match. */
    -} /* re_match_2 */
    -
    -/* Subroutine definitions for re_match_2. */
    -
    -
    -/* We are passed P pointing to a register number after a start_memory.
    -
    - Return true if the pattern up to the corresponding stop_memory can
    - match the empty string, and false otherwise.
    -
    - If we find the matching stop_memory, sets P to point to one past its number.
    - Otherwise, sets P to an undefined byte less than or equal to END.
    -
    - We don't handle duplicates properly (yet). */
    -
    -static boolean
    -group_match_null_string_p (p, end, reg_info)
    - unsigned char **p, *end;
    - register_info_type *reg_info;
    -{
    - int mcnt;
    - /* Point to after the args to the start_memory. */
    - unsigned char *p1 = *p + 2;
    -
    - while (p1 < end)
    - {
    - /* Skip over opcodes that can match nothing, and return true or
    - false, as appropriate, when we get to one that can't, or to the
    - matching stop_memory. */
    -
    - switch ((re_opcode_t) *p1)
    - {
    - /* Could be either a loop or a series of alternatives. */
    - case on_failure_jump:
    - p1++;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    -
    - /* If the next operation is not a jump backwards in the
    - pattern. */
    -
    - if (mcnt >= 0)
    - {
    - /* Go through the on_failure_jumps of the alternatives,
    - seeing if any of the alternatives cannot match nothing.
    - The last alternative starts with only a jump,
    - whereas the rest start with on_failure_jump and end
    - with a jump, e.g., here is the pattern for `a|b|c':
    -
    - /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
    - /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
    - /exactn/1/c
    -
    - So, we have to first go through the first (n-1)
    - alternatives and then deal with the last one separately. */
    -
    -
    - /* Deal with the first (n-1) alternatives, which start
    - with an on_failure_jump (see above) that jumps to right
    - past a jump_past_alt. */
    -
    - while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
    - {
    - /* `mcnt' holds how many bytes long the alternative
    - is, including the ending `jump_past_alt' and
    - its number. */
    -
    - if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
    - reg_info))
    - return false;
    -
    - /* Move to right after this alternative, including the
    - jump_past_alt. */
    - p1 += mcnt;
    -
    - /* Break if it's the beginning of an n-th alternative
    - that doesn't begin with an on_failure_jump. */
    - if ((re_opcode_t) *p1 != on_failure_jump)
    - break;
    -
    - /* Still have to check that it's not an n-th
    - alternative that starts with an on_failure_jump. */
    - p1++;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    - if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
    - {
    - /* Get to the beginning of the n-th alternative. */
    - p1 -= 3;
    - break;
    - }
    - }
    -
    - /* Deal with the last alternative: go back and get number
    - of the `jump_past_alt' just before it. `mcnt' contains
    - the length of the alternative. */
    - EXTRACT_NUMBER (mcnt, p1 - 2);
    -
    - if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
    - return false;
    -
    - p1 += mcnt; /* Get past the n-th alternative. */
    - } /* if mcnt > 0 */
    - break;
    -
    -
    - case stop_memory:
    - assert (p1[1] == **p);
    - *p = p1 + 2;
    - return true;
    -
    -
    - default:
    - if (!common_op_match_null_string_p (&p1, end, reg_info))
    - return false;
    - }
    - } /* while p1 < end */
    -
    - return false;
    -} /* group_match_null_string_p */
    -
    -
    -/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
    - It expects P to be the first byte of a single alternative and END one
    - byte past the last. The alternative can contain groups. */
    -
    -static boolean
    -alt_match_null_string_p (p, end, reg_info)
    - unsigned char *p, *end;
    - register_info_type *reg_info;
    -{
    - int mcnt;
    - unsigned char *p1 = p;
    -
    - while (p1 < end)
    - {
    - /* Skip over opcodes that can match nothing, and break when we get
    - to one that can't. */
    -
    - switch ((re_opcode_t) *p1)
    - {
    - /* It's a loop. */
    - case on_failure_jump:
    - p1++;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    - p1 += mcnt;
    - break;
    -
    - default:
    - if (!common_op_match_null_string_p (&p1, end, reg_info))
    - return false;
    - }
    - } /* while p1 < end */
    -
    - return true;
    -} /* alt_match_null_string_p */
    -
    -
    -/* Deals with the ops common to group_match_null_string_p and
    - alt_match_null_string_p.
    -
    - Sets P to one after the op and its arguments, if any. */
    -
    -static boolean
    -common_op_match_null_string_p (p, end, reg_info)
    - unsigned char **p, *end;
    - register_info_type *reg_info;
    -{
    - int mcnt;
    - boolean ret;
    - int reg_no;
    - unsigned char *p1 = *p;
    -
    - switch ((re_opcode_t) *p1++)
    - {
    - case no_op:
    - case begline:
    - case endline:
    - case begbuf:
    - case endbuf:
    - case wordbeg:
    - case wordend:
    - case wordbound:
    - case notwordbound:
    -#ifdef emacs
    - case before_dot:
    - case at_dot:
    - case after_dot:
    -#endif
    - break;
    -
    - case start_memory:
    - reg_no = *p1;
    - assert (reg_no > 0 && reg_no <= MAX_REGNUM);
    - ret = group_match_null_string_p (&p1, end, reg_info);
    -
    - /* Have to set this here in case we're checking a group which
    - contains a group and a back reference to it. */
    -
    - if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
    - REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
    -
    - if (!ret)
    - return false;
    - break;
    -
    - /* If this is an optimized succeed_n for zero times, make the jump. */
    - case jump:
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    - if (mcnt >= 0)
    - p1 += mcnt;
    - else
    - return false;
    - break;
    -
    - case succeed_n:
    - /* Get to the number of times to succeed. */
    - p1 += 2;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    -
    - if (mcnt == 0)
    - {
    - p1 -= 4;
    - EXTRACT_NUMBER_AND_INCR (mcnt, p1);
    - p1 += mcnt;
    - }
    - else
    - return false;
    - break;
    -
    - case duplicate:
    - if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
    - return false;
    - break;
    -
    - case set_number_at:
    - p1 += 4;
    -
    - default:
    - /* All other opcodes mean we cannot match the empty string. */
    - return false;
    - }
    -
    - *p = p1;
    - return true;
    -} /* common_op_match_null_string_p */
    -
    -
    -/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
    - bytes; nonzero otherwise. */
    -
    -static int
    -bcmp_translate (s1, s2, len, translate)
    - unsigned char *s1, *s2;
    - register int len;
    - char *translate;
    -{
    - register unsigned char *p1 = s1, *p2 = s2;
    - while (len)
    - {
    - if (translate[*p1++] != translate[*p2++]) return 1;
    - len--;
    - }
    - return 0;
    -}
    -
    -/* Entry points for GNU code. */
    -
    -/* re_compile_pattern is the GNU regular expression compiler: it
    - compiles PATTERN (of length SIZE) and puts the result in BUFP.
    - Returns 0 if the pattern was valid, otherwise an error string.
    -
    - Assumes the `allocated' (and perhaps `buffer') and `translate' fields
    - are set in BUFP on entry.
    -
    - We call regex_compile to do the actual compilation. */
    -
    -const char *
    -re_compile_pattern (pattern, length, bufp)
    - const char *pattern;
    - int length;
    - struct re_pattern_buffer *bufp;
    -{
    - reg_errcode_t ret;
    -
    - /* GNU code is written to assume at least RE_NREGS registers will be set
    - (and at least one extra will be -1). */
    - bufp->regs_allocated = REGS_UNALLOCATED;
    -
    - /* And GNU code determines whether or not to get register information
    - by passing null for the REGS argument to re_match, etc., not by
    - setting no_sub. */
    - bufp->no_sub = 0;
    -
    - /* Match anchors at newline. */
    - bufp->newline_anchor = 1;
    -
    - ret = regex_compile (pattern, length, re_syntax_options, bufp);
    -
    - return re_error_msg[(int) ret];
    -}
    -
    -/* Entry points compatible with 4.2 BSD regex library. We don't define
    - them if this is an Emacs or POSIX compilation. */
    -
    -#if !defined (emacs) && !defined (_POSIX_SOURCE)
    -
    -/* BSD has one and only one pattern buffer. */
    -static struct re_pattern_buffer re_comp_buf;
    -
    -char *
    -re_comp (s)
    - const char *s;
    -{
    - reg_errcode_t ret;
    -
    - if (!s)
    - {
    - if (!re_comp_buf.buffer)
    - return "No previous regular expression";
    - return 0;
    - }
    -
    - if (!re_comp_buf.buffer)
    - {
    - re_comp_buf.buffer = (unsigned char *) malloc (200);
    - if (re_comp_buf.buffer == NULL)
    - return "Memory exhausted";
    - re_comp_buf.allocated = 200;
    -
    - re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
    - if (re_comp_buf.fastmap == NULL)
    - return "Memory exhausted";
    - }
    -
    - /* Since `re_exec' always passes NULL for the `regs' argument, we
    - don't need to initialize the pattern buffer fields which affect it. */
    -
    - /* Match anchors at newlines. */
    - re_comp_buf.newline_anchor = 1;
    -
    - ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
    -
    - /* Yes, we're discarding `const' here. */
    - return (char *) re_error_msg[(int) ret];
    -}
    -
    -
    -int
    -re_exec (s)
    - const char *s;
    -{
    - const int len = strlen (s);
    - return
    - 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
    -}
    -#endif /* not emacs and not _POSIX_SOURCE */
    -
    -/* POSIX.2 functions. Don't define these for Emacs. */
    -
    -#ifndef emacs
    -
    -/* regcomp takes a regular expression as a string and compiles it.
    -
    - PREG is a regex_t *. We do not expect any fields to be initialized,
    - since POSIX says we shouldn't. Thus, we set
    -
    - `buffer' to the compiled pattern;
    - `used' to the length of the compiled pattern;
    - `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
    - REG_EXTENDED bit in CFLAGS is set; otherwise, to
    - RE_SYNTAX_POSIX_BASIC;
    - `newline_anchor' to REG_NEWLINE being set in CFLAGS;
    - `fastmap' and `fastmap_accurate' to zero;
    - `re_nsub' to the number of subexpressions in PATTERN.
    -
    - PATTERN is the address of the pattern string.
    -
    - CFLAGS is a series of bits which affect compilation.
    -
    - If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
    - use POSIX basic syntax.
    -
    - If REG_NEWLINE is set, then . and [^...] don't match newline.
    - Also, regexec will try a match beginning after every newline.
    -
    - If REG_ICASE is set, then we considers upper- and lowercase
    - versions of letters to be equivalent when matching.
    -
    - If REG_NOSUB is set, then when PREG is passed to regexec, that
    - routine will report only success or failure, and nothing about the
    - registers.
    -
    - It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
    - the return codes and their meanings.) */
    -
    -int
    -regcomp (preg, pattern, cflags)
    - regex_t *preg;
    - const char *pattern;
    - int cflags;
    -{
    - reg_errcode_t ret;
    - unsigned syntax
    - = (cflags & REG_EXTENDED) ?
    - RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
    -
    - /* regex_compile will allocate the space for the compiled pattern. */
    - preg->buffer = 0;
    - preg->allocated = 0;
    -
    - /* Don't bother to use a fastmap when searching. This simplifies the
    - REG_NEWLINE case: if we used a fastmap, we'd have to put all the
    - characters after newlines into the fastmap. This way, we just try
    - every character. */
    - preg->fastmap = 0;
    -
    - if (cflags & REG_ICASE)
    - {
    - unsigned i;
    -
    - preg->translate = (char *) malloc (CHAR_SET_SIZE);
    - if (preg->translate == NULL)
    - return (int) REG_ESPACE;
    -
    - /* Map uppercase characters to corresponding lowercase ones. */
    - for (i = 0; i < CHAR_SET_SIZE; i++)
    - preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
    - }
    - else
    - preg->translate = NULL;
    -
    - /* If REG_NEWLINE is set, newlines are treated differently. */
    - if (cflags & REG_NEWLINE)
    - { /* REG_NEWLINE implies neither . nor [^...] match newline. */
    - syntax &= ~RE_DOT_NEWLINE;
    - syntax |= RE_HAT_LISTS_NOT_NEWLINE;
    - /* It also changes the matching behavior. */
    - preg->newline_anchor = 1;
    - }
    - else
    - preg->newline_anchor = 0;
    -
    - preg->no_sub = !!(cflags & REG_NOSUB);
    -
    - /* POSIX says a null character in the pattern terminates it, so we
    - can use strlen here in compiling the pattern. */
    - ret = regex_compile (pattern, strlen (pattern), syntax, preg);
    -
    - /* POSIX doesn't distinguish between an unmatched open-group and an
    - unmatched close-group: both are REG_EPAREN. */
    - if (ret == REG_ERPAREN) ret = REG_EPAREN;
    -
    - return (int) ret;
    -}
    -
    -
    -/* regexec searches for a given pattern, specified by PREG, in the
    - string STRING.
    -
    - If NMATCH is zero or REG_NOSUB was set in the cflags argument to
    - `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
    - least NMATCH elements, and we set them to the offsets of the
    - corresponding matched substrings.
    -
    - EFLAGS specifies `execution flags' which affect matching: if
    - REG_NOTBOL is set, then ^ does not match at the beginning of the
    - string; if REG_NOTEOL is set, then $ does not match at the end.
    -
    - We return 0 if we find a match and REG_NOMATCH if not. */
    -
    -int
    -regexec (preg, string, nmatch, pmatch, eflags)
    - const regex_t *preg;
    - const char *string;
    - size_t nmatch;
    - regmatch_t pmatch[];
    - int eflags;
    -{
    - int ret;
    - struct re_registers regs;
    - regex_t private_preg;
    - int len = strlen (string);
    - boolean want_reg_info = !preg->no_sub && nmatch > 0;
    -
    - private_preg = *preg;
    -
    - private_preg.not_bol = !!(eflags & REG_NOTBOL);
    - private_preg.not_eol = !!(eflags & REG_NOTEOL);
    -
    - /* The user has told us exactly how many registers to return
    - information about, via `nmatch'. We have to pass that on to the
    - matching routines. */
    - private_preg.regs_allocated = REGS_FIXED;
    -
    - if (want_reg_info)
    - {
    - regs.num_regs = nmatch;
    - regs.start = TALLOC (nmatch, regoff_t);
    - regs.end = TALLOC (nmatch, regoff_t);
    - if (regs.start == NULL || regs.end == NULL)
    - return (int) REG_NOMATCH;
    - }
    -
    - /* Perform the searching operation. */
    - ret = re_search (&private_preg, string, len,
    - /* start: */ 0, /* range: */ len,
    - want_reg_info ? &regs : (struct re_registers *) 0);
    -
    - /* Copy the register information to the POSIX structure. */
    - if (want_reg_info)
    - {
    - if (ret >= 0)
    - {
    - unsigned r;
    -
    - for (r = 0; r < nmatch; r++)
    - {
    - pmatch[r].rm_so = regs.start[r];
    - pmatch[r].rm_eo = regs.end[r];
    - }
    - }
    -
    - /* If we needed the temporary register info, free the space now. */
    - free (regs.start);
    - free (regs.end);
    - }
    -
    - /* We want zero return to mean success, unlike `re_search'. */
    - return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
    -}
    -
    -
    -/* Returns a message corresponding to an error code, ERRCODE, returned
    - from either regcomp or regexec. We don't use PREG here. */
    -
    -size_t
    -regerror (errcode, preg, errbuf, errbuf_size)
    - int errcode;
    - const regex_t *preg;
    - char *errbuf;
    - size_t errbuf_size;
    -{
    - const char *msg;
    - size_t msg_size;
    -
    - if (errcode < 0
    - || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
    - /* Only error codes returned by the rest of the code should be passed
    - to this routine. If we are given anything else, or if other regex
    - code generates an invalid error code, then the program has a bug.
    - Dump core so we can fix it. */
    - abort ();
    -
    - msg = re_error_msg[errcode];
    -
    - /* POSIX doesn't require that we do anything in this case, but why
    - not be nice. */
    - if (! msg)
    - msg = "Success";
    -
    - msg_size = strlen (msg) + 1; /* Includes the null. */
    -
    - if (errbuf_size != 0)
    - {
    - if (msg_size > errbuf_size)
    - {
    - strncpy (errbuf, msg, errbuf_size - 1);
    - errbuf[errbuf_size - 1] = 0;
    - }
    - else
    - strcpy (errbuf, msg);
    - }
    -
    - return msg_size;
    -}
    -
    -
    -/* Free dynamically allocated space used by PREG. */
    -
    -void
    -regfree (preg)
    - regex_t *preg;
    -{
    - if (preg->buffer != NULL)
    - free (preg->buffer);
    - preg->buffer = NULL;
    -
    - preg->allocated = 0;
    - preg->used = 0;
    -
    - if (preg->fastmap != NULL)
    - free (preg->fastmap);
    - preg->fastmap = NULL;
    - preg->fastmap_accurate = 0;
    -
    - if (preg->translate != NULL)
    - free (preg->translate);
    - preg->translate = NULL;
    -}
    -
    -#endif /* not emacs */
    -
    -/*
    -Local variables:
    -make-backup-files: t
    -version-control: t
    -trim-versions-without-asking: nil
    -End:
    -*/
    --- a/ignorance/regex.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,490 +0,0 @@
    -/* Definitions for data structures and routines for the regular
    - expression library, version 0.12.
    -
    - Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
    -
    -#ifndef __REGEXP_LIBRARY_H__
    -#define __REGEXP_LIBRARY_H__
    -
    -/* POSIX says that <sys/types.h> must be included (by the caller) before
    - <regex.h>. */
    -
    -#ifdef VMS
    -/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
    - should be there. */
    -#include <stddef.h>
    -#endif
    -
    -
    -/* The following bits are used to determine the regexp syntax we
    - recognize. The set/not-set meanings are chosen so that Emacs syntax
    - remains the value 0. The bits are given in alphabetical order, and
    - the definitions shifted by one from the previous bit; thus, when we
    - add or remove a bit, only one other definition need change. */
    -typedef unsigned reg_syntax_t;
    -
    -/* If this bit is not set, then \ inside a bracket expression is literal.
    - If set, then such a \ quotes the following character. */
    -#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
    -
    -/* If this bit is not set, then + and ? are operators, and \+ and \? are
    - literals.
    - If set, then \+ and \? are operators and + and ? are literals. */
    -#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
    -
    -/* If this bit is set, then character classes are supported. They are:
    - [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
    - [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
    - If not set, then character classes are not supported. */
    -#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
    -
    -/* If this bit is set, then ^ and $ are always anchors (outside bracket
    - expressions, of course).
    - If this bit is not set, then it depends:
    - ^ is an anchor if it is at the beginning of a regular
    - expression or after an open-group or an alternation operator;
    - $ is an anchor if it is at the end of a regular expression, or
    - before a close-group or an alternation operator.
    -
    - This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
    - POSIX draft 11.2 says that * etc. in leading positions is undefined.
    - We already implemented a previous draft which made those constructs
    - invalid, though, so we haven't changed the code back. */
    -#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
    -
    -/* If this bit is set, then special characters are always special
    - regardless of where they are in the pattern.
    - If this bit is not set, then special characters are special only in
    - some contexts; otherwise they are ordinary. Specifically,
    - * + ? and intervals are only special when not after the beginning,
    - open-group, or alternation operator. */
    -#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
    -
    -/* If this bit is set, then *, +, ?, and { cannot be first in an re or
    - immediately after an alternation or begin-group operator. */
    -#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
    -
    -/* If this bit is set, then . matches newline.
    - If not set, then it doesn't. */
    -#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
    -
    -/* If this bit is set, then . doesn't match NUL.
    - If not set, then it does. */
    -#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
    -
    -/* If this bit is set, nonmatching lists [^...] do not match newline.
    - If not set, they do. */
    -#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
    -
    -/* If this bit is set, either \{...\} or {...} defines an
    - interval, depending on RE_NO_BK_BRACES.
    - If not set, \{, \}, {, and } are literals. */
    -#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
    -
    -/* If this bit is set, +, ? and | aren't recognized as operators.
    - If not set, they are. */
    -#define RE_LIMITED_OPS (RE_INTERVALS << 1)
    -
    -/* If this bit is set, newline is an alternation operator.
    - If not set, newline is literal. */
    -#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
    -
    -/* If this bit is set, then `{...}' defines an interval, and \{ and \}
    - are literals.
    - If not set, then `\{...\}' defines an interval. */
    -#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
    -
    -/* If this bit is set, (...) defines a group, and \( and \) are literals.
    - If not set, \(...\) defines a group, and ( and ) are literals. */
    -#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
    -
    -/* If this bit is set, then \<digit> matches <digit>.
    - If not set, then \<digit> is a back-reference. */
    -#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
    -
    -/* If this bit is set, then | is an alternation operator, and \| is literal.
    - If not set, then \| is an alternation operator, and | is literal. */
    -#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
    -
    -/* If this bit is set, then an ending range point collating higher
    - than the starting range point, as in [z-a], is invalid.
    - If not set, then when ending range point collates higher than the
    - starting range point, the range is ignored. */
    -#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
    -
    -/* If this bit is set, then an unmatched ) is ordinary.
    - If not set, then an unmatched ) is invalid. */
    -#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
    -
    -/* This global variable defines the particular regexp syntax to use (for
    - some interfaces). When a regexp is compiled, the syntax used is
    - stored in the pattern buffer, so changing this does not affect
    - already-compiled regexps. */
    -extern reg_syntax_t re_syntax_options;
    -
    -/* Define combinations of the above bits for the standard possibilities.
    - (The [[[ comments delimit what gets put into the Texinfo file, so
    - don't delete them!) */
    -/* [[[begin syntaxes]]] */
    -#define RE_SYNTAX_EMACS 0
    -
    -#define RE_SYNTAX_AWK \
    - (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
    - | RE_NO_BK_PARENS | RE_NO_BK_REFS \
    - | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
    - | RE_UNMATCHED_RIGHT_PAREN_ORD)
    -
    -#define RE_SYNTAX_POSIX_AWK \
    - (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
    -
    -#define RE_SYNTAX_GREP \
    - (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
    - | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
    - | RE_NEWLINE_ALT)
    -
    -#define RE_SYNTAX_EGREP \
    - (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
    - | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
    - | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
    - | RE_NO_BK_VBAR)
    -
    -#define RE_SYNTAX_POSIX_EGREP \
    - (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
    -
    -/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
    -#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
    -
    -#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
    -
    -/* Syntax bits common to both basic and extended POSIX regex syntax. */
    -#define _RE_SYNTAX_POSIX_COMMON \
    - (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
    - | RE_INTERVALS | RE_NO_EMPTY_RANGES)
    -
    -#define RE_SYNTAX_POSIX_BASIC \
    - (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
    -
    -/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
    - RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
    - isn't minimal, since other operators, such as \`, aren't disabled. */
    -#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
    - (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
    -
    -#define RE_SYNTAX_POSIX_EXTENDED \
    - (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
    - | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
    - | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
    - | RE_UNMATCHED_RIGHT_PAREN_ORD)
    -
    -/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
    - replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
    -#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
    - (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
    - | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
    - | RE_NO_BK_PARENS | RE_NO_BK_REFS \
    - | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
    -/* [[[end syntaxes]]] */
    -
    -/* Maximum number of duplicates an interval can allow. Some systems
    - (erroneously) define this in other header files, but we want our
    - value, so remove any previous define. */
    -#ifdef RE_DUP_MAX
    -#undef RE_DUP_MAX
    -#endif
    -#define RE_DUP_MAX ((1 << 15) - 1)
    -
    -
    -/* POSIX `cflags' bits (i.e., information for `regcomp'). */
    -
    -/* If this bit is set, then use extended regular expression syntax.
    - If not set, then use basic regular expression syntax. */
    -#define REG_EXTENDED 1
    -
    -/* If this bit is set, then ignore case when matching.
    - If not set, then case is significant. */
    -#define REG_ICASE (REG_EXTENDED << 1)
    -
    -/* If this bit is set, then anchors do not match at newline
    - characters in the string.
    - If not set, then anchors do match at newlines. */
    -#define REG_NEWLINE (REG_ICASE << 1)
    -
    -/* If this bit is set, then report only success or fail in regexec.
    - If not set, then returns differ between not matching and errors. */
    -#define REG_NOSUB (REG_NEWLINE << 1)
    -
    -
    -/* POSIX `eflags' bits (i.e., information for regexec). */
    -
    -/* If this bit is set, then the beginning-of-line operator doesn't match
    - the beginning of the string (presumably because it's not the
    - beginning of a line).
    - If not set, then the beginning-of-line operator does match the
    - beginning of the string. */
    -#define REG_NOTBOL 1
    -
    -/* Like REG_NOTBOL, except for the end-of-line. */
    -#define REG_NOTEOL (1 << 1)
    -
    -
    -/* If any error codes are removed, changed, or added, update the
    - `re_error_msg' table in regex.c. */
    -typedef enum
    -{
    - REG_NOERROR = 0, /* Success. */
    - REG_NOMATCH, /* Didn't find a match (for regexec). */
    -
    - /* POSIX regcomp return error codes. (In the order listed in the
    - standard.) */
    - REG_BADPAT, /* Invalid pattern. */
    - REG_ECOLLATE, /* Not implemented. */
    - REG_ECTYPE, /* Invalid character class name. */
    - REG_EESCAPE, /* Trailing backslash. */
    - REG_ESUBREG, /* Invalid back reference. */
    - REG_EBRACK, /* Unmatched left bracket. */
    - REG_EPAREN, /* Parenthesis imbalance. */
    - REG_EBRACE, /* Unmatched \{. */
    - REG_BADBR, /* Invalid contents of \{\}. */
    - REG_ERANGE, /* Invalid range end. */
    - REG_ESPACE, /* Ran out of memory. */
    - REG_BADRPT, /* No preceding re for repetition op. */
    -
    - /* Error codes we've added. */
    - REG_EEND, /* Premature end. */
    - REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
    - REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
    -} reg_errcode_t;
    -
    -/* This data structure represents a compiled pattern. Before calling
    - the pattern compiler, the fields `buffer', `allocated', `fastmap',
    - `translate', and `no_sub' can be set. After the pattern has been
    - compiled, the `re_nsub' field is available. All other fields are
    - private to the regex routines. */
    -
    -struct re_pattern_buffer
    -{
    -/* [[[begin pattern_buffer]]] */
    - /* Space that holds the compiled pattern. It is declared as
    - `unsigned char *' because its elements are
    - sometimes used as array indexes. */
    - unsigned char *buffer;
    -
    - /* Number of bytes to which `buffer' points. */
    - unsigned long allocated;
    -
    - /* Number of bytes actually used in `buffer'. */
    - unsigned long used;
    -
    - /* Syntax setting with which the pattern was compiled. */
    - reg_syntax_t syntax;
    -
    - /* Pointer to a fastmap, if any, otherwise zero. re_search uses
    - the fastmap, if there is one, to skip over impossible
    - starting points for matches. */
    - char *fastmap;
    -
    - /* Either a translate table to apply to all characters before
    - comparing them, or zero for no translation. The translation
    - is applied to a pattern when it is compiled and to a string
    - when it is matched. */
    - char *translate;
    -
    - /* Number of subexpressions found by the compiler. */
    - size_t re_nsub;
    -
    - /* Zero if this pattern cannot match the empty string, one else.
    - Well, in truth it's used only in `re_search_2', to see
    - whether or not we should use the fastmap, so we don't set
    - this absolutely perfectly; see `re_compile_fastmap' (the
    - `duplicate' case). */
    - unsigned can_be_null : 1;
    -
    - /* If REGS_UNALLOCATED, allocate space in the `regs' structure
    - for `max (RE_NREGS, re_nsub + 1)' groups.
    - If REGS_REALLOCATE, reallocate space if necessary.
    - If REGS_FIXED, use what's there. */
    -#define REGS_UNALLOCATED 0
    -#define REGS_REALLOCATE 1
    -#define REGS_FIXED 2
    - unsigned regs_allocated : 2;
    -
    - /* Set to zero when `regex_compile' compiles a pattern; set to one
    - by `re_compile_fastmap' if it updates the fastmap. */
    - unsigned fastmap_accurate : 1;
    -
    - /* If set, `re_match_2' does not return information about
    - subexpressions. */
    - unsigned no_sub : 1;
    -
    - /* If set, a beginning-of-line anchor doesn't match at the
    - beginning of the string. */
    - unsigned not_bol : 1;
    -
    - /* Similarly for an end-of-line anchor. */
    - unsigned not_eol : 1;
    -
    - /* If true, an anchor at a newline matches. */
    - unsigned newline_anchor : 1;
    -
    -/* [[[end pattern_buffer]]] */
    -};
    -
    -typedef struct re_pattern_buffer regex_t;
    -
    -
    -/* search.c (search_buffer) in Emacs needs this one opcode value. It is
    - defined both in `regex.c' and here. */
    -#define RE_EXACTN_VALUE 1
    -
    -/* Type for byte offsets within the string. POSIX mandates this. */
    -typedef int regoff_t;
    -
    -
    -/* This is the structure we store register match data in. See
    - regex.texinfo for a full description of what registers match. */
    -struct re_registers
    -{
    - unsigned num_regs;
    - regoff_t *start;
    - regoff_t *end;
    -};
    -
    -
    -/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
    - `re_match_2' returns information about at least this many registers
    - the first time a `regs' structure is passed. */
    -#ifndef RE_NREGS
    -#define RE_NREGS 30
    -#endif
    -
    -
    -/* POSIX specification for registers. Aside from the different names than
    - `re_registers', POSIX uses an array of structures, instead of a
    - structure of arrays. */
    -typedef struct
    -{
    - regoff_t rm_so; /* Byte offset from string's start to substring's start. */
    - regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
    -} regmatch_t;
    -
    -/* Declarations for routines. */
    -
    -/* To avoid duplicating every routine declaration -- once with a
    - prototype (if we are ANSI), and once without (if we aren't) -- we
    - use the following macro to declare argument types. This
    - unfortunately clutters up the declarations a bit, but I think it's
    - worth it. */
    -
    -#if __STDC__
    -
    -#define _RE_ARGS(args) args
    -
    -#else /* not __STDC__ */
    -
    -#define _RE_ARGS(args) ()
    -
    -#endif /* not __STDC__ */
    -
    -/* Sets the current default syntax to SYNTAX, and return the old syntax.
    - You can also simply assign to the `re_syntax_options' variable. */
    -extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
    -
    -/* Compile the regular expression PATTERN, with length LENGTH
    - and syntax given by the global `re_syntax_options', into the buffer
    - BUFFER. Return NULL if successful, and an error string if not. */
    -extern const char *re_compile_pattern
    - _RE_ARGS ((const char *pattern, int length,
    - struct re_pattern_buffer *buffer));
    -
    -
    -/* Compile a fastmap for the compiled pattern in BUFFER; used to
    - accelerate searches. Return 0 if successful and -2 if was an
    - internal error. */
    -extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
    -
    -
    -/* Search in the string STRING (with length LENGTH) for the pattern
    - compiled into BUFFER. Start searching at position START, for RANGE
    - characters. Return the starting position of the match, -1 for no
    - match, or -2 for an internal error. Also return register
    - information in REGS (if REGS and BUFFER->no_sub are nonzero). */
    -extern int re_search
    - _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
    - int length, int start, int range, struct re_registers *regs));
    -
    -
    -/* Like `re_search', but search in the concatenation of STRING1 and
    - STRING2. Also, stop searching at index START + STOP. */
    -extern int re_search_2
    - _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
    - int length1, const char *string2, int length2,
    - int start, int range, struct re_registers *regs, int stop));
    -
    -
    -/* Like `re_search', but return how many characters in STRING the regexp
    - in BUFFER matched, starting at position START. */
    -extern int re_match
    - _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
    - int length, int start, struct re_registers *regs));
    -
    -
    -/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
    -extern int re_match_2
    - _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
    - int length1, const char *string2, int length2,
    - int start, struct re_registers *regs, int stop));
    -
    -
    -/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
    - ENDS. Subsequent matches using BUFFER and REGS will use this memory
    - for recording register information. STARTS and ENDS must be
    - allocated with malloc, and must each be at least `NUM_REGS * sizeof
    - (regoff_t)' bytes long.
    -
    - If NUM_REGS == 0, then subsequent matches should allocate their own
    - register data.
    -
    - Unless this function is called, the first search or match using
    - PATTERN_BUFFER will allocate its own register data, without
    - freeing the old data. */
    -extern void re_set_registers
    - _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
    - unsigned num_regs, regoff_t *starts, regoff_t *ends));
    -
    -/* 4.2 bsd compatibility. */
    -extern char *re_comp _RE_ARGS ((const char *));
    -extern int re_exec _RE_ARGS ((const char *));
    -
    -/* POSIX compatibility. */
    -extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
    -extern int regexec
    - _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
    - regmatch_t pmatch[], int eflags));
    -extern size_t regerror
    - _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
    - size_t errbuf_size));
    -extern void regfree _RE_ARGS ((regex_t *preg));
    -
    -#endif /* not __REGEXP_LIBRARY_H__ */
    -
    -/*
    -Local variables:
    -make-backup-files: t
    -version-control: t
    -trim-versions-without-asking: nil
    -End:
    -*/
    --- a/ignorance/support.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,158 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifdef HAVE_CONFIG_H
    -# include "../pp_config.h"
    -#endif
    -
    -#include "../common/pp_internal.h"
    -
    -#include <sys/types.h>
    -#include <sys/stat.h>
    -#include <unistd.h>
    -#include <string.h>
    -#include <stdio.h>
    -
    -#include <gtk/gtk.h>
    -
    -#include "ignorance_internal.h"
    -#include "support.h"
    -
    -GtkWidget*
    -lookup_widget (GtkWidget *widget,
    - const gchar *widget_name)
    -{
    - GtkWidget *parent, *found_widget;
    -
    - for (;;)
    - {
    - if (GTK_IS_MENU (widget))
    - parent = gtk_menu_get_attach_widget (GTK_MENU (widget));
    - else
    - parent = widget->parent;
    - if (!parent)
    - parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey");
    - if (parent == NULL)
    - break;
    - widget = parent;
    - }
    -
    - found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget),
    - widget_name);
    - if (!found_widget)
    - g_warning ("Widget not found: %s", widget_name);
    - return found_widget;
    -}
    -
    -static GList *pixmaps_directories = NULL;
    -
    -/* Use this function to set the directory containing installed pixmaps. */
    -void
    -add_pixmap_directory (const gchar *directory)
    -{
    - pixmaps_directories = g_list_prepend (pixmaps_directories,
    - g_strdup (directory));
    -}
    -
    -/* This is an internally used function to find pixmap files. */
    -static gchar*
    -find_pixmap_file (const gchar *filename)
    -{
    - GList *elem;
    -
    - /* We step through each of the pixmaps directory to find it. */
    - elem = pixmaps_directories;
    - while (elem)
    - {
    - gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data,
    - G_DIR_SEPARATOR_S, filename);
    - if (g_file_test (pathname, G_FILE_TEST_EXISTS))
    - return pathname;
    - g_free (pathname);
    - elem = elem->next;
    - }
    - return NULL;
    -}
    -
    -/* This is an internally used function to create pixmaps. */
    -GtkWidget*
    -create_pixmap (GtkWidget *widget,
    - const gchar *filename)
    -{
    - gchar *pathname = NULL;
    - GtkWidget *pixmap;
    -
    - if (!filename || !filename[0])
    - return gtk_image_new ();
    -
    - pathname = find_pixmap_file (filename);
    -
    - if (!pathname)
    - {
    - g_warning (_("Couldn't find pixmap file: %s"), filename);
    - return gtk_image_new ();
    - }
    -
    - pixmap = gtk_image_new_from_file (pathname);
    - g_free (pathname);
    - return pixmap;
    -}
    -
    -/* This is an internally used function to create pixmaps. */
    -GdkPixbuf*
    -create_pixbuf (const gchar *filename)
    -{
    - gchar *pathname = NULL;
    - GdkPixbuf *pixbuf;
    - GError *error = NULL;
    -
    - if (!filename || !filename[0])
    - return NULL;
    -
    - pathname = find_pixmap_file (filename);
    -
    - if (!pathname)
    - {
    - g_warning (_("Couldn't find pixmap file: %s"), filename);
    - return NULL;
    - }
    -
    - pixbuf = gdk_pixbuf_new_from_file (pathname, &error);
    - if (!pixbuf)
    - {
    - fprintf (stderr, "Failed to load pixbuf file: %s: %s\n",
    - pathname, error->message);
    - g_error_free (error);
    - }
    - g_free (pathname);
    - return pixbuf;
    -}
    -
    -/* This is used to set ATK action descriptions. */
    -void
    -glade_set_atk_action_description (AtkAction *action,
    - const gchar *action_name,
    - const gchar *description)
    -{
    - gint n_actions, i;
    -
    - n_actions = atk_action_get_n_actions (action);
    - for (i = 0; i < n_actions; i++)
    - {
    - if (!strcmp (atk_action_get_name (action, i), action_name))
    - atk_action_set_description (action, i, description);
    - }
    -}
    --- a/ignorance/support.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,58 +0,0 @@
    -/*
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#ifndef IGNORANCE_SUPPORT_H
    -#define IGNORANCE_SUPPORT_H
    -
    -#include <gtk/gtk.h>
    -
    -
    -
    -/*
    - * Public Functions.
    - */
    -
    -/*
    - * This function returns a widget in a component created by Glade.
    - * Call it with the toplevel widget in the component (i.e. a window/dialog),
    - * or alternatively any widget in the component, and the name of the widget
    - * you want returned.
    - */
    -GtkWidget* lookup_widget (GtkWidget *widget,
    - const gchar *widget_name);
    -
    -
    -/* Use this function to set the directory containing installed pixmaps. */
    -void add_pixmap_directory (const gchar *directory);
    -
    -
    -/*
    - * Private Functions.
    - */
    -
    -/* This is used to create the pixmaps used in the interface. */
    -GtkWidget* create_pixmap (GtkWidget *widget,
    - const gchar *filename);
    -
    -/* This is used to create the pixbufs used in the interface. */
    -GdkPixbuf* create_pixbuf (const gchar *filename);
    -
    -/* This is used to set ATK action descriptions. */
    -void glade_set_atk_action_description (AtkAction *action,
    - const gchar *action_name,
    - const gchar *description);
    -
    -#endif
    --- a/infopane/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# Infopane Options
    -
    -dependencies: pidgin
    -authors: Sadrul Habib Chowdhury
    -introduced: 2.2.0
    -notes: Requires Pidgin 2.1.0 or newer.
    -
    -Adds some options for the information pane in conversations.
    -
    --- a/infopane/infopane.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,261 +0,0 @@
    -/*
    - * Infopane - Use different views for the details information in conversation windows.
    - * Copyright (C) 2007-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.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 the Free Software Foundation; either version 2 of the
    - * License, 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
    - * 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include "pidgin.h"
    -
    -#include "conversation.h"
    -#include "debug.h"
    -#include "log.h"
    -#include "notify.h"
    -#include "prefs.h"
    -#include "signals.h"
    -#include "util.h"
    -
    -#include "gtkconv.h"
    -#include "gtkimhtml.h"
    -#include "gtkplugin.h"
    -
    -#define PLUGIN_ID "gtk-plugin_pack-infopane"
    -
    -#define PREF_PREFIX "/plugins/gtk/infopane"
    -#define PREF_POSITION PREF_PREFIX "/position"
    -#define PREF_DRAG PREF_PREFIX "/drag"
    -#define PREF_SINGLE PREF_PREFIX "/single"
    -#define PREF_ICON PREF_PREFIX "/icon"
    -
    -static gboolean ensure_tabs_are_showing(PurpleConversation *conv)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    - PidginWindow *win = gtkconv->win;
    - if (win && win->gtkconvs && win->gtkconvs->next)
    - return FALSE;
    - if (purple_prefs_get_bool(PREF_SINGLE)) {
    - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), TRUE);
    - } else if (win->gtkconvs->next == NULL) {
    - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
    - }
    - return FALSE;
    -}
    -
    -static void set_conv_window_prefs(PidginConversation *gtkconv)
    -{
    - GtkWidget *paned, *vbox;
    - GList *list;
    - char pref;
    -
    - pref = purple_prefs_get_string(PREF_POSITION)[0];
    -
    - if (pref == 't')
    - goto end_position;
    -
    - if (pref == 'n') {
    - gtk_widget_hide_all(gtkconv->infopane_hbox->parent);
    - goto end_position;
    - }
    -
    - list = gtk_container_get_children(GTK_CONTAINER(gtkconv->tab_cont));
    - paned = list->data;
    - vbox = gtk_paned_get_child1(GTK_PANED(paned));
    -
    - g_object_ref(G_OBJECT(gtkconv->infopane_hbox->parent));
    - gtk_container_remove(GTK_CONTAINER(vbox), gtkconv->infopane_hbox->parent);
    - gtk_box_pack_end(GTK_BOX(vbox), gtkconv->infopane_hbox->parent, FALSE, FALSE, 0);
    - g_object_unref(G_OBJECT(gtkconv->infopane_hbox->parent));
    -
    -end_position:
    - /* PREF_DRAG */
    - /* To disable dragging, setup a listener for button_press_event and return TRUE if the
    - * click is not going to popup up the sendto or the context menu */
    -
    - /* PREF_SINGLE */
    - ensure_tabs_are_showing(gtkconv->active_conv);
    -
    - /* PREF_ICON */
    - if (purple_prefs_get_bool(PREF_ICON)) {
    - gtk_widget_show(gtkconv->icon);
    - } else {
    - gtk_widget_hide(gtkconv->icon);
    - }
    -
    - return;
    -}
    -
    -static void conversation_deleted(PurpleConversation *conv)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    - PidginWindow *win = gtkconv->win;
    - if (win->gtkconvs->next && !win->gtkconvs->next->next) { /* There are only two tabs in the window */
    - PidginConversation *p = win->gtkconvs->data;
    - int id;
    - if (p == gtkconv)
    - p = win->gtkconvs->next->data;
    - id = g_timeout_add(0, (GSourceFunc)ensure_tabs_are_showing, p->active_conv);
    - g_signal_connect_swapped(G_OBJECT(win->window), "destroy",
    - G_CALLBACK(g_source_remove), GINT_TO_POINTER(id));
    - }
    -}
    -
    -static void
    -pref_changed(gpointer data, ...)
    -{
    - GList *wins = pidgin_conv_windows_get_list();
    - for (; wins; wins = wins->next) {
    - GList *tabs = pidgin_conv_window_get_gtkconvs(wins->data);
    - for (; tabs; tabs = tabs->next) {
    - set_conv_window_prefs(tabs->data);
    - }
    - }
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin)
    -{
    - guint regsuccess = 0;
    -
    - regsuccess = purple_signal_connect(pidgin_conversations_get_handle(),
    - "conversation-displayed",
    - plugin, PURPLE_CALLBACK(set_conv_window_prefs), NULL);
    -
    - if(regsuccess == 0) {
    - purple_debug_error(PLUGIN_ID, "Libpurple and Pidgin are too old!\n");
    - purple_debug_error(PLUGIN_ID, _("Libpurple and Pidgin are too old!\n"));
    - purple_notify_error(plugin, _("Incompatible Plugin"),
    - _("You need to update Pidgin!"),
    - _("This plugin is incompatible with the running version of Pidgin and Libpurple because it is too old. Please upgrade to the newest version of Pidgin."));
    - return FALSE;
    - }
    -
    - purple_signal_connect(purple_conversations_get_handle(),
    - "deleting-conversation",
    - plugin, PURPLE_CALLBACK(conversation_deleted), NULL);
    - purple_signal_connect(pidgin_conversations_get_handle(),
    - "conversation-switched",
    - plugin, PURPLE_CALLBACK(ensure_tabs_are_showing), NULL);
    -
    - purple_prefs_connect_callback(plugin, PREF_POSITION, (PurplePrefCallback)pref_changed, NULL);
    - purple_prefs_connect_callback(plugin, PREF_DRAG, (PurplePrefCallback)pref_changed, NULL);
    - purple_prefs_connect_callback(plugin, PREF_ICON, (PurplePrefCallback)pref_changed, NULL);
    - purple_prefs_connect_callback(plugin, PREF_SINGLE, (PurplePrefCallback)pref_changed, NULL);
    -
    - purple_prefs_trigger_callback(PREF_POSITION);
    -
    - return TRUE;
    -}
    -
    -static PurplePluginPrefFrame *
    -get_plugin_pref_frame(PurplePlugin *plugin)
    -{
    - PurplePluginPrefFrame *frame;
    - PurplePluginPref *pref;
    -
    - frame = purple_plugin_pref_frame_new();
    -
    - /* XXX: Is there a better way than this? There really should be. */
    - pref = purple_plugin_pref_new_with_name_and_label(PREF_POSITION, _("Position of the infopane ('top', 'bottom' or 'none')"));
    - purple_plugin_pref_frame_add(frame, pref);
    -#if 0
    - pref = purple_plugin_pref_new_with_name_and_label(PREF_ICON,
    - _("Show icon in the tabs"));
    - purple_plugin_pref_frame_add(frame, pref);
    -#endif
    - pref = purple_plugin_pref_new_with_name_and_label(PREF_SINGLE,
    - _("Always show the tab"));
    - purple_plugin_pref_frame_add(frame, pref);
    -
    - return frame;
    -}
    -
    -static PurplePluginUiInfo prefs_info =
    -{
    - get_plugin_pref_frame,
    - 0,
    - NULL,
    -
    - /* padding */
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD,
    - PIDGIN_PLUGIN_TYPE,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    - PLUGIN_ID,
    - NULL,
    - PP_VERSION,
    - NULL,
    - NULL,
    - "Sadrul H Chowdhury <sadrul@pidgin.im>",
    - PP_WEBSITE,
    - plugin_load,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - &prefs_info,
    - NULL,
    -
    - /* padding */
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin)
    -{
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - if(purple_version_check(2,2,0) == NULL) {
    - info.name = _("Infopane Options");
    - info.summary = _("Allow customizing the details information in conversation windows.");
    - info.description = _("Allow customizing the details information in conversation windows.");
    -
    - purple_prefs_add_none(PREF_PREFIX);
    - purple_prefs_add_string(PREF_POSITION, "top");
    - purple_prefs_add_bool(PREF_DRAG, FALSE);
    - purple_prefs_add_bool(PREF_SINGLE, TRUE);
    - purple_prefs_add_bool(PREF_ICON, TRUE);
    - } else {
    - purple_debug_error(PLUGIN_ID, "Libpurple and Pidgin are too old!\n");
    - purple_debug_error(PLUGIN_ID, _("Libpurple and Pidgin are too old!\n"));
    -
    - info.name = _("Incompatible Plugin! - Check plugin details!");
    - info.summary = _("This plugin is NOT compatible with this version of Pidgin!");
    - info.description = _("This plugin is NOT compatible with this version of Pidgin!");
    - }
    -}
    -
    -PURPLE_INIT_PLUGIN(infopane, init_plugin, info)
    --- a/infopane/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('default') and PIDGIN.found()
    - infopane = shared_module('infopane',
    - 'infopane.c',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'infopane'
    -endif
    --- a/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ b/meson.build Thu Mar 05 09:53:21 2020 +0000
    @@ -164,15 +164,10 @@
    PP_FINCH_BUILD = []
    subdir('album')
    -subdir('autoprofile')
    subdir('autoreply')
    -subdir('awaynotify')
    subdir('bash')
    -subdir('bit')
    subdir('blistops')
    -subdir('buddytime')
    subdir('capsnot')
    -subdir('chronic')
    subdir('colorize')
    subdir('convbadger')
    subdir('dewysiwygification')
    @@ -180,29 +175,22 @@
    subdir('difftopic')
    subdir('eight_ball')
    subdir('enhancedhist')
    -subdir('findip')
    subdir('flip')
    subdir('gRIM')
    subdir('google')
    subdir('groupmsg')
    -subdir('hideconv')
    subdir('highlight')
    subdir('icon-override')
    -subdir('ignorance')
    subdir('ignore')
    -subdir('infopane')
    subdir('irc-more')
    subdir('irchelper')
    subdir('irssi')
    subdir('lastseen')
    subdir('listhandler')
    subdir('listlog')
    -subdir('msglen')
    subdir('mystatusbox')
    -subdir('napster')
    subdir('nicksaid')
    subdir('ning')
    -subdir('nomobility')
    subdir('okcupid')
    subdir('oldlogger')
    subdir('omegle')
    @@ -212,14 +200,10 @@
    subdir('showoffline')
    subdir('simfix')
    subdir('slashexec')
    -subdir('smartear')
    subdir('snpp')
    subdir('splitter')
    -#subdir('stress')
    subdir('sslinfo')
    -subdir('stocker')
    subdir('switchspell')
    -subdir('talkfilters')
    subdir('timelog')
    subdir('translate')
    subdir('xmppprio')
    --- a/msglen/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# Message Length
    -
    -status: incomplete
    -dependencies: pidgin
    -authors: Gary Kramlich
    -introduced: 2.4.0
    -
    -Shows the length of your current message in the menu tray.
    -
    --- a/msglen/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('incomplete') and PIDGIN.found()
    - msglen = shared_module('msglen',
    - 'msglen.c',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'msglen'
    -endif
    --- a/msglen/msglen.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,258 +0,0 @@
    -/*
    - * msglen - Adds the current message's length to the menutray of a conversation
    - * Copyright (C) 2008 Gary Kramlich <grim@reaperworld.com>
    - *
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include <gtk/gtk.h>
    -
    -#include <conversation.h>
    -#include <signals.h>
    -
    -#include <gtkconv.h>
    -#include <gtkmenutray.h>
    -#include <gtkplugin.h>
    -#include <gtkutils.h>
    -
    -/******************************************************************************
    - * Structs
    - *****************************************************************************/
    -typedef struct {
    - PurpleConversation *conv;
    - PidginWindow *win;
    - GtkWidget *label;
    - gulong sig_id;
    -} MsgLenData;
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -static GHashTable *data = NULL;
    -
    -/******************************************************************************
    - * helpers
    - *****************************************************************************/
    -static void
    -msg_len_data_free(MsgLenData *mld) {
    - mld->win = NULL;
    -
    - if(mld->label && GTK_IS_LABEL(mld->label))
    - gtk_widget_destroy(mld->label);
    -
    - g_free(mld);
    -
    - mld = NULL;
    -}
    -
    -static void
    -msg_len_data_free_helper(gpointer k, gpointer v, gpointer d) {
    - MsgLenData *mld = (MsgLenData *)v;
    -
    - msg_len_data_free(mld);
    -}
    -
    -static void msg_len_update(PidginWindow *win, PurpleConversation *conv);
    -
    -static gboolean
    -msg_len_key_released_cb(GtkWidget *w, GdkEventKey *e, gpointer d) {
    - MsgLenData *mld = (MsgLenData *)d;
    -
    - if(d)
    - msg_len_update(mld->win, mld->conv);
    -
    - return FALSE;
    -}
    -
    -static void
    -msg_len_add_signal(PidginConversation *pconv, MsgLenData *mld) {
    - g_signal_connect(G_OBJECT(pconv->entry),
    - "key-release-event",
    - G_CALLBACK(msg_len_key_released_cb), mld);
    -}
    -
    -static MsgLenData *
    -msg_len_find_data(PidginWindow *win) {
    - MsgLenData *mld = NULL;
    -
    - mld = g_hash_table_lookup(data, win);
    -
    - if(mld == NULL) {
    - mld = g_new0(MsgLenData, 1);
    -
    - mld->win = win;
    -
    - mld->label = gtk_label_new("");
    - pidgin_menu_tray_append(PIDGIN_MENU_TRAY(win->menu.tray), mld->label,
    - NULL);
    - gtk_widget_show(mld->label);
    -
    - g_signal_connect_swapped(G_OBJECT(mld->label), "destroy",
    - G_CALLBACK(g_nullify_pointer), &mld->label);
    - }
    -
    - return mld;
    -}
    -
    -static void
    -msg_len_update(PidginWindow *win, PurpleConversation *conv) {
    - PidginConversation *gtkconv = NULL;
    - MsgLenData *mld = NULL;
    - gchar *text = NULL;
    - gint count = 0;
    -
    - g_return_if_fail(win);
    - g_return_if_fail(conv);
    -
    - gtkconv = PIDGIN_CONVERSATION(conv);
    -
    - mld = msg_len_find_data(win);
    -
    - mld->conv = conv;
    -
    - count = gtk_text_buffer_get_char_count(gtkconv->entry_buffer);
    -
    - text = g_strdup_printf("%d", count);
    - gtk_label_set_text(GTK_LABEL(mld->label), text);
    - g_free(text);
    -
    - g_hash_table_insert(data, win, mld);
    -}
    -
    -/******************************************************************************
    - * Callbacks
    - *****************************************************************************/
    -static void
    -msg_len_conv_created_cb(PurpleConversation *conv, gpointer d) {
    - PidginConversation *pconv = PIDGIN_CONVERSATION(conv);
    - PidginWindow *win = pidgin_conv_get_window(pconv);
    - MsgLenData *mld = NULL;
    -
    - mld = msg_len_find_data(win);
    -
    - msg_len_add_signal(pconv, mld);
    -
    - msg_len_update(win, conv);
    -}
    -
    -static void
    -msg_len_conv_destroyed_cb(PurpleConversation *conv, gpointer d) {
    - g_hash_table_remove(data, conv);
    -}
    -
    -static void
    -msg_len_conv_switched_cb(PurpleConversation *conv, gpointer d) {
    - PidginConversation *pconv = PIDGIN_CONVERSATION(conv);
    - PidginWindow *win = pidgin_conv_get_window(pconv);
    -
    - msg_len_update(win, conv);
    -}
    -
    -/******************************************************************************
    - * Plugin Stuff
    - *****************************************************************************/
    -static gboolean
    -plugin_load(PurplePlugin *plugin) {
    - GList *convs = NULL;
    - void *conv_handle = purple_conversations_get_handle();
    -
    - data = g_hash_table_new_full(g_direct_hash, g_direct_equal,
    - NULL, NULL);
    -
    - purple_signal_connect(conv_handle, "conversation-created", plugin,
    - PURPLE_CALLBACK(msg_len_conv_created_cb), NULL);
    - purple_signal_connect(conv_handle, "deleting-conversation", plugin,
    - PURPLE_CALLBACK(msg_len_conv_destroyed_cb), NULL);
    -
    - purple_signal_connect(pidgin_conversations_get_handle(),
    - "conversation-switched", plugin,
    - PURPLE_CALLBACK(msg_len_conv_switched_cb), NULL);
    -
    - for(convs = purple_get_conversations(); convs; convs = convs->next) {
    - PurpleConversation *conv = (PurpleConversation *)convs->data;
    - PidginConversation *pconv = PIDGIN_CONVERSATION(conv);
    - MsgLenData *mld = msg_len_find_data(pconv->win);
    -
    - msg_len_add_signal(pconv, mld);
    -
    - msg_len_update(pconv->win, conv);
    - }
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin) {
    - g_hash_table_foreach(data, msg_len_data_free_helper, NULL);
    -
    - g_hash_table_destroy(data);
    -
    - data = NULL;
    -
    - return TRUE;
    -}
    -
    -static PurplePluginInfo info = {
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD,
    - PIDGIN_PLUGIN_TYPE,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    -
    - "gtk-plugin_pack-msglen",
    - NULL,
    - PP_VERSION,
    - NULL,
    - NULL,
    - "Gary Kramlich <grim@reaperworld.com>",
    - PP_WEBSITE,
    -
    - plugin_load,
    - plugin_unload,
    - NULL,
    -
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    -
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -
    -static void
    -init_plugin(PurplePlugin *plugin) {
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("Message Length");
    - info.summary = _("Shows the length of your current message in the menu "
    - "tray");
    - info.description = info.summary;
    -}
    -
    -PURPLE_INIT_PLUGIN(msg_len, init_plugin, info)
    -
    Binary file napster/16/napster.png has changed
    Binary file napster/22/napster.png has changed
    Binary file napster/48/napster.png has changed
    --- a/napster/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# Napster Protocol Plugin
    -
    -dependencies: libpurple
    -authors: Rob Flynn
    -introduced: 1.0beta6
    -notes: Introduced into the Purple Plugin Pack after it was removed from libpurple.
    -
    -NAPSTER Protocol Plugin
    -
    --- a/napster/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,16 +0,0 @@
    -if TYPES.contains('default') and PURPLE.found() and IS_PURPLE_TWO
    - napster = shared_library('napster',
    - 'napster.c',
    - dependencies : [PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'napster'
    -
    - if PIDGIN.found()
    - foreach size : [16, 22, 48]
    - install_data('@0@/napster.png'.format(size),
    - install_dir : join_paths(PIDGIN_PIXMAPSDIR, 'protocols/@0@'.format(size)))
    - endforeach
    - endif
    -endif
    --- a/napster/napster.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,765 +0,0 @@
    -/*
    - * napster - Napster Protocol Plugin
    - *
    - * Copyright (C) 2000-2001, Rob Flynn <rob@marko.net>
    - *
    - * Assimilated for inclusion in the Plugin Pack:
    - * Copyright (C) 2006-2008 Gary Kramlich <grim@reaperworld.com>
    - * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
    - *
    - * 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 of the License, 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 02111-1301 USA
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include <account.h>
    -#include <accountopt.h>
    -#include <blist.h>
    -#include <conversation.h>
    -#include <debug.h>
    -#include <notify.h>
    -#include <prpl.h>
    -#include <proxy.h>
    -#include <util.h>
    -
    -#include <errno.h>
    -#include <fcntl.h>
    -#include <string.h>
    -#include <unistd.h>
    -
    -#define NAP_SERVER "64.124.41.187"
    -#define NAP_PORT 8888
    -
    -#define NAPSTER_CONNECT_STEPS 2
    -
    -GSList *nap_connections = NULL;
    -
    -struct nap_data {
    - int fd;
    - gchar *email;
    -};
    -
    -static PurpleConversation *
    -nap_find_chat(PurpleConnection *gc, const char *name) {
    - GSList *bcs = gc->buddy_chats;
    -
    - while (bcs) {
    - PurpleConversation *b = bcs->data;
    - if (!purple_utf8_strcasecmp(b->name, name))
    - return b;
    - bcs = bcs->next;
    - }
    -
    - return NULL;
    -}
    -
    -static void
    -nap_write_packet(PurpleConnection *gc, unsigned short command,
    - const char *format, ...)
    -{
    - struct nap_data *ndata = (struct nap_data *)gc->proto_data;
    - va_list ap;
    - gchar *message;
    - unsigned short size;
    -
    - va_start(ap, format);
    - message = g_strdup_vprintf(format, ap);
    - va_end(ap);
    -
    - size = strlen(message);
    - purple_debug(PURPLE_DEBUG_MISC, "napster", "S %3hd: %s\n", command, message);
    -
    - write(ndata->fd, &size, 2);
    - write(ndata->fd, &command, 2);
    - write(ndata->fd, message, size);
    -
    - g_free(message);
    -}
    -
    -static int
    -nap_do_irc_style(PurpleConnection *gc, const char *message, const char *name) {
    - gchar **res;
    -
    - purple_debug(PURPLE_DEBUG_MISC, "napster", "C %s\n", message);
    -
    - res = g_strsplit(message, " ", 2);
    -
    - if (!g_ascii_strcasecmp(res[0], "/ME")) { /* MSG_CLIENT_PUBLIC */
    - nap_write_packet(gc, 824, "%s \"%s\"", name, res[1]);
    -
    - } else if (!g_ascii_strcasecmp(res[0], "/MSG")) { /* MSG_CLIENT_PUBLIC */
    - nap_write_packet(gc, 205, "%s", res[1]);
    -
    - } else if (!g_ascii_strcasecmp(res[0], "/JOIN")) { /* join chatroom MSG_CLIENT_JOIN */
    - if (!res[1]) {
    - g_strfreev(res);
    - return 1;
    - }
    - if (res[1][0] != '#')
    - nap_write_packet(gc, 400, "#%s", res[1]);
    - else
    - nap_write_packet(gc, 400, "%s", res[1]);
    -
    - } else if (!g_ascii_strcasecmp(res[0], "/PART")) { /* partchatroom MSG_CLIENT_PART */
    - nap_write_packet(gc, 401, "%s", res[1] ? res[1] : name);
    -
    - } else if (!g_ascii_strcasecmp(res[0], "/TOPIC")) { /* set topic MSG_SERVER_TOPIC */
    - nap_write_packet(gc, 410, "%s", res[1] ? res[1] : name);
    -
    - } else if (!g_ascii_strcasecmp(res[0], "/WHOIS")) { /* whois request MSG_CLIENT_WHOIS */
    - nap_write_packet(gc, 603, "%s", res[1]);
    -
    - } else if (!g_ascii_strcasecmp(res[0], "/PING")) { /* send ping MSG_CLIENT_PING */
    - nap_write_packet(gc, 751, "%s", res[1]);
    -
    - } else if (!g_ascii_strcasecmp(res[0], "/KICK")) { /* kick asswipe MSG_CLIENT_KICK */
    - nap_write_packet(gc, 829, "%s", res[1]);
    -
    - } else {
    - g_strfreev(res);
    - return 1;
    - }
    -
    - g_strfreev(res);
    - return 0;
    -}
    -
    -/* 205 - MSG_CLIENT_PRIVMSG */
    -static int
    -nap_send_im(PurpleConnection *gc, const char *who, const char *message,
    - PurpleMessageFlags flags)
    -{
    - char *tmp = purple_unescape_html(message);
    -
    - if ((strlen(tmp) < 2) || (tmp[0] != '/' ) || (tmp[1] == '/')) {
    - /* Actually send a chat message */
    - nap_write_packet(gc, 205, "%s %s", who, tmp);
    - } else {
    - /* user typed an IRC-style command */
    - nap_do_irc_style(gc, tmp, who);
    - }
    -
    - g_free(tmp);
    -
    - return 1;
    -}
    -
    -/* 207 - MSG_CLIENT_ADD_HOTLIST */
    -static void
    -nap_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
    - nap_write_packet(gc, 207, "%s", buddy->name);
    -}
    -
    -/* 208 - MSG_CLIENT_ADD_HOTLIST_SEQ */
    -static void
    -nap_send_buddylist(PurpleConnection *gc) {
    - PurpleBuddyList *blist;
    - PurpleBlistNode *gnode, *cnode, *bnode;
    - PurpleBuddy *buddy;
    -
    - if ((blist = purple_get_blist()) != NULL)
    - {
    - for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
    - {
    - if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
    - continue;
    - for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
    - {
    - if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
    - continue;
    - for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
    - {
    - if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
    - continue;
    - buddy = (PurpleBuddy *)bnode;
    - nap_write_packet(gc, 208, "%s", buddy->name);
    - }
    - }
    - }
    - }
    -}
    -
    -/* 303 - MSG_CLIENT_REMOVE_HOTLIST */
    -static void
    -nap_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
    - nap_write_packet(gc, 303, "%s", buddy->name);
    -}
    -
    -static char *
    -nap_get_chat_name(GHashTable *data) {
    - char *name = g_hash_table_lookup(data, "group");
    -
    - /* Make sure the name has a # preceding it */
    - if (name[0] != '#') {
    - return g_strdup_printf("#%s", name);
    - }
    -
    - return g_strdup(name);
    -}
    -
    -/* 400 - MSG_CLIENT_JOIN */
    -static void
    -nap_join_chat(PurpleConnection *gc, GHashTable *data) {
    - char *name;
    -
    - if (!data)
    - return;
    -
    - name = nap_get_chat_name(data);
    -
    - if (name) {
    - nap_write_packet(gc, 400, "%s", name);
    - g_free(name);
    - }
    -}
    -
    -/* 401 - MSG_CLIENT_PART */
    -static void
    -nap_chat_leave(PurpleConnection *gc, int id) {
    - PurpleConversation *c = purple_find_chat(gc, id);
    -
    - if (!c)
    - return;
    -
    - nap_write_packet(gc, 401, "%s", c->name);
    -}
    -
    -/* 402 - MSG_CLIENT_PUBLIC */
    -static int
    -nap_chat_send(PurpleConnection *gc, int id, const char *message,
    - PurpleMessageFlags flags)
    -{
    - PurpleConversation *c = purple_find_chat(gc, id);
    - char *tmp = purple_unescape_html(message);
    -
    - if (!c)
    - return -EINVAL;
    -
    - if ((strlen(tmp) < 2) || (tmp[0] != '/' ) || (tmp[1] == '/')) {
    - /* Actually send a chat message */
    - nap_write_packet(gc, 402, "%s %s", c->name, tmp);
    - } else {
    - /* user typed an IRC-style command */
    - nap_do_irc_style(gc, tmp, c->name);
    - }
    -
    - g_free(tmp);
    -
    - return 0;
    -}
    -
    -/* 603 - MSG_CLIENT_WHOIS */
    -static void
    -nap_get_info(PurpleConnection *gc, const char *who) {
    - nap_write_packet(gc, 603, "%s", who);
    -}
    -
    -static void
    -nap_callback(gpointer data, gint source, PurpleInputCondition condition) {
    - PurpleConnection *gc = data;
    - struct nap_data *ndata = gc->proto_data;
    - PurpleAccount *account = NULL;
    - PurpleConversation *c = NULL;
    - PurpleNotifyUserInfo *pnui = NULL;
    - gchar *buf = NULL, *buf2 = NULL, *buf3 = NULL, **res = NULL;
    - unsigned short header[2] = { 0 , 0 };
    - int len = 0;
    - int command = 0;
    - int i;
    -
    - account = purple_connection_get_account(gc);
    -
    - if (read(source, (void*)header, 4) != 4) {
    - purple_connection_error(gc, _("Unable to read header from server"));
    - return;
    - }
    -
    - len = header[0];
    - command = header[1];
    - buf = (gchar *)g_malloc((len + 1) * sizeof(gchar));
    - buf[len] = '\0';
    -
    - i = 0;
    - do {
    - int tmp = read(source, buf + i, len - i);
    - if (tmp <= 0) {
    - g_free(buf);
    - buf = g_strdup_printf(_("Unable to read message from server: %s. Command is %hd, length is %hd."), strerror(errno), len, command);
    - purple_connection_error(gc, buf);
    - g_free(buf);
    - return;
    - }
    - i += tmp;
    - } while (i != len);
    -
    - purple_debug(PURPLE_DEBUG_MISC, "napster", "R %3hd: %s\n", command, buf);
    -
    - switch (command) {
    - case 000: /* MSG_SERVER_ERROR */
    - purple_notify_error(gc, NULL, buf, NULL);
    - purple_input_remove(gc->inpa);
    - gc->inpa = 0;
    - close(source);
    - purple_connection_error(gc, _("Unknown server error."));
    - break;
    -
    - case 003: /* MSG_SERVER_EMAIL */
    - purple_debug(PURPLE_DEBUG_MISC, "napster", "Registered with e-mail address: %s\n", buf);
    - ndata->email = g_strdup(buf);
    -
    - /* Our signon is complete */
    - purple_connection_set_state(gc, PURPLE_CONNECTED);
    -
    - /* Send the server our buddy list */
    - nap_send_buddylist(gc);
    -
    - break;
    -
    - case 201: /* MSG_SERVER_SEARCH_RESULT */
    - res = g_strsplit(buf, " ", 0);
    - purple_prpl_got_user_status(account, res[0], "available", NULL);
    - g_strfreev(res);
    - break;
    -
    - case 202: /* MSG_SERVER_SEARCH_END */
    - purple_prpl_got_user_status(account, buf, "offline", NULL);
    - break;
    -
    - case 205: /* MSG_CLIENT_PRIVMSG */
    - res = g_strsplit(buf, " ", 2);
    - buf2 = g_markup_escape_text(res[1], -1);
    - serv_got_im(gc, res[0], buf2, 0, time(NULL));
    - g_free(buf2);
    - g_strfreev(res);
    - break;
    -
    - case 209: /* MSG_SERVER_USER_SIGNON */
    - /* USERNAME SPEED */
    - res = g_strsplit(buf, " ", 2);
    - purple_prpl_got_user_status(account, res[0], "available", NULL);
    - g_strfreev(res);
    - break;
    -
    - case 210: /* MSG_SERVER_USER_SIGNOFF */
    - /* USERNAME SPEED */
    - res = g_strsplit(buf, " ", 2);
    - purple_prpl_got_user_status(account, res[0], "offline", NULL);
    - g_strfreev(res);
    - break;
    -
    - case 214: /* MSG_SERVER_STATS */
    - res = g_strsplit(buf, " ", 3);
    - buf2 = g_strdup_printf(_("users: %s, files: %s, size: %sGB"), res[0], res[1], res[2]);
    - serv_got_im(gc, "server", buf2, 0, time(NULL));
    - g_free(buf2);
    - g_strfreev(res);
    - break;
    -
    - case 301: /* MSG_SERVER_HOTLIST_ACK */
    - /* Our buddy was added successfully */
    - break;
    -
    - case 302: /* MSG_SERVER_HOTLIST_ERROR */
    - buf2 = g_strdup_printf(_("Unable to add \"%s\" to your Napster hotlist"), buf);
    - purple_notify_error(gc, NULL, buf2, NULL);
    - g_free(buf2);
    - break;
    -
    - case 316: /* MSG_SERVER_DISCONNECTING */
    - /* we have been kicked off =^( */
    - purple_connection_error(gc, _("You were disconnected from the server."));
    - break;
    -
    - case 401: /* MSG_CLIENT_PART */
    - c = nap_find_chat(gc, buf);
    - if (c)
    - serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)));
    - break;
    -
    - case 403: /* MSG_SERVER_PUBLIC */
    - res = g_strsplit(buf, " ", 3);
    - c = nap_find_chat(gc, res[0]);
    - if (c)
    - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), res[1], 0, res[2], time(NULL));
    - g_strfreev(res);
    - break;
    -
    - case 404: /* MSG_SERVER_NOSUCH */
    - /* abused by opennap servers to broadcast stuff */
    - buf2 = g_markup_escape_text(buf, -1);
    - serv_got_im(gc, "server", buf2, 0, time(NULL));
    - g_free(buf2);
    - break;
    -
    - case 405: /* MSG_SERVER_JOIN_ACK */
    - c = nap_find_chat(gc, buf);
    - if (!c)
    - serv_got_joined_chat(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), buf);
    - break;
    -
    - case 407: /* MSG_SERVER_PART */
    - res = g_strsplit(buf, " ", 0);
    - c = nap_find_chat(gc, res[0]);
    - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c), res[1], NULL);
    - g_strfreev(res);
    - break;
    -
    - case 406: /* MSG_SERVER_JOIN */
    - case 408: /* MSG_SERVER_CHANNEL_USER_LIST */
    - res = g_strsplit(buf, " ", 4);
    - c = nap_find_chat(gc, res[0]);
    - purple_conv_chat_add_user(PURPLE_CONV_CHAT(c), res[1], NULL, PURPLE_CBFLAGS_NONE, TRUE);
    - g_strfreev(res);
    - break;
    -
    - case 409: /* MSG_SERVER_CHANNEL_USER_LIST_END */
    - break;
    -
    - case 410: /* MSG_SERVER_TOPIC */
    - /* display the topic in the channel */
    - res = g_strsplit(buf, " ", 2);
    - c = nap_find_chat(gc, res[0]);
    - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), res[0], res[1]);
    - g_strfreev(res);
    - break;
    -
    - case 603: /* MSG_CLIENT_WHOIS */
    - buf2 = g_strdup_printf(_("%s requested your information"), buf);
    - serv_got_im(gc, "server", buf2, 0, time(NULL));
    - g_free(buf2);
    - break;
    -
    - case 604: /* MSG_SERVER_WHOIS_RESPONSE */
    - /* XXX - Format is: "Elite" 37 " " "Active" 0 0 0 0 "purple 0.63cvs" 0 0 192.168.1.41 32798 0 unknown flounder */
    - res = g_strsplit(buf, " ", 2);
    - /* res[0] == username */
    - pnui = purple_notify_user_info_new();
    - purple_notify_user_info_add_pair(pnui, _("Napster User Info:"), res[1]);
    - purple_notify_userinfo(gc, res[0], pnui, NULL, NULL);
    - g_strfreev(res);
    - break;
    -
    - case 621:
    - case 622: /* MSG_CLIENT_MOTD */
    - /* also replaces MSG_SERVER_MOTD, so we should display it */
    - buf2 = g_markup_escape_text(buf, -1);
    - serv_got_im(gc, "motd", buf2, 0, time(NULL));
    - g_free(buf2);
    - break;
    -
    - case 627: /* MSG_CLIENT_WALLOP */
    - /* abused by opennap server maintainers to broadcast stuff */
    - buf2 = g_markup_escape_text(buf, -1);
    - serv_got_im(gc, "wallop", buf2, 0, time(NULL));
    - g_free(buf2);
    - break;
    -
    - case 628: /* MSG_CLIENT_ANNOUNCE */
    - buf2 = g_markup_escape_text(buf, -1);
    - serv_got_im(gc, "announce", buf2, 0, time(NULL));
    - g_free(buf);
    - break;
    -
    - case 748: /* MSG_SERVER_GHOST */
    - /* Looks like someone logged in as us! =-O */
    - purple_connection_error(gc, _("You have signed on from another location."));
    - break;
    -
    - case 751: /* MSG_CLIENT_PING */
    - buf2 = g_strdup_printf(_("%s requested a PING"), buf);
    - serv_got_im(gc, "server", buf2, 0, time(NULL));
    - g_free(buf2);
    - /* send back a pong */
    - /* MSG_CLIENT_PONG */
    - nap_write_packet(gc, 752, "%s", buf);
    - break;
    -
    - case 752: /* MSG_CLIENT_PONG */
    - buf2 = g_strdup_printf("Received pong from %s", buf);
    - purple_notify_info(gc, NULL, buf2, NULL);
    - g_free(buf2);
    - break;
    -
    - case 824: /* MSG_CLIENT_EMOTE */
    - res = g_strsplit(buf, " ", 3);
    - buf2 = g_strndup(res[2]+1, strlen(res[2]) - 2); /* chomp off the surround quotes */
    - buf3 = g_strdup_printf("/me %s", buf2);
    - g_free(buf2);
    - if ((c = nap_find_chat(gc, res[0]))) {
    - purple_conv_chat_write(PURPLE_CONV_CHAT(c), res[1], buf3, PURPLE_MESSAGE_NICK, time(NULL));
    - }
    - g_free(buf3);
    - g_strfreev(res);
    - break;
    -
    - default:
    - purple_debug(PURPLE_DEBUG_MISC, "napster", "Unknown packet %hd: %s\n", command, buf);
    - break;
    - }
    -
    - g_free(buf);
    -}
    -
    -/* 002 - MSG_CLIENT_LOGIN */
    -static void
    -nap_login_connect(gpointer data, gint source, const gchar *error_message) {
    - PurpleConnection *gc = data;
    - struct nap_data *ndata = (struct nap_data *)gc->proto_data;
    - gchar *buf;
    -
    - if (!g_list_find(purple_connections_get_all(), gc)) {
    - close(source);
    - return;
    - }
    -
    - if (source < 0) {
    - purple_connection_error(gc, _("Unable to connect."));
    - return;
    - }
    -
    - /* Clear the nonblocking flag
    - This protocol should be updated to support nonblocking I/O if
    - anyone is going to actually use it */
    - fcntl(source, F_SETFL, 0);
    -
    - ndata->fd = source;
    -
    - /* Update the login progress status display */
    - buf = g_strdup_printf("Logging in: %s", purple_account_get_username(gc->account));
    - purple_connection_update_progress(gc, buf, 1, NAPSTER_CONNECT_STEPS);
    - g_free(buf);
    -
    - /* Write our signon data */
    - nap_write_packet(gc, 2, "%s %s 0 \"purple %s\" 0",
    - purple_account_get_username(gc->account),
    - purple_connection_get_password(gc), PP_VERSION);
    -
    - /* And set up the input watcher */
    - gc->inpa = purple_input_add(ndata->fd, PURPLE_INPUT_READ, nap_callback, gc);
    -}
    -
    -static void
    -nap_login(PurpleAccount *account) {
    - PurpleConnection *gc = purple_account_get_connection(account);
    -
    - purple_connection_update_progress(gc, _("Connecting"), 0, NAPSTER_CONNECT_STEPS);
    -
    - gc->proto_data = g_new0(struct nap_data, 1);
    - if (purple_proxy_connect(gc, account,
    - purple_account_get_string(account, "server", NAP_SERVER),
    - purple_account_get_int(account, "port", NAP_PORT),
    - nap_login_connect, gc) != 0) {
    - purple_connection_error(gc, _("Unable to connect."));
    - }
    -}
    -
    -static void
    -nap_close(PurpleConnection *gc) {
    - struct nap_data *ndata = (struct nap_data *)gc->proto_data;
    -
    - if (gc->inpa)
    - purple_input_remove(gc->inpa);
    -
    - if (!ndata)
    - return;
    -
    - close(ndata->fd);
    -
    - g_free(ndata->email);
    - g_free(ndata);
    -}
    -
    -static const char *
    -nap_list_icon(PurpleAccount *a, PurpleBuddy *b) {
    - return "napster";
    -}
    -
    -static GList *
    -nap_status_types(PurpleAccount *account) {
    - GList *types = NULL;
    - PurpleStatusType *type;
    -
    - g_return_val_if_fail(account != NULL, NULL);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
    - NULL, NULL, TRUE, TRUE, FALSE);
    - types = g_list_append(types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
    - NULL, NULL, TRUE, TRUE, FALSE);
    - types = g_list_append(types, type);
    -
    - return types;
    -}
    -
    -static GList *
    -nap_chat_info(PurpleConnection *gc) {
    - GList *m = NULL;
    - struct proto_chat_entry *pce;
    -
    - pce = g_new0(struct proto_chat_entry, 1);
    - pce->label = _("_Group:");
    - pce->identifier = "group";
    - m = g_list_append(m, pce);
    -
    - return m;
    -}
    -
    -static GHashTable *
    -nap_chat_info_defaults(PurpleConnection *gc, const char *chat_name) {
    - GHashTable *defaults;
    -
    - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
    -
    - if (chat_name != NULL)
    - g_hash_table_insert(defaults, "group", g_strdup(chat_name));
    -
    - return defaults;
    -}
    -
    -static PurplePlugin *my_protocol = NULL;
    -
    -static PurplePluginProtocolInfo prpl_info = {
    - OPT_PROTO_CHAT_TOPIC,
    - NULL, /* user_splits */
    - NULL, /* protocol_options */
    - NO_BUDDY_ICONS, /* icon_spec */
    - nap_list_icon, /* list_icon */
    - NULL, /* list_emblems */
    - NULL, /* status_text */
    - NULL, /* tooltip_text */
    - nap_status_types, /* status_types */
    - NULL, /* blist_node_menu */
    - nap_chat_info, /* chat_info */
    - nap_chat_info_defaults, /* chat_info_defaults */
    - nap_login, /* login */
    - nap_close, /* close */
    - nap_send_im, /* send_im */
    - NULL, /* set_info */
    - NULL, /* send_typing */
    - nap_get_info, /* get_info */
    - NULL, /* set_away */
    - NULL, /* set_idle */
    - NULL, /* change_passwd */
    - nap_add_buddy, /* add_buddy */
    - NULL, /* add_buddies */
    - nap_remove_buddy, /* remove_buddy */
    - NULL, /* remove_buddies */
    - NULL, /* add_permit */
    - NULL, /* add_deny */
    - NULL, /* rem_permit */
    - NULL, /* rem_deny */
    - NULL, /* set_permit_deny */
    - nap_join_chat, /* join_chat */
    - NULL, /* reject chat invite */
    - nap_get_chat_name, /* get_chat_name */
    - NULL, /* chat_invite */
    - nap_chat_leave, /* chat_leave */
    - NULL, /* chat_whisper */
    - nap_chat_send, /* chat_send */
    - NULL, /* keepalive */
    - NULL, /* register_user */
    - NULL, /* get_cb_info */
    - NULL, /* get_cb_away */
    - NULL, /* alias_buddy */
    - NULL, /* group_buddy */
    - NULL, /* rename_group */
    - NULL, /* buddy_free */
    - NULL, /* convo_closed */
    - NULL, /* normalize */
    - NULL, /* set_buddy_icon */
    - NULL, /* remove_group */
    - NULL, /* get_cb_real_name */
    - NULL, /* set_chat_topic */
    - NULL, /* find_blist_chat */
    - NULL, /* roomlist_get_list */
    - NULL, /* roomlist_cancel */
    - NULL, /* roomlist_expand_category */
    - NULL, /* can_receive_file */
    - NULL, /* send_file */
    - NULL, /* new_xfer */
    - NULL, /* offline_message */
    - NULL, /* whiteboard_prpl_ops */
    - NULL, /* send_raw */
    - NULL, /* roomlist_room_serialize */
    - NULL, /* unregister_user */
    - NULL, /* send_attention */
    - NULL, /* get_attention_types */
    - sizeof(PurplePluginProtocolInfo), /* struct_size */
    - NULL, /* get_account_text_table */
    - NULL, /* initiate_media */
    - NULL, /* get_media_caps */
    - NULL, /* get_moods */
    - NULL, /* set_public_alias */
    - NULL /* get_public_alias */
    -};
    -
    -static PurplePluginInfo info = {
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_PROTOCOL, /**< type */
    - NULL, /**< ui_requirement */
    - 0, /**< flags */
    - NULL, /**< dependencies */
    - PURPLE_PRIORITY_DEFAULT, /**< priority */
    -
    - "prpl-napster", /**< id */
    - N_("Napster"), /**< name */
    - PP_VERSION, /**< version */
    - /** summary */
    - N_("NAPSTER Protocol Plugin"),
    - /** description */
    - N_("NAPSTER Protocol Plugin"),
    - NULL, /**< author */
    - PP_WEBSITE, /**< homepage */
    -
    - NULL, /**< load */
    - NULL, /**< unload */
    - NULL, /**< destroy */
    -
    - NULL, /**< ui_info */
    - &prpl_info, /**< extra_info */
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin) {
    - PurpleAccountOption *option;
    -
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - option = purple_account_option_string_new(_("Server"), "server", NAP_SERVER);
    - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
    -
    - option = purple_account_option_int_new(_("Port"), "port", 8888);
    - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
    -
    - my_protocol = plugin;
    -
    - info.description = _(info.description);
    - info.summary = _(info.summary);
    -}
    -
    -PURPLE_INIT_PLUGIN(napster, init_plugin, info);
    --- a/nomobility/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# No Mobility
    -
    -status: incomplete
    -dependencies: libpurple
    -authors: Gary Kramlich
    -introduced: 2.5.0
    -
    -Stops you from messaging mobile users.
    -
    --- a/nomobility/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('incomplete') and PURPLE.found()
    - nomobility = shared_library('nomobility',
    - 'nomobility.c',
    - dependencies : [PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'nomobility'
    -endif
    --- a/nomobility/nomobility.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,314 +0,0 @@
    -/*
    - * nomobility - stops you from sending messages to mobile users
    - * Copyright (C) 2008 Gary Kramlich <grim@reaperworld.com>
    - *
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include <time.h>
    -
    -#include <plugin.h>
    -
    -#include <blist.h>
    -#include <cmds.h>
    -#include <conversation.h>
    -#include <debug.h>
    -#include <status.h>
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -#define NO_MOBILITY_QUEUE_KEY "no-mobility"
    -#define NO_MOBILITY_COMMAND "mobile"
    -
    -static PurpleCmdId nomobility_cmd_id = 0;
    -
    -/******************************************************************************
    - * Helpers
    - *****************************************************************************/
    -static void
    -nomobility_list_messages(PurpleConversation *conv, GList *queue) {
    - GList *l = NULL;
    - const gchar *name = purple_conversation_get_name(conv);
    - gint i = 0;
    -
    - if(!queue) {
    - purple_conv_im_write(PURPLE_CONV_IM(conv), name,
    - _("There are no messages in the queue."),
    - PURPLE_MESSAGE_NO_LOG, time(NULL));
    -
    - return;
    - }
    -
    - for(l = queue; l; l = l->next, i++) {
    - gchar *msg = g_strdup_printf(_("%d. %s"), i + 1, (gchar *)l->data);
    -
    - purple_conv_im_write(PURPLE_CONV_IM(conv), name, msg,
    - PURPLE_MESSAGE_NO_LOG, time(NULL));
    -
    - g_free(msg);
    - }
    -}
    -
    -static void
    -nomobility_clear(PurpleConversation *conv, GList *queue) {
    - GList *l = NULL;
    -
    - for(l = queue; l; l = l->next)
    - g_free(l->data);
    -
    - g_list_free(queue);
    -
    - purple_conversation_set_data(conv, NO_MOBILITY_QUEUE_KEY, NULL);
    -}
    -
    -static void
    -nomobility_send(PurpleConversation *conv, GList *queue) {
    - GList *l = NULL;
    - GString *str = g_string_new("");
    -
    - for(l = queue; l; l = l->next) {
    - gchar *msg = (gchar *)l->data;
    - const gchar *suffix = (l->next) ? "\n" : "";
    -
    - g_string_append_printf(str, "%s%s", msg, suffix);
    - }
    -
    - purple_conv_im_send(PURPLE_CONV_IM(conv), str->str);
    -
    - g_string_free(str, TRUE);
    -
    - nomobility_clear(conv, queue);
    -}
    -
    -static void
    -nomobility_delete(PurpleConversation *conv, GList *queue, gint n_msg) {
    - GList *l = NULL;
    - gint i = 0;
    -
    - for(l = queue; i < n_msg - 1; i++, l = l->next);
    -
    - if(l)
    - g_free(l->data);
    -
    - queue = g_list_remove(queue, l);
    -
    - purple_conversation_set_data(conv, NO_MOBILITY_QUEUE_KEY, queue);
    -}
    -
    -/******************************************************************************
    - * Callbacks
    - *****************************************************************************/
    -static void
    -sending_im_msg(PurpleAccount *account, gchar *receiver, gchar **message,
    - gpointer data)
    -{
    - PurpleBuddy *buddy = NULL;
    - PurplePresence *presence = NULL;
    -
    - if(!message || !*message)
    - return;
    -
    - buddy = purple_find_buddy(account, receiver);
    -
    - if(!buddy)
    - return;
    -
    - presence = purple_buddy_get_presence(buddy);
    -
    - if(!presence)
    - return;
    -
    -#if 0
    - if(purple_presence_is_status_primitive_active(presence,
    - PURPLE_STATUS_MOBILE))
    -#endif
    - {
    - PurpleConversation *conv = NULL;
    - gchar *msg = NULL;
    -
    - msg = g_strdup_printf(_("Cancelled message to %s, they are currently "
    - "mobile."),
    - receiver);
    -
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
    - receiver, account);
    -
    - /* if we have the account, add the message to our queue */
    - if(conv) {
    - GList *queue = NULL;
    -
    - queue = purple_conversation_get_data(conv, NO_MOBILITY_QUEUE_KEY);
    -
    - queue = g_list_append(queue, g_strdup(*message));
    - purple_conversation_set_data(conv, NO_MOBILITY_QUEUE_KEY, queue);
    - }
    -
    - /* now actually kill the message */
    - g_free(*message);
    - *message = NULL;
    -
    - /* if we failed to find the conv, write a debug message and bail */
    - if(!conv) {
    - purple_debug_info("nomobility", "%s\n", msg);
    - g_free(msg);
    -
    - return;
    - }
    -
    - /* we have a conv, so note that we queue the message in conv */
    - purple_conv_im_write(PURPLE_CONV_IM(conv), receiver, msg,
    - PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_DELAYED,
    - time(NULL));
    - g_free(msg);
    - }
    -}
    -
    -static PurpleCmdRet
    -nomobility_cmd(PurpleConversation *conv, const gchar *cmd, gchar **args,
    - gchar **error, gpointer data)
    -{
    - GList *queue = NULL;
    - gchar *lower = NULL;
    -
    - if(!*args && !args[0]) {
    - *error = g_strdup("eek!");
    -
    - return PURPLE_CMD_RET_FAILED;
    - }
    -
    - queue = purple_conversation_get_data(conv, NO_MOBILITY_QUEUE_KEY);
    -
    - lower = g_ascii_strdown(args[0], strlen(args[0]));
    -
    - if(strcmp(lower, "clear") == 0) {
    - nomobility_clear(conv, queue);
    - } else if(strcmp(lower, "delete") == 0) {
    - gint n_msg = 0;
    -
    - if(!args[1]) {
    - *error = g_strdup(_("Delete failed: no message number given!"));
    -
    - return PURPLE_CMD_RET_FAILED;
    - }
    -
    - n_msg = atoi(args[1]);
    - if(n_msg < 0 || n_msg >= g_list_length(queue)) {
    - *error =
    - g_strdup_printf(_("Delete failed: no messaged numbered %d!"),
    - n_msg);
    -
    - return PURPLE_CMD_RET_FAILED;
    - }
    -
    - nomobility_delete(conv, queue, n_msg);
    - } else if(strcmp(lower, "list") == 0) {
    - nomobility_list_messages(conv, queue);
    - } else if(strcmp(lower, "sendall") == 0) {
    - nomobility_send(conv, queue);
    - }
    -
    - g_free(lower);
    -
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -/******************************************************************************
    - * Plugin Stuff
    - *****************************************************************************/
    -static gboolean
    -plugin_load(PurplePlugin *plugin) {
    - PurpleCmdFlag flags = PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS;
    - void *conv_handle = purple_conversations_get_handle();
    - gchar *help = NULL;
    -
    - /* signals */
    - purple_signal_connect(conv_handle, "sending-im-msg", plugin,
    - PURPLE_CALLBACK(sending_im_msg), NULL);
    -
    - /* commands */
    - help = g_strdup_printf(_("%s &lt;[clear][clear][delete][send]&gt;\n"
    - "clear Clears all queued messages\n"
    - "delete # Deletes the message numbered #\n"
    - "list Lists all queued messages\n"
    - "sendall Sends all queued messages\n"),
    - NO_MOBILITY_COMMAND);
    -
    - nomobility_cmd_id =
    - purple_cmd_register(NO_MOBILITY_COMMAND, "ws", PURPLE_CMD_P_PLUGIN,
    - flags, NULL,
    - PURPLE_CMD_FUNC(nomobility_cmd), help, NULL);
    - g_free(help);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin)
    -{
    - return TRUE;
    -}
    -
    -static PurplePluginInfo info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD,
    - NULL,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    -
    - "core-plugin_pack-nomobility",
    - NULL,
    - PP_VERSION,
    - NULL,
    - NULL,
    - "Gary Kramlich <grim@reaperworld.com>",
    - PP_WEBSITE,
    -
    - plugin_load,
    - plugin_unload,
    - NULL,
    -
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin)
    -{
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("No Mobility");
    - info.summary = _("Stops you from messaging mobile users");
    - info.description = info.summary;
    -
    -}
    -
    -PURPLE_INIT_PLUGIN(nomobility, init_plugin, info)
    --- a/smartear/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,10 +0,0 @@
    -# Smart Ear
    -
    -status: incomplete
    -depends: libpurple, pidgin (optional), libgnt (optional)
    -authors: John Bailey
    -introduced: not yet finished
    -
    -The Core component of the Smart Ear plugins
    -
    -
    --- a/smartear/gtksmartear.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,123 +0,0 @@
    -/*
    - * gktsmartear.c - GTK+ configuration UI plugin to accompany smartear.
    - * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
    - *
    - * 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 of the
    - * License, 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., 59 Temple Place - Suite 330, Boston, MA
    - * 02111-1307, USA.
    - */
    -
    -#ifdef HAVE_CONFIG_H
    -# include "../pp_config.h"
    -#endif
    -
    -/* Pack/Local headers */
    -#include "../common/pp_internal.h"
    -
    -/* System headers */
    -#include <gdk/gdk.h>
    -#include <glib.h>
    -#include <gtk/gtk.h>
    -
    -/* Pidgin headers */
    -#include <gtkblist.h>
    -#include <gtkplugin.h>
    -
    -static void
    -gtksmartear_blist_menu_cb(PurpleBlistNode *node, gpointer data) {
    -}
    -
    -static void
    -gtksmartear_drawing_blist_menu_cb(PurpleBlistNode *node, GList **menu) {
    - /* Don't do anything if the blistnode won't be saved */
    - if(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)
    - return;
    -
    - /* We don't support setting anything for a chat, since there's no way
    - * to get the name of a chat */
    - if(PURPLE_BLIST_NODE_IS_CHAT(node))
    - return;
    -
    - (*menu) = g_list_append(*menu, purple_menu_action_new(_("SmartEar Options"),
    - PURPLE_CALLBACK(gtksmartear_blist_menu_cb),
    - NULL, NULL));
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin) {
    - purple_signal_connect(purple_blist_get_handle(),
    - "blist-node-extended-menu",
    - plugin,
    - PURPLE_CALLBACK(gtksmartear_drawing_blist_menu_cb),
    - NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin) {
    - return TRUE;
    -}
    -
    -static PurplePluginInfo info = {
    - PURPLE_PLUGIN_MAGIC, /* Magic */
    - PURPLE_MAJOR_VERSION, /* Purple Major Version */
    - PURPLE_MINOR_VERSION, /* Purple Minor Version */
    - PURPLE_PLUGIN_STANDARD, /* plugin type */
    - PIDGIN_PLUGIN_TYPE, /* ui requirement */
    - 0, /* flags */
    - NULL, /* dependencies */
    - PURPLE_PRIORITY_DEFAULT, /* priority */
    -
    - "gtk-plugin_pack-smartear", /* plugin id */
    - NULL, /* name */
    - PP_VERSION, /* version */
    - NULL, /* summary */
    - NULL, /* description */
    - "John Bailey <rekkanoryo@rekkanoryo.org>",
    - PP_WEBSITE, /* website */
    -
    - plugin_load, /* load */
    - plugin_unload, /* unload */
    - NULL, /* destroy */
    -
    - NULL, /* ui_info */
    - NULL, /* extra_info */
    - NULL, /* prefs_info */
    - NULL, /* actions */
    -
    - NULL, /* reserved 1 */
    - NULL, /* reserved 2 */
    - NULL, /* reserved 3 */
    - NULL /* reserved 4 */
    -};
    -
    -static void
    -init_plugin(PurplePlugin *plugin) {
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - info.name = _("SmartEar");
    - info.summary = _("The GTK+ (Pidgin) component of the SmartEar plugin suite");
    - info.description = _("This plugin provides the Pidgin interface to the "
    - "SmartEar plugin suite's functionality. The suite "
    - "allows you to specify sounds per-buddy, per-contact, "
    - "or per-group for specific events.");
    -
    - info.dependencies = g_list_append(NULL, "core-plugin_pack-smartear");
    -}
    -
    -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
    --- a/smartear/interface.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,566 +0,0 @@
    -/*
    - * DO NOT EDIT THIS FILE - it is generated by Glade.
    - */
    -
    -#ifdef HAVE_CONFIG_H
    -# include <config.h>
    -#endif
    -
    -#include <sys/types.h>
    -#include <sys/stat.h>
    -#include <unistd.h>
    -#include <string.h>
    -#include <stdio.h>
    -
    -#include <gdk/gdkkeysyms.h>
    -#include <gtk/gtk.h>
    -
    -#include "callbacks.h"
    -#include "interface.h"
    -#include "support.h"
    -
    -#define GLADE_HOOKUP_OBJECT(component,widget,name) \
    - g_object_set_data_full (G_OBJECT (component), name, \
    - gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
    -
    -#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
    - g_object_set_data (G_OBJECT (component), name, widget)
    -
    -GtkWidget*
    -create_config (void)
    -{
    - GtkWidget *config;
    - GtkWidget *config_vbox;
    - GtkWidget *frame1;
    - GtkWidget *table1;
    - GtkWidget *label9;
    - GtkObject *delay_spin_adj;
    - GtkWidget *delay_spin;
    - GtkWidget *focus_but;
    - GtkWidget *timer_but;
    - GtkWidget *label8;
    - GtkWidget *frame2;
    - GtkWidget *table2;
    - GtkWidget *delete_but;
    - GtkWidget *new_but;
    - GtkWidget *scrolledwindow2;
    - GtkWidget *treeview;
    - GtkWidget *edit_but;
    - GtkWidget *label7;
    - GtkWidget *hbuttonbox1;
    - GtkWidget *revert_but;
    - GtkWidget *save_but;
    -
    - config = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    - gtk_window_set_title (GTK_WINDOW (config), "SmartEar Configuration");
    - gtk_window_set_destroy_with_parent (GTK_WINDOW (config), TRUE);
    -
    - config_vbox = gtk_vbox_new (FALSE, 0);
    - gtk_widget_show (config_vbox);
    - gtk_container_add (GTK_CONTAINER (config), config_vbox);
    - gtk_widget_set_size_request (config_vbox, -1, 640);
    -
    - frame1 = gtk_frame_new (NULL);
    - gtk_widget_show (frame1);
    - gtk_box_pack_start (GTK_BOX (config_vbox), frame1, FALSE, TRUE, 0);
    - gtk_container_set_border_width (GTK_CONTAINER (frame1), 5);
    -
    - table1 = gtk_table_new (3, 2, FALSE);
    - gtk_widget_show (table1);
    - gtk_container_add (GTK_CONTAINER (frame1), table1);
    - gtk_container_set_border_width (GTK_CONTAINER (table1), 5);
    - gtk_table_set_row_spacings (GTK_TABLE (table1), 10);
    - gtk_table_set_col_spacings (GTK_TABLE (table1), 10);
    -
    - label9 = gtk_label_new ("Time delay between playing sounds for a particular buddy:");
    - gtk_widget_show (label9);
    - gtk_table_attach (GTK_TABLE (table1), label9, 0, 1, 0, 1,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    - gtk_misc_set_alignment (GTK_MISC (label9), 0, 0.5);
    -
    - delay_spin_adj = gtk_adjustment_new (60, 0, 1000000, 1, 10, 10);
    - delay_spin = gtk_spin_button_new (GTK_ADJUSTMENT (delay_spin_adj), 1, 0);
    - gtk_widget_show (delay_spin);
    - gtk_table_attach (GTK_TABLE (table1), delay_spin, 1, 2, 0, 1,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    - gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (delay_spin), GTK_UPDATE_IF_VALID);
    -
    - focus_but = gtk_check_button_new_with_mnemonic ("Don't play sounds for the conversation that has focus.");
    - gtk_widget_show (focus_but);
    - gtk_table_attach (GTK_TABLE (table1), focus_but, 0, 2, 1, 2,
    - (GtkAttachOptions) (GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - timer_but = gtk_check_button_new_with_mnemonic ("Also play sounds if you don't respond to a particular IM within a delay period.");
    - gtk_widget_show (timer_but);
    - gtk_table_attach (GTK_TABLE (table1), timer_but, 0, 2, 2, 3,
    - (GtkAttachOptions) (GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - label8 = gtk_label_new ("Options");
    - gtk_widget_show (label8);
    - gtk_frame_set_label_widget (GTK_FRAME (frame1), label8);
    -
    - frame2 = gtk_frame_new (NULL);
    - gtk_widget_show (frame2);
    - gtk_box_pack_start (GTK_BOX (config_vbox), frame2, TRUE, TRUE, 0);
    - gtk_container_set_border_width (GTK_CONTAINER (frame2), 5);
    -
    - table2 = gtk_table_new (2, 3, FALSE);
    - gtk_widget_show (table2);
    - gtk_container_add (GTK_CONTAINER (frame2), table2);
    - gtk_container_set_border_width (GTK_CONTAINER (table2), 5);
    - gtk_table_set_row_spacings (GTK_TABLE (table2), 10);
    - gtk_table_set_col_spacings (GTK_TABLE (table2), 10);
    -
    - delete_but = gtk_button_new_from_stock ("gtk-delete");
    - gtk_widget_show (delete_but);
    - gtk_table_attach (GTK_TABLE (table2), delete_but, 2, 3, 1, 2,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    - GTK_WIDGET_SET_FLAGS (delete_but, GTK_CAN_DEFAULT);
    -
    - new_but = gtk_button_new_from_stock ("gtk-new");
    - gtk_widget_show (new_but);
    - gtk_table_attach (GTK_TABLE (table2), new_but, 0, 1, 1, 2,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    - GTK_WIDGET_SET_FLAGS (new_but, GTK_CAN_DEFAULT);
    -
    - scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
    - gtk_widget_show (scrolledwindow2);
    - gtk_table_attach (GTK_TABLE (table2), scrolledwindow2, 0, 3, 0, 1,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
    - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    -
    - treeview = gtk_tree_view_new ();
    - gtk_widget_show (treeview);
    - gtk_container_add (GTK_CONTAINER (scrolledwindow2), treeview);
    -
    - edit_but = gtk_button_new_from_stock ("gtk-properties");
    - gtk_widget_show (edit_but);
    - gtk_table_attach (GTK_TABLE (table2), edit_but, 1, 2, 1, 2,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - label7 = gtk_label_new ("Entries");
    - gtk_widget_show (label7);
    - gtk_frame_set_label_widget (GTK_FRAME (frame2), label7);
    -
    - hbuttonbox1 = gtk_hbutton_box_new ();
    - gtk_widget_show (hbuttonbox1);
    - gtk_box_pack_start (GTK_BOX (config_vbox), hbuttonbox1, FALSE, TRUE, 0);
    - gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 5);
    - gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_SPREAD);
    -
    - revert_but = gtk_button_new_from_stock ("gtk-revert-to-saved");
    - gtk_widget_show (revert_but);
    - gtk_container_add (GTK_CONTAINER (hbuttonbox1), revert_but);
    - GTK_WIDGET_SET_FLAGS (revert_but, GTK_CAN_DEFAULT);
    -
    - save_but = gtk_button_new_from_stock ("gtk-save");
    - gtk_widget_show (save_but);
    - gtk_container_add (GTK_CONTAINER (hbuttonbox1), save_but);
    - GTK_WIDGET_SET_FLAGS (save_but, GTK_CAN_DEFAULT);
    -
    - g_signal_connect ((gpointer) config_vbox, "destroy",
    - G_CALLBACK (on_config_destroy),
    - NULL);
    - g_signal_connect ((gpointer) delay_spin, "changed",
    - G_CALLBACK (on_delay_changed),
    - NULL);
    - g_signal_connect ((gpointer) focus_but, "toggled",
    - G_CALLBACK (on_focus_toggled),
    - NULL);
    - g_signal_connect ((gpointer) timer_but, "toggled",
    - G_CALLBACK (on_timer_toggled),
    - NULL);
    - g_signal_connect ((gpointer) delete_but, "clicked",
    - G_CALLBACK (on_delete_clicked),
    - NULL);
    - g_signal_connect ((gpointer) new_but, "clicked",
    - G_CALLBACK (on_new_clicked),
    - NULL);
    - g_signal_connect ((gpointer) edit_but, "clicked",
    - G_CALLBACK (on_edit_clicked),
    - NULL);
    - g_signal_connect ((gpointer) revert_but, "clicked",
    - G_CALLBACK (on_revert_clicked),
    - NULL);
    - g_signal_connect ((gpointer) save_but, "clicked",
    - G_CALLBACK (on_save_clicked),
    - NULL);
    -
    - /* Store pointers to all widgets, for use by lookup_widget(). */
    - GLADE_HOOKUP_OBJECT_NO_REF (config, config, "config");
    - GLADE_HOOKUP_OBJECT (config, config_vbox, "config_vbox");
    - GLADE_HOOKUP_OBJECT (config, frame1, "frame1");
    - GLADE_HOOKUP_OBJECT (config, table1, "table1");
    - GLADE_HOOKUP_OBJECT (config, label9, "label9");
    - GLADE_HOOKUP_OBJECT (config, delay_spin, "delay_spin");
    - GLADE_HOOKUP_OBJECT (config, focus_but, "focus_but");
    - GLADE_HOOKUP_OBJECT (config, timer_but, "timer_but");
    - GLADE_HOOKUP_OBJECT (config, label8, "label8");
    - GLADE_HOOKUP_OBJECT (config, frame2, "frame2");
    - GLADE_HOOKUP_OBJECT (config, table2, "table2");
    - GLADE_HOOKUP_OBJECT (config, delete_but, "delete_but");
    - GLADE_HOOKUP_OBJECT (config, new_but, "new_but");
    - GLADE_HOOKUP_OBJECT (config, scrolledwindow2, "scrolledwindow2");
    - GLADE_HOOKUP_OBJECT (config, treeview, "treeview");
    - GLADE_HOOKUP_OBJECT (config, edit_but, "edit_but");
    - GLADE_HOOKUP_OBJECT (config, label7, "label7");
    - GLADE_HOOKUP_OBJECT (config, hbuttonbox1, "hbuttonbox1");
    - GLADE_HOOKUP_OBJECT (config, revert_but, "revert_but");
    - GLADE_HOOKUP_OBJECT (config, save_but, "save_but");
    -
    - return config;
    -}
    -
    -GtkWidget*
    -create_file_browse (void)
    -{
    - GtkWidget *file_browse;
    - GtkWidget *ok_button1;
    - GtkWidget *cancel_button1;
    -
    - file_browse = gtk_file_selection_new ("Select Sound");
    - gtk_container_set_border_width (GTK_CONTAINER (file_browse), 10);
    - gtk_window_set_destroy_with_parent (GTK_WINDOW (file_browse), TRUE);
    - gtk_window_set_type_hint (GTK_WINDOW (file_browse), GDK_WINDOW_TYPE_HINT_DIALOG);
    -
    - ok_button1 = GTK_FILE_SELECTION (file_browse)->ok_button;
    - gtk_widget_show (ok_button1);
    - GTK_WIDGET_SET_FLAGS (ok_button1, GTK_CAN_DEFAULT);
    -
    - cancel_button1 = GTK_FILE_SELECTION (file_browse)->cancel_button;
    - gtk_widget_show (cancel_button1);
    - GTK_WIDGET_SET_FLAGS (cancel_button1, GTK_CAN_DEFAULT);
    -
    - g_signal_connect ((gpointer) file_browse, "destroy",
    - G_CALLBACK (on_file_browse_destroy),
    - NULL);
    - g_signal_connect ((gpointer) ok_button1, "clicked",
    - G_CALLBACK (on_browse_ok_clicked),
    - NULL);
    - g_signal_connect_swapped ((gpointer) ok_button1, "clicked",
    - G_CALLBACK (gtk_widget_destroy),
    - GTK_OBJECT (file_browse));
    - g_signal_connect_swapped ((gpointer) cancel_button1, "clicked",
    - G_CALLBACK (gtk_widget_destroy),
    - GTK_OBJECT (file_browse));
    -
    - /* Store pointers to all widgets, for use by lookup_widget(). */
    - GLADE_HOOKUP_OBJECT_NO_REF (file_browse, file_browse, "file_browse");
    - GLADE_HOOKUP_OBJECT_NO_REF (file_browse, ok_button1, "ok_button1");
    - GLADE_HOOKUP_OBJECT_NO_REF (file_browse, cancel_button1, "cancel_button1");
    -
    - return file_browse;
    -}
    -
    -GtkWidget*
    -create_edit_win (void)
    -{
    - GtkWidget *edit_win;
    - GtkWidget *vbox1;
    - GtkWidget *hbox1;
    - GtkWidget *label29;
    - GtkWidget *name_entry;
    - GtkWidget *label30;
    - GtkWidget *type_option;
    - GtkWidget *menu1;
    - GtkWidget *item_buddy;
    - GtkWidget *item_group;
    - GtkWidget *hbuttonbox2;
    - GtkWidget *applysave_but;
    - GtkWidget *apply_but;
    - GtkWidget *cancel_but;
    - GtkWidget *frame3;
    - GtkWidget *table5;
    - GtkWidget *label31;
    - GtkWidget *label34;
    - GtkWidget *label33;
    - GtkWidget *label32;
    - GtkWidget *unaway_sound_entry;
    - GtkWidget *unidle_sound_entry;
    - GtkWidget *signon_sound_entry;
    - GtkWidget *unaway_test_but;
    - GtkWidget *unidle_test_but;
    - GtkWidget *signon_test_but;
    - GtkWidget *unaway_browse_but;
    - GtkWidget *unidle_browse_but;
    - GtkWidget *signon_browse_but;
    - GtkWidget *im_browse_but;
    - GtkWidget *im_sound_entry;
    - GtkWidget *im_test_but;
    - GtkWidget *label24;
    -
    - edit_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    - gtk_widget_set_size_request (edit_win, 600, 300);
    - gtk_window_set_title (GTK_WINDOW (edit_win), "Edit Entry");
    - gtk_window_set_default_size (GTK_WINDOW (edit_win), 600, 300);
    -
    - vbox1 = gtk_vbox_new (FALSE, 0);
    - gtk_widget_show (vbox1);
    - gtk_container_add (GTK_CONTAINER (edit_win), vbox1);
    -
    - hbox1 = gtk_hbox_new (FALSE, 5);
    - gtk_widget_show (hbox1);
    - gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, TRUE, 10);
    - gtk_container_set_border_width (GTK_CONTAINER (hbox1), 5);
    -
    - label29 = gtk_label_new ("Name:");
    - gtk_widget_show (label29);
    - gtk_box_pack_start (GTK_BOX (hbox1), label29, FALSE, FALSE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label29), 1, 0.5);
    -
    - name_entry = gtk_entry_new ();
    - gtk_widget_show (name_entry);
    - gtk_box_pack_start (GTK_BOX (hbox1), name_entry, TRUE, TRUE, 0);
    -
    - label30 = gtk_label_new ("Type:");
    - gtk_widget_show (label30);
    - gtk_box_pack_start (GTK_BOX (hbox1), label30, FALSE, TRUE, 0);
    - gtk_misc_set_alignment (GTK_MISC (label30), 1, 0.5);
    -
    - type_option = gtk_option_menu_new ();
    - gtk_widget_show (type_option);
    - gtk_box_pack_start (GTK_BOX (hbox1), type_option, FALSE, FALSE, 0);
    -
    - menu1 = gtk_menu_new ();
    -
    - item_buddy = gtk_menu_item_new_with_mnemonic ("Buddy");
    - gtk_widget_show (item_buddy);
    - gtk_container_add (GTK_CONTAINER (menu1), item_buddy);
    -
    - item_group = gtk_menu_item_new_with_mnemonic ("Group");
    - gtk_widget_show (item_group);
    - gtk_container_add (GTK_CONTAINER (menu1), item_group);
    -
    - gtk_option_menu_set_menu (GTK_OPTION_MENU (type_option), menu1);
    -
    - hbuttonbox2 = gtk_hbutton_box_new ();
    - gtk_widget_show (hbuttonbox2);
    - gtk_box_pack_end (GTK_BOX (vbox1), hbuttonbox2, FALSE, TRUE, 0);
    - gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox2), 5);
    - gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_SPREAD);
    -
    - applysave_but = gtk_button_new_with_mnemonic ("Apply and Save");
    - gtk_widget_show (applysave_but);
    - gtk_container_add (GTK_CONTAINER (hbuttonbox2), applysave_but);
    - GTK_WIDGET_SET_FLAGS (applysave_but, GTK_CAN_DEFAULT);
    -
    - apply_but = gtk_button_new_from_stock ("gtk-apply");
    - gtk_widget_show (apply_but);
    - gtk_container_add (GTK_CONTAINER (hbuttonbox2), apply_but);
    - GTK_WIDGET_SET_FLAGS (apply_but, GTK_CAN_DEFAULT);
    -
    - cancel_but = gtk_button_new_from_stock ("gtk-cancel");
    - gtk_widget_show (cancel_but);
    - gtk_container_add (GTK_CONTAINER (hbuttonbox2), cancel_but);
    - GTK_WIDGET_SET_FLAGS (cancel_but, GTK_CAN_DEFAULT);
    -
    - frame3 = gtk_frame_new (NULL);
    - gtk_widget_show (frame3);
    - gtk_box_pack_start (GTK_BOX (vbox1), frame3, FALSE, FALSE, 0);
    - gtk_widget_set_size_request (frame3, -1, 200);
    - gtk_container_set_border_width (GTK_CONTAINER (frame3), 5);
    -
    - table5 = gtk_table_new (4, 5, FALSE);
    - gtk_widget_show (table5);
    - gtk_container_add (GTK_CONTAINER (frame3), table5);
    - gtk_widget_set_size_request (table5, 600, 400);
    - gtk_container_set_border_width (GTK_CONTAINER (table5), 5);
    - gtk_table_set_row_spacings (GTK_TABLE (table5), 10);
    - gtk_table_set_col_spacings (GTK_TABLE (table5), 10);
    -
    - label31 = gtk_label_new ("Play On IM:");
    - gtk_widget_show (label31);
    - gtk_table_attach (GTK_TABLE (table5), label31, 0, 1, 0, 1,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    - gtk_misc_set_alignment (GTK_MISC (label31), 0, 0.5);
    -
    - label34 = gtk_label_new ("Play On Unaway:");
    - gtk_widget_show (label34);
    - gtk_table_attach (GTK_TABLE (table5), label34, 0, 1, 3, 4,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    - gtk_misc_set_alignment (GTK_MISC (label34), 0, 0.5);
    -
    - label33 = gtk_label_new ("Play On Unidle:");
    - gtk_widget_show (label33);
    - gtk_table_attach (GTK_TABLE (table5), label33, 0, 1, 2, 3,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    - gtk_misc_set_alignment (GTK_MISC (label33), 0, 0.5);
    -
    - label32 = gtk_label_new ("Play On Signon:");
    - gtk_widget_show (label32);
    - gtk_table_attach (GTK_TABLE (table5), label32, 0, 1, 1, 2,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    - gtk_misc_set_alignment (GTK_MISC (label32), 0, 0.5);
    -
    - unaway_sound_entry = gtk_entry_new ();
    - gtk_widget_show (unaway_sound_entry);
    - gtk_table_attach (GTK_TABLE (table5), unaway_sound_entry, 1, 3, 3, 4,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - unidle_sound_entry = gtk_entry_new ();
    - gtk_widget_show (unidle_sound_entry);
    - gtk_table_attach (GTK_TABLE (table5), unidle_sound_entry, 1, 3, 2, 3,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - signon_sound_entry = gtk_entry_new ();
    - gtk_widget_show (signon_sound_entry);
    - gtk_table_attach (GTK_TABLE (table5), signon_sound_entry, 1, 3, 1, 2,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - unaway_test_but = gtk_button_new_with_mnemonic ("Test");
    - gtk_widget_show (unaway_test_but);
    - gtk_table_attach (GTK_TABLE (table5), unaway_test_but, 4, 5, 3, 4,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - unidle_test_but = gtk_button_new_with_mnemonic ("Test");
    - gtk_widget_show (unidle_test_but);
    - gtk_table_attach (GTK_TABLE (table5), unidle_test_but, 4, 5, 2, 3,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - signon_test_but = gtk_button_new_with_mnemonic ("Test");
    - gtk_widget_show (signon_test_but);
    - gtk_table_attach (GTK_TABLE (table5), signon_test_but, 4, 5, 1, 2,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - unaway_browse_but = gtk_button_new_from_stock ("gtk-open");
    - gtk_widget_show (unaway_browse_but);
    - gtk_table_attach (GTK_TABLE (table5), unaway_browse_but, 3, 4, 3, 4,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - unidle_browse_but = gtk_button_new_from_stock ("gtk-open");
    - gtk_widget_show (unidle_browse_but);
    - gtk_table_attach (GTK_TABLE (table5), unidle_browse_but, 3, 4, 2, 3,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - signon_browse_but = gtk_button_new_from_stock ("gtk-open");
    - gtk_widget_show (signon_browse_but);
    - gtk_table_attach (GTK_TABLE (table5), signon_browse_but, 3, 4, 1, 2,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - im_browse_but = gtk_button_new_from_stock ("gtk-open");
    - gtk_widget_show (im_browse_but);
    - gtk_table_attach (GTK_TABLE (table5), im_browse_but, 3, 4, 0, 1,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - im_sound_entry = gtk_entry_new ();
    - gtk_widget_show (im_sound_entry);
    - gtk_table_attach (GTK_TABLE (table5), im_sound_entry, 1, 3, 0, 1,
    - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - im_test_but = gtk_button_new_with_mnemonic ("Test");
    - gtk_widget_show (im_test_but);
    - gtk_table_attach (GTK_TABLE (table5), im_test_but, 4, 5, 0, 1,
    - (GtkAttachOptions) (0),
    - (GtkAttachOptions) (0), 0, 0);
    -
    - label24 = gtk_label_new ("Sound Events");
    - gtk_widget_show (label24);
    - gtk_frame_set_label_widget (GTK_FRAME (frame3), label24);
    -
    - g_signal_connect ((gpointer) edit_win, "destroy",
    - G_CALLBACK (on_edit_win_destroy),
    - NULL);
    - g_signal_connect ((gpointer) applysave_but, "clicked",
    - G_CALLBACK (on_apply_clicked),
    - NULL);
    - g_signal_connect ((gpointer) applysave_but, "clicked",
    - G_CALLBACK (on_save_clicked),
    - NULL);
    - g_signal_connect_swapped ((gpointer) applysave_but, "clicked",
    - G_CALLBACK (gtk_widget_destroy),
    - GTK_OBJECT (edit_win));
    - g_signal_connect ((gpointer) apply_but, "clicked",
    - G_CALLBACK (on_apply_clicked),
    - NULL);
    - g_signal_connect_swapped ((gpointer) apply_but, "clicked",
    - G_CALLBACK (gtk_widget_destroy),
    - GTK_OBJECT (edit_win));
    - g_signal_connect_swapped ((gpointer) cancel_but, "clicked",
    - G_CALLBACK (gtk_widget_destroy),
    - GTK_OBJECT (edit_win));
    - g_signal_connect ((gpointer) unaway_test_but, "clicked",
    - G_CALLBACK (on_unaway_test_clicked),
    - NULL);
    - g_signal_connect ((gpointer) unidle_test_but, "clicked",
    - G_CALLBACK (on_unidle_test_clicked),
    - NULL);
    - g_signal_connect ((gpointer) signon_test_but, "clicked",
    - G_CALLBACK (on_signon_test_clicked),
    - NULL);
    - g_signal_connect ((gpointer) unaway_browse_but, "clicked",
    - G_CALLBACK (on_unaway_browse_clicked),
    - NULL);
    - g_signal_connect ((gpointer) unidle_browse_but, "clicked",
    - G_CALLBACK (on_unidle_browse_clicked),
    - NULL);
    - g_signal_connect ((gpointer) signon_browse_but, "clicked",
    - G_CALLBACK (on_signon_browse_clicked),
    - NULL);
    - g_signal_connect ((gpointer) im_browse_but, "clicked",
    - G_CALLBACK (on_im_browse_clicked),
    - NULL);
    - g_signal_connect ((gpointer) im_test_but, "clicked",
    - G_CALLBACK (on_im_test_clicked),
    - NULL);
    -
    - /* Store pointers to all widgets, for use by lookup_widget(). */
    - GLADE_HOOKUP_OBJECT_NO_REF (edit_win, edit_win, "edit_win");
    - GLADE_HOOKUP_OBJECT (edit_win, vbox1, "vbox1");
    - GLADE_HOOKUP_OBJECT (edit_win, hbox1, "hbox1");
    - GLADE_HOOKUP_OBJECT (edit_win, label29, "label29");
    - GLADE_HOOKUP_OBJECT (edit_win, name_entry, "name_entry");
    - GLADE_HOOKUP_OBJECT (edit_win, label30, "label30");
    - GLADE_HOOKUP_OBJECT (edit_win, type_option, "type_option");
    - GLADE_HOOKUP_OBJECT (edit_win, menu1, "menu1");
    - GLADE_HOOKUP_OBJECT (edit_win, item_buddy, "item_buddy");
    - GLADE_HOOKUP_OBJECT (edit_win, item_group, "item_group");
    - GLADE_HOOKUP_OBJECT (edit_win, hbuttonbox2, "hbuttonbox2");
    - GLADE_HOOKUP_OBJECT (edit_win, applysave_but, "applysave_but");
    - GLADE_HOOKUP_OBJECT (edit_win, apply_but, "apply_but");
    - GLADE_HOOKUP_OBJECT (edit_win, cancel_but, "cancel_but");
    - GLADE_HOOKUP_OBJECT (edit_win, frame3, "frame3");
    - GLADE_HOOKUP_OBJECT (edit_win, table5, "table5");
    - GLADE_HOOKUP_OBJECT (edit_win, label31, "label31");
    - GLADE_HOOKUP_OBJECT (edit_win, label34, "label34");
    - GLADE_HOOKUP_OBJECT (edit_win, label33, "label33");
    - GLADE_HOOKUP_OBJECT (edit_win, label32, "label32");
    - GLADE_HOOKUP_OBJECT (edit_win, unaway_sound_entry, "unaway_sound_entry");
    - GLADE_HOOKUP_OBJECT (edit_win, unidle_sound_entry, "unidle_sound_entry");
    - GLADE_HOOKUP_OBJECT (edit_win, signon_sound_entry, "signon_sound_entry");
    - GLADE_HOOKUP_OBJECT (edit_win, unaway_test_but, "unaway_test_but");
    - GLADE_HOOKUP_OBJECT (edit_win, unidle_test_but, "unidle_test_but");
    - GLADE_HOOKUP_OBJECT (edit_win, signon_test_but, "signon_test_but");
    - GLADE_HOOKUP_OBJECT (edit_win, unaway_browse_but, "unaway_browse_but");
    - GLADE_HOOKUP_OBJECT (edit_win, unidle_browse_but, "unidle_browse_but");
    - GLADE_HOOKUP_OBJECT (edit_win, signon_browse_but, "signon_browse_but");
    - GLADE_HOOKUP_OBJECT (edit_win, im_browse_but, "im_browse_but");
    - GLADE_HOOKUP_OBJECT (edit_win, im_sound_entry, "im_sound_entry");
    - GLADE_HOOKUP_OBJECT (edit_win, im_test_but, "im_test_but");
    - GLADE_HOOKUP_OBJECT (edit_win, label24, "label24");
    -
    - return edit_win;
    -}
    -
    --- a/smartear/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,29 +0,0 @@
    -if TYPES.contains('incomplete') and PURPLE.found()
    - smartear = shared_library('smartear',
    - 'smartear.c',
    - dependencies : [PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'smartear'
    -endif
    -
    -if TYPES.contains('incomplete') and PIDGIN.found()
    - gtksmartear = shared_module('gtksmartear',
    - 'gtksmartear.c',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'gtksmartear'
    -endif
    -
    -if TYPES.contains('incomplete') and FINCH.found()
    - gntsmartear = shared_library('gntsmartear',
    - 'gntsmartear.c',
    - dependencies : [FINCH, GNT],
    - name_prefix : '',
    - install : true,
    - install_dir : FINCH_LIBDIR)
    - PP_FINCH_BUILD += 'gntsmartear'
    -endif
    --- a/smartear/smartear.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,266 +0,0 @@
    -/*
    - * smartear.c - SmartEar plugin for libpurple
    - * Copyright (c) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
    - *
    - * Original code copyright (c) 2003-2007 Matt Perry
    - *
    - * 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 of the
    - * License, 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., 59 Temple Place - Suite 330, Boston, MA
    - * 02111-1307, USA
    - *
    - * This plugin is a hidden plugin. It follows preferences set by other plugins
    - * which are specific to the libpurple UI in use. The GTK+ plugin for Pidgin
    - * and the GNT plugin for Finch will list this plugin as a dependency, causing
    - * libpurple to load this plugin.
    - */
    -
    -#define PURPLE_PLUGINS
    -
    -#ifdef HAVE_CONFIG_H
    -# include "../pp_config.h"
    -#endif /* HAVE_CONFIG_H */
    -
    -#include "../common/pp_internal.h"
    -
    -/* libpurple headers */
    -#include <blist.h>
    -#include <debug.h>
    -#include <plugin.h>
    -#include <pluginpref.h>
    -#include <sound.h>
    -#include <signals.h>
    -
    -/* Glib header */
    -#include <glib.h>
    -
    -/* Enumerations */
    -
    -typedef enum {
    - SMARTEAR_EVENT_SIGNON,
    - SMARTEAR_EVENT_SIGNOFF,
    - SMARTEAR_EVENT_IDLECHG,
    - SMARTEAR_EVENT_RECEIVEDMSG,
    - SMARTEAR_EVENT_SENTMSG
    -} SmartEarEvent;
    -
    -/* Helpers */
    -
    -static const char *
    -smartear_event_get_setting_string(SmartEarEvent event)
    -{
    - const char *setting = NULL;
    -
    - switch(event) {
    - case SMARTEAR_EVENT_SIGNON:
    - setting = "signon_sound";
    - break;
    - case SMARTEAR_EVENT_SIGNOFF:
    - setting = "signoff_sound";
    - break;
    - case SMARTEAR_EVENT_IDLECHG:
    - setting = "idlechg_sound";
    - break;
    - case SMARTEAR_EVENT_RECEIVEDMSG:
    - setting = "receivedmsg_sound";
    - break;
    - case SMARTEAR_EVENT_SENTMSG:
    - setting = "sentmsg_sound";
    - break;
    - }
    -
    - return setting;
    -}
    -
    -static const char *
    -smartear_sound_get_default(SmartEarEvent event)
    -{
    - const char *prefname = NULL, *prefval = NULL;
    - char *prefpath = NULL;
    -
    - prefname = smartear_event_get_setting_string(event);
    - prefpath = g_strdup_printf("/plugins/core/smartear/%s", prefname);
    - prefval = purple_prefs_get_string(prefpath);
    -
    - g_free(prefpath);
    -
    - return prefval;
    -}
    -
    -static const char *
    -smartear_sound_determine(const char *bfile, const char *cfile, const char *gfile, SmartEarEvent event)
    -{
    - const char *pfile = NULL;
    -
    - /* if the string is "(Default)" then set the pointer to NULL */
    - if(!g_ascii_strcasecmp(bfile, "(Default)"))
    - bfile = NULL;
    - if(!g_ascii_strcasecmp(cfile, "(Default)"))
    - cfile = NULL;
    - if(!g_ascii_strcasecmp(gfile, "(Default)"))
    - gfile = NULL;
    -
    - /* determine the sound to play - if the pointer is NULL, try falling back
    - * to another sound - if no sound defined at any level, fall back to the
    - * default */
    - if(!bfile)
    - if(!cfile)
    - if(!gfile)
    - pfile = smartear_sound_get_default(event);
    - else
    - pfile = gfile;
    - else
    - pfile = cfile;
    - else
    - pfile = bfile;
    -
    - return pfile;
    -}
    -
    -static void
    -smartear_sound_play(PurpleBuddy *buddy, PurpleAccount *account, SmartEarEvent event)
    -{
    - const char *bfile = NULL, *cfile = NULL, *gfile = NULL, *pfile = NULL, *setting = NULL;
    - PurpleBlistNode *bnode = (PurpleBlistNode *)buddy,
    - *cnode = (PurpleBlistNode *)(bnode->parent),
    - *gnode = (PurpleBlistNode *)(cnode->parent);
    -
    - /* get the setting string */
    - setting = smartear_event_get_setting_string(event);
    -
    - /* grab the settings from each blist node in the hierarchy */
    - bfile = purple_blist_node_get_string(bnode, setting);
    - cfile = purple_blist_node_get_string(cnode, setting);
    - gfile = purple_blist_node_get_string(gnode, setting);
    -
    - /* determine which sound to play */
    - pfile = smartear_sound_determine(bfile, cfile, gfile, event);
    -
    - if(pfile)
    - purple_sound_play_file(pfile, account);
    -}
    -
    -/* Callbacks */
    -
    -static void
    -smartear_cb_sent_msg(PurpleAccount *account, const gchar *receiver, const gchar *message)
    -{
    - PurpleBuddy *buddy = purple_find_buddy(account, receiver);
    -
    - smartear_sound_play(buddy, account, SMARTEAR_EVENT_SENTMSG);
    -}
    -
    -static void
    -smartear_cb_received_msg(PurpleAccount *account, gchar *sender, char *message,
    - PurpleConversation *conv, PurpleMessageFlags flags)
    -{
    - if(!(flags & PURPLE_MESSAGE_SYSTEM)) {
    - PurpleBuddy *buddy = purple_find_buddy(account, sender);
    -
    - smartear_sound_play(buddy, account, SMARTEAR_EVENT_RECEIVEDMSG);
    - }
    -}
    -
    -static void
    -smartear_cb_idle(PurpleBuddy *buddy, gboolean wasidle, gboolean nowidle)
    -{
    - smartear_sound_play(buddy, purple_buddy_get_account(buddy), SMARTEAR_EVENT_IDLECHG);
    -}
    -
    -static void
    -smartear_cb_signoff(PurpleBuddy *buddy)
    -{
    - smartear_sound_play(buddy, purple_buddy_get_account(buddy), SMARTEAR_EVENT_SIGNOFF);
    -}
    -
    -static void
    -smartear_cb_signon(PurpleBuddy *buddy)
    -{
    - smartear_sound_play(buddy, purple_buddy_get_account(buddy), SMARTEAR_EVENT_SIGNON);
    -}
    -
    -/* Purple Plugin stuff */
    -
    -static gboolean
    -smartear_load(PurplePlugin *plugin)
    -{
    - void *blist_handle = purple_blist_get_handle();
    - void *conv_handle = purple_conversations_get_handle();
    -
    - /* blist signals we need to detect the buddy's activities */
    - purple_signal_connect(blist_handle, "buddy-signed-on", plugin,
    - PURPLE_CALLBACK(smartear_cb_signon), NULL);
    - purple_signal_connect(blist_handle, "buddy-signed-off", plugin,
    - PURPLE_CALLBACK(smartear_cb_signoff), NULL);
    - purple_signal_connect(blist_handle, "buddy-idle-changed", plugin,
    - PURPLE_CALLBACK(smartear_cb_idle), NULL);
    -
    - /* conv signals we need to detect activities */
    - purple_signal_connect(conv_handle, "received-im-msg", plugin,
    - PURPLE_CALLBACK(smartear_cb_received_msg), NULL);
    - purple_signal_connect(conv_handle, "sent-im-msg", plugin,
    - PURPLE_CALLBACK(smartear_cb_sent_msg), NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -smartear_unload(PurplePlugin *plugin)
    -{
    - return TRUE;
    -}
    -
    -PurplePluginInfo smartear_info =
    -{
    - PURPLE_PLUGIN_MAGIC, /* Magic, my ass */
    - PURPLE_MAJOR_VERSION, /* libpurple major version */
    - PURPLE_MINOR_VERSION, /* libpurple minor version */
    - PURPLE_PLUGIN_STANDARD, /* plugin type - this is a normal plugin */
    - NULL, /* UI requirement - we're invisible! */
    - PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags - we're invisible! */
    - NULL, /* dependencies - we have none */
    - PURPLE_PRIORITY_DEFAULT, /* priority - nothing special here */
    - "core-plugin_pack-smartear", /* Plugin ID */
    - NULL, /* name - defined later for i18n */
    - PP_VERSION, /* plugin version - use plugin pack version */
    - NULL, /* summary - defined later for i18n */
    - NULL, /* description - defined later for i18n */
    - "John Bailey <rekkanoryo@rekkanoryo.org>", /* author */
    - PP_WEBSITE, /* plugin website - use plugin pack website */
    - smartear_load, /* plugin load - purple calls this when loading */
    - smartear_unload, /* plugin unload - purple calls this when unloading */
    - NULL, /* plugin destroy - we don't need one */
    - NULL, /* ui_info - we don't need this */
    - NULL, /* extra_info - we don't need this */
    - NULL, /* prefs_info - we don't need this yet */
    - NULL, /* actions - we don't have any */
    - NULL, /* reserved 1 */
    - NULL, /* reserved 2 */
    - NULL, /* reserved 3 */
    - NULL /* reserved 4 */
    -};
    -
    -static void
    -smartear_init(PurplePlugin *plugin)
    -{
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif
    -
    - smartear_info.name = _("Smart Ear - Hidden Core Plugin");
    - smartear_info.summary = _("The Core component of the Smart Ear plugins");
    - smartear_info.description = _("The Core component of the Smart Ear plugins");
    -}
    -
    -PURPLE_INIT_PLUGIN(smartear, smartear_init, smartear_info)
    --- a/stocker/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,10 +0,0 @@
    -# Stocker
    -
    -status: incomplete
    -dependencies: pidgin
    -authors: Gary Kramlich
    -introduced: 1.0beta1
    -notes: This plugin partially works but is buggy and in need of TLC.
    -
    -Adds a stock ticker similar to the one in the Windows AIM client to the bottom of the buddy list.
    -
    --- a/stocker/gtkticker.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,513 +0,0 @@
    -/* GTK - The GIMP Toolkit
    - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
    - *
    - * 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., 51 Franklin Street, Fifth Floor,
    - * Boston, MA 02111-1301, USA.
    - */
    -
    -/*
    - * GtkTicker Copyright 2000 Syd Logan
    - */
    -
    -#include "gtkticker.h"
    -#include <gtk/gtk.h>
    -
    -static void gtk_ticker_compute_offsets (GtkTicker *ticker);
    -static void gtk_ticker_class_init (GtkTickerClass *klass);
    -static void gtk_ticker_init (GtkTicker *ticker);
    -static void gtk_ticker_map (GtkWidget *widget);
    -static void gtk_ticker_realize (GtkWidget *widget);
    -static void gtk_ticker_size_request (GtkWidget *widget,
    - GtkRequisition *requisition);
    -static void gtk_ticker_size_allocate (GtkWidget *widget,
    - GtkAllocation *allocation);
    -static void gtk_ticker_add_real (GtkContainer *container,
    - GtkWidget *widget);
    -static void gtk_ticker_remove_real (GtkContainer *container,
    - GtkWidget *widget);
    -static void gtk_ticker_forall (GtkContainer *container,
    - gboolean include_internals,
    - GtkCallback callback,
    - gpointer callback_data);
    -static GtkType gtk_ticker_child_type (GtkContainer *container);
    -
    -
    -static GtkContainerClass *parent_class = NULL;
    -
    -
    -GType gtk_ticker_get_type (void)
    -{
    - static GType ticker_type = 0;
    -
    - ticker_type = g_type_from_name("GtkTicker");
    -
    - if (ticker_type == 0)
    - {
    - static const GTypeInfo ticker_info =
    - {
    - sizeof(GtkTickerClass),
    - NULL,
    - NULL,
    - (GClassInitFunc) gtk_ticker_class_init,
    - NULL,
    - NULL,
    - sizeof(GtkTicker),
    - 0,
    - (GInstanceInitFunc) gtk_ticker_init
    - };
    -
    - ticker_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTicker",
    - &ticker_info, 0);
    - }
    -
    - /* kludge to re-initialise the class if it's already registered */
    - else if (parent_class == NULL) {
    - gtk_ticker_class_init((GtkTickerClass *)g_type_class_peek(ticker_type));
    - }
    -
    - return ticker_type;
    -}
    -
    -static void gtk_ticker_finalize(GObject *object) {
    - gtk_ticker_stop_scroll(GTK_TICKER(object));
    -
    - G_OBJECT_CLASS(parent_class)->finalize(object);
    -}
    -
    -static void gtk_ticker_class_init (GtkTickerClass *class)
    -{
    - GObjectClass *gobject_class;
    - GtkWidgetClass *widget_class;
    - GtkContainerClass *container_class;
    -
    - gobject_class = (GObjectClass*) class;
    - widget_class = (GtkWidgetClass*) class;
    - container_class = (GtkContainerClass*) class;
    -
    - parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
    -
    - gobject_class->finalize = gtk_ticker_finalize;
    -
    - widget_class->map = gtk_ticker_map;
    - widget_class->realize = gtk_ticker_realize;
    - widget_class->size_request = gtk_ticker_size_request;
    - widget_class->size_allocate = gtk_ticker_size_allocate;
    -
    - container_class->add = gtk_ticker_add_real;
    - container_class->remove = gtk_ticker_remove_real;
    - container_class->forall = gtk_ticker_forall;
    - container_class->child_type = gtk_ticker_child_type;
    -}
    -
    -static GtkType gtk_ticker_child_type (GtkContainer *container)
    -{
    - return GTK_TYPE_WIDGET;
    -}
    -
    -static void gtk_ticker_init (GtkTicker *ticker)
    -{
    - GTK_WIDGET_UNSET_FLAGS (ticker, GTK_NO_WINDOW);
    -
    - ticker->interval = (guint) 200;
    - ticker->scootch = (guint) 2;
    - ticker->children = NULL;
    - ticker->timer = 0;
    - ticker->dirty = TRUE;
    -}
    -
    -GtkWidget* gtk_ticker_new (void)
    -{
    - return GTK_WIDGET(g_object_new(GTK_TYPE_TICKER, NULL));
    -}
    -
    -static void gtk_ticker_put (GtkTicker *ticker, GtkWidget *widget)
    -{
    - GtkTickerChild *child_info;
    -
    - g_return_if_fail (ticker != NULL);
    - g_return_if_fail (GTK_IS_TICKER (ticker));
    - g_return_if_fail (widget != NULL);
    -
    - child_info = g_new(GtkTickerChild, 1);
    - child_info->widget = widget;
    - child_info->x = 0;
    -
    - gtk_widget_set_parent(widget, GTK_WIDGET (ticker));
    -
    - ticker->children = g_list_append (ticker->children, child_info);
    -
    - if (GTK_WIDGET_REALIZED (ticker))
    - gtk_widget_realize (widget);
    -
    - if (GTK_WIDGET_VISIBLE (ticker) && GTK_WIDGET_VISIBLE (widget))
    - {
    - if (GTK_WIDGET_MAPPED (ticker))
    - gtk_widget_map (widget);
    -
    - gtk_widget_queue_resize (GTK_WIDGET (ticker));
    - }
    -}
    -
    -void gtk_ticker_set_interval (GtkTicker *ticker, gint interval)
    -{
    - g_return_if_fail (ticker != NULL);
    - g_return_if_fail (GTK_IS_TICKER (ticker));
    -
    - if ( interval < 0 )
    - interval = 200;
    - ticker->interval = interval;
    -}
    -
    -guint gtk_ticker_get_interval (GtkTicker *ticker)
    -{
    - g_return_val_if_fail (ticker != NULL, -1);
    - g_return_val_if_fail (GTK_IS_TICKER (ticker), -1);
    -
    - return ticker->interval;
    -}
    -
    -void gtk_ticker_set_scootch (GtkTicker *ticker, gint scootch)
    -{
    - g_return_if_fail (ticker != NULL);
    - g_return_if_fail (GTK_IS_TICKER (ticker));
    -
    - if (scootch <= 0)
    - scootch = 2;
    - ticker->scootch = scootch;
    - ticker->dirty = TRUE;
    -}
    -
    -guint gtk_ticker_get_scootch (GtkTicker *ticker )
    -{
    - g_return_val_if_fail (ticker != NULL, -1);
    - g_return_val_if_fail (GTK_IS_TICKER (ticker), -1);
    -
    - return ticker->scootch;
    -}
    -
    -void gtk_ticker_set_spacing (GtkTicker *ticker, gint spacing )
    -{
    - g_return_if_fail (ticker != NULL);
    - g_return_if_fail (GTK_IS_TICKER (ticker));
    -
    - if ( spacing < 0 )
    - spacing = 0;
    - ticker->spacing = spacing;
    - ticker->dirty = TRUE;
    -}
    -
    -static int ticker_timeout(gpointer data)
    -{
    - GtkTicker *ticker = (GtkTicker *) data;
    -
    - if (GTK_WIDGET_VISIBLE (ticker))
    - gtk_widget_queue_resize (GTK_WIDGET (ticker));
    -
    - return( TRUE );
    -}
    -
    -void gtk_ticker_start_scroll(GtkTicker *ticker)
    -{
    - g_return_if_fail (ticker != NULL);
    - g_return_if_fail (GTK_IS_TICKER (ticker));
    - if ( ticker->timer != 0 )
    - return;
    - ticker->timer = g_timeout_add(ticker->interval, ticker_timeout, ticker);
    -}
    -
    -void gtk_ticker_stop_scroll(GtkTicker *ticker)
    -{
    - g_return_if_fail (ticker != NULL);
    - g_return_if_fail (GTK_IS_TICKER (ticker));
    - if ( ticker->timer == 0 )
    - return;
    - g_source_remove(ticker->timer);
    - ticker->timer = 0;
    -}
    -
    -guint gtk_ticker_get_spacing (GtkTicker *ticker )
    -{
    - g_return_val_if_fail (ticker != NULL, -1);
    - g_return_val_if_fail (GTK_IS_TICKER (ticker), -1);
    -
    - return ticker->spacing;
    -}
    -
    -static void gtk_ticker_map (GtkWidget *widget)
    -{
    - GtkTicker *ticker;
    - GtkTickerChild *child;
    - GList *children;
    -
    - g_return_if_fail (widget != NULL);
    - g_return_if_fail (GTK_IS_TICKER (widget));
    -
    - GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
    - ticker = GTK_TICKER (widget);
    -
    - children = ticker->children;
    - while (children)
    - {
    - child = children->data;
    - children = children->next;
    -
    - if (GTK_WIDGET_VISIBLE (child->widget) &&
    - !GTK_WIDGET_MAPPED (child->widget))
    - gtk_widget_map (child->widget);
    - }
    -
    - gdk_window_show (widget->window);
    -}
    -
    -static void gtk_ticker_realize (GtkWidget *widget)
    -{
    - GdkWindowAttr attributes;
    - gint attributes_mask;
    -
    - g_return_if_fail (widget != NULL);
    - g_return_if_fail (GTK_IS_TICKER (widget));
    -
    - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
    -
    - attributes.window_type = GDK_WINDOW_CHILD;
    - attributes.x = widget->allocation.x;
    - attributes.y = widget->allocation.y;
    - attributes.width = widget->allocation.width;
    - attributes.height = widget->allocation.height;
    - attributes.wclass = GDK_INPUT_OUTPUT;
    - attributes.visual = gtk_widget_get_visual (widget);
    - attributes.colormap = gtk_widget_get_colormap (widget);
    - attributes.event_mask = gtk_widget_get_events (widget);
    - attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
    -
    - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
    -
    - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
    - &attributes, attributes_mask);
    - gdk_window_set_user_data (widget->window, widget);
    -
    - widget->style = gtk_style_attach (widget->style, widget->window);
    - gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
    -}
    -
    -static void gtk_ticker_size_request (GtkWidget *widget, GtkRequisition *requisition)
    -{
    - GtkTicker *ticker;
    - GtkTickerChild *child;
    - GList *children;
    - GtkRequisition child_requisition;
    -
    - g_return_if_fail (widget != NULL);
    - g_return_if_fail (GTK_IS_TICKER (widget));
    - g_return_if_fail (requisition != NULL);
    -
    - ticker = GTK_TICKER (widget);
    - requisition->width = 0;
    - requisition->height = 0;
    -
    - children = ticker->children;
    - while (children)
    - {
    - child = children->data;
    - children = children->next;
    -
    - if (GTK_WIDGET_VISIBLE (child->widget))
    - {
    - gtk_widget_size_request (child->widget, &child_requisition);
    -
    - requisition->height = MAX (requisition->height,
    - child_requisition.height);
    - requisition->width += child_requisition.width + ticker->spacing;
    - }
    - }
    - if ( requisition->width > ticker->spacing )
    - requisition->width -= ticker->spacing;
    -
    - requisition->height += GTK_CONTAINER (ticker)->border_width * 2;
    - requisition->width += GTK_CONTAINER (ticker)->border_width * 2;
    -}
    -
    -static void gtk_ticker_compute_offsets (GtkTicker *ticker)
    -{
    - GtkTickerChild *child;
    - GtkRequisition child_requisition;
    - GList *children;
    - guint16 border_width;
    -
    - g_return_if_fail (ticker != NULL);
    - g_return_if_fail (GTK_IS_TICKER(ticker));
    -
    - border_width = GTK_CONTAINER (ticker)->border_width;
    -
    - ticker->width = GTK_WIDGET(ticker)->allocation.width;
    - ticker->total = 0;
    - children = ticker->children;
    - while (children) {
    - child = children->data;
    -
    - child->x = 0;
    - if (GTK_WIDGET_VISIBLE (child->widget)) {
    - gtk_widget_get_child_requisition (child->widget, &child_requisition);
    - child->offset = ticker->total;
    - ticker->total +=
    - child_requisition.width + border_width + ticker->spacing;
    - }
    - children = children->next;
    - }
    - ticker->dirty = FALSE;
    -}
    -
    -static void gtk_ticker_size_allocate (GtkWidget *widget,
    - GtkAllocation *allocation)
    -{
    - GtkTicker *ticker;
    - GtkTickerChild *child;
    - GtkAllocation child_allocation;
    - GtkRequisition child_requisition;
    - GList *children;
    - guint16 border_width;
    -
    - g_return_if_fail (widget != NULL);
    - g_return_if_fail (GTK_IS_TICKER(widget));
    - g_return_if_fail (allocation != NULL);
    -
    - ticker = GTK_TICKER (widget);
    -
    - if ( GTK_WIDGET(ticker)->allocation.width != ticker->width )
    - ticker->dirty = TRUE;
    -
    - if ( ticker->dirty == TRUE ) {
    - gtk_ticker_compute_offsets( ticker );
    - }
    -
    - widget->allocation = *allocation;
    - if (GTK_WIDGET_REALIZED (widget))
    - gdk_window_move_resize (widget->window,
    - allocation->x,
    - allocation->y,
    - allocation->width,
    - allocation->height);
    -
    - border_width = GTK_CONTAINER (ticker)->border_width;
    -
    - children = ticker->children;
    - while (children)
    - {
    - child = children->data;
    - child->x -= ticker->scootch;
    -
    - if (GTK_WIDGET_VISIBLE (child->widget)) {
    - gtk_widget_get_child_requisition (child->widget, &child_requisition);
    - child_allocation.width = child_requisition.width;
    - child_allocation.x = child->offset + border_width + child->x;
    - if ( ( child_allocation.x + child_allocation.width ) < GTK_WIDGET(ticker)->allocation.x ) {
    - if ( ticker->total >= GTK_WIDGET(ticker)->allocation.width ) {
    - child->x += GTK_WIDGET(ticker)->allocation.x + GTK_WIDGET(ticker)->allocation.width + ( ticker->total - ( GTK_WIDGET(ticker)->allocation.x + GTK_WIDGET(ticker)->allocation.width ) );
    - }
    - else {
    - child->x += GTK_WIDGET(ticker)->allocation.x + GTK_WIDGET(ticker)->allocation.width;
    - }
    - }
    - child_allocation.y = border_width;
    - child_allocation.height = child_requisition.height;
    - gtk_widget_size_allocate (child->widget, &child_allocation);
    - }
    - children = children->next;
    - }
    -}
    -
    -void gtk_ticker_add(GtkTicker *ticker, GtkWidget *widget)
    -{
    - gtk_ticker_add_real( GTK_CONTAINER( ticker ), widget );
    - ticker->dirty = TRUE;
    -}
    -
    -void gtk_ticker_remove(GtkTicker *ticker, GtkWidget *widget)
    -{
    - gtk_ticker_remove_real( GTK_CONTAINER( ticker ), widget );
    - ticker->dirty = TRUE;
    -}
    -
    -static void gtk_ticker_add_real(GtkContainer *container, GtkWidget *widget)
    -{
    - g_return_if_fail (container != NULL);
    - g_return_if_fail (GTK_IS_TICKER (container));
    - g_return_if_fail (widget != NULL);
    -
    - gtk_ticker_put(GTK_TICKER (container), widget);
    -}
    -
    -static void gtk_ticker_remove_real(GtkContainer *container, GtkWidget *widget)
    -{
    - GtkTicker *ticker;
    - GtkTickerChild *child;
    - GList *children;
    -
    - g_return_if_fail (container != NULL);
    - g_return_if_fail (GTK_IS_TICKER (container));
    - g_return_if_fail (widget != NULL);
    -
    - ticker = GTK_TICKER (container);
    -
    - children = ticker->children;
    - while (children)
    - {
    - child = children->data;
    -
    - if (child->widget == widget)
    - {
    - gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
    -
    - gtk_widget_unparent (widget);
    -
    - ticker->children = g_list_remove_link (ticker->children, children);
    - g_list_free (children);
    - g_free (child);
    -
    - if (was_visible && GTK_WIDGET_VISIBLE (container))
    - gtk_widget_queue_resize (GTK_WIDGET (container));
    -
    - break;
    - }
    -
    - children = children->next;
    - }
    -}
    -
    -static void gtk_ticker_forall (GtkContainer *container,
    - gboolean include_internals,
    - GtkCallback callback,
    - gpointer callback_data)
    -{
    - GtkTicker *ticker;
    - GtkTickerChild *child;
    - GList *children;
    -
    - g_return_if_fail (container != NULL);
    - g_return_if_fail (GTK_IS_TICKER (container));
    - g_return_if_fail (callback != NULL);
    -
    - ticker = GTK_TICKER (container);
    -
    - children = ticker->children;
    - while (children)
    - {
    - child = children->data;
    - children = children->next;
    -
    - (* callback) (child->widget, callback_data);
    - }
    -}
    -
    --- a/stocker/gtkticker.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,99 +0,0 @@
    -/* GTK - The GIMP Toolkit
    - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
    - *
    - * 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., 51 Franklin Street, Fifth Floor,
    - * Boston, MA 02111-1301, USA.
    - */
    -
    -/*
    - * Copyright 2000 Syd Logan
    - */
    -
    -#ifndef __GTK_TICKER_H__
    -#define __GTK_TICKER_H__
    -
    -
    -#include <gdk/gdk.h>
    -#include <gtk/gtkcontainer.h>
    -#include <gtk/gtkmain.h>
    -
    -
    -#ifdef __cplusplus
    -extern "C" {
    -#endif /* __cplusplus */
    -
    -#define GTK_TYPE_TICKER (gtk_ticker_get_type ())
    -#define GTK_TICKER(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TICKER, GtkTicker))
    -#define GTK_TICKER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TICKER, GtkTickerClass))
    -#define GTK_IS_TICKER(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TICKER))
    -#define GTK_IS_TICKER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TICKER))
    -
    -
    -typedef struct _GtkTicker GtkTicker;
    -typedef struct _GtkTickerClass GtkTickerClass;
    -typedef struct _GtkTickerChild GtkTickerChild;
    -
    -/* XXX children move from right to left, should be able to go other way */
    -
    -struct _GtkTicker
    -{
    - GtkContainer container;
    - guint interval; /* how often to scootch */
    - gint spacing; /* inter-child horizontal spacing */
    - guint scootch; /* how many pixels to move each scootch */
    - gint timer; /* timer object */
    - gint total; /* total width of widgets */
    - gint width; /* width of containing window */
    - gboolean dirty;
    - GList *children;
    -};
    -
    -struct _GtkTickerClass
    -{
    - GtkContainerClass parent_class;
    -};
    -
    -struct _GtkTickerChild
    -{
    - GtkWidget *widget;
    - gint x; /* current position */
    - gint offset; /* offset in list */
    -};
    -
    -
    -GtkType gtk_ticker_get_type (void);
    -GtkWidget* gtk_ticker_new (void);
    -void gtk_ticker_add (GtkTicker *ticker,
    - GtkWidget *widget);
    -void gtk_ticker_remove (GtkTicker *ticker,
    - GtkWidget *widget);
    -void gtk_ticker_set_interval (GtkTicker *ticker,
    - gint interval);
    -guint gtk_ticker_get_interval (GtkTicker *ticker);
    -void gtk_ticker_set_spacing (GtkTicker *ticker,
    - gint spacing);
    -guint gtk_ticker_get_spacing (GtkTicker *ticker);
    -void gtk_ticker_set_scootch (GtkTicker *ticker,
    - gint scootch);
    -guint gtk_ticker_get_scootch (GtkTicker *ticker);
    -void gtk_ticker_start_scroll (GtkTicker *ticker);
    -void gtk_ticker_stop_scroll (GtkTicker *ticker);
    -
    -#ifdef __cplusplus
    -}
    -#endif /* __cplusplus */
    -
    -
    -#endif /* __GTK_TICKER_H__ */
    --- a/stocker/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -if TYPES.contains('incomplete') and PIDGIN.found()
    - stocker = shared_module('stocker',
    - 'stocker.c',
    - 'stocker_prefs.c',
    - 'gtkticker.c',
    - dependencies : [PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'stocker'
    -endif
    --- a/stocker/stocker.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,412 +0,0 @@
    -/*
    - * Stocker - Adds a stock ticker to the buddy list
    - * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com>
    - *
    - * 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 of the
    - * License, 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
    - * 02111-1301, USA.
    - */
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include <gdk/gdk.h>
    -#include <gtk/gtk.h>
    -
    -#include <gtkplugin.h>
    -#include <gtkprefs.h>
    -#include <gtkblist.h>
    -#include <gtkutils.h>
    -#include <prefs.h>
    -
    -#include "gtkticker.h"
    -#include "stocker_prefs.h"
    -
    -#define URL_REQUEST "http://quotewebvip-m01.blue.aol.com/?action=aim&syms=%s&fields=nspg"
    -
    -#define CHANGE_INCREASE "<span color=\"green3\">%+0.04g</span>"
    -#define CHANGE_DECREASE "<span color=\"red\">%+0.04g</span>"
    -#define CHANGE_NONE "%0.04g"
    -
    -/******************************************************************************
    - * structs
    - *****************************************************************************/
    -#define STOCKER_QUOTE(obj) ((StockerQuote *)(obj))
    -
    -typedef struct {
    - gchar *symbol;
    -
    - GtkWidget *label;
    -
    - guint ref;
    -} StockerQuote;
    -
    -/******************************************************************************
    - * globals
    - *****************************************************************************/
    -static GtkWidget *ticker = NULL;
    -static GHashTable *quotes = NULL;
    -static guint quotes_id = 0, interval_id = 0, interval_timer = 0;
    -
    -/******************************************************************************
    - * Quote stuff
    - *****************************************************************************/
    -static StockerQuote *
    -stocker_quote_new(const gchar *symbol) {
    - StockerQuote *ret = g_new0(StockerQuote, 1);
    - gchar *label = NULL;
    -
    - ret->symbol = g_strdup(symbol);
    -
    - label = g_strdup_printf("%s (refreshing)", symbol);
    - ret->label = gtk_label_new(label);
    - g_free(label);
    -
    - gtk_ticker_add(GTK_TICKER(ticker), ret->label);
    - gtk_widget_show(ret->label);
    -
    - ret->ref = 1;
    -
    - return ret;
    -}
    -
    -static void
    -stocker_quote_ref(StockerQuote *quote) {
    - quote->ref++;
    -}
    -
    -static void
    -stocker_quote_unref(StockerQuote *quote) {
    - quote->ref--;
    -
    - if(quote->ref != 0)
    - return;
    -
    - g_free(quote->symbol);
    -
    - gtk_widget_destroy(quote->label);
    -
    - g_free(quote);
    -
    - quote = NULL;
    -}
    -
    -static void
    -stocker_quote_update(StockerQuote *quote, const gchar *name, gdouble current,
    - gdouble change)
    -{
    - GString *str = g_string_sized_new(512);
    -
    - g_string_append_printf(str,
    - "<span weight=\"bold\">%s</span> "
    - "<span size=\"small\">(%s)</span> $%g ",
    - name, quote->symbol, current);
    - if(change < 0.0)
    - g_string_append_printf(str, CHANGE_DECREASE, change);
    - else if(change > 0.0)
    - g_string_append_printf(str, CHANGE_INCREASE, change);
    - else
    - g_string_append_printf(str, CHANGE_NONE, change);
    -
    - gtk_label_set_markup(GTK_LABEL(quote->label), str->str);
    -
    - g_string_free(str, TRUE);
    -}
    -
    -/******************************************************************************
    - * main stuff
    - *****************************************************************************/
    -static void
    -stocker_refresh_url_cb(PurpleUtilFetchUrlData *url_data, gpointer data,
    - const gchar *text, gsize len, const gchar *errmsg)
    -{
    - const gchar *p = text;
    - gchar *t;
    -
    - while((p = g_strstr_len(p, strlen(p), "DATA="))) {
    - const gchar *name = NULL, *symbol = NULL;
    - gdouble current = 0.0, change = 0.0;
    -
    - /* move paste the data text */
    - p += 5;
    -
    - /* find the name */
    - t = strchr(p, ';');
    - *t = '\0';
    - name = p;
    -
    - /* find the symbol */
    - p = t + 1;
    - t = strchr(p, ';');
    - *t = '\0';
    - symbol = p;
    -
    - /* find the current price */
    - p = t + 1;
    - t = strchr(p, ';');
    - *t = '\0';
    - current = atof(p);
    -
    - /* find the change */
    - p = t + 1;
    - t = strchr(p, '\r');
    - *t = '\0';
    - change = atof(p);
    -
    - /* now move p to the EOL */
    - p = t + 1;
    -
    - if(symbol) {
    - StockerQuote *quote = g_hash_table_lookup(quotes, symbol);
    -
    - if(quote) {
    - stocker_quote_update(quote, name, current, change);
    - }
    - }
    - }
    -}
    -
    -static void
    -stocker_refresh_helper(gpointer k, gpointer v, gpointer d) {
    - GString *str = (GString *)d;
    - gchar *symbol = (gchar *)k;
    -
    - g_string_append_printf(str, "%s%s",
    - (str->len > 0) ? "," : "",
    - symbol);
    -}
    -
    -static void
    -stocker_refresh(void) {
    - GString *syms = g_string_sized_new(64);
    - gchar *url = NULL;
    -
    - g_hash_table_foreach(quotes, stocker_refresh_helper, syms);
    -
    - url = g_strdup_printf(URL_REQUEST, syms->str);
    - g_string_free(syms, TRUE);
    -
    - purple_util_fetch_url(url, TRUE, "purple", TRUE,
    - stocker_refresh_url_cb,
    - NULL);
    - g_free(url);
    -}
    -
    -static gboolean
    -stocker_create() {
    - PurpleBuddyList *blist;
    - PidginBuddyList *gtkblist;
    -
    - if(GTK_IS_WIDGET(ticker))
    - gtk_widget_destroy(ticker);
    -
    - blist = purple_get_blist();
    - if(!blist)
    - return FALSE;
    -
    - gtkblist = PIDGIN_BLIST(blist);
    -
    - ticker = gtk_ticker_new();
    - gtk_box_pack_start(GTK_BOX(gtkblist->vbox), ticker, FALSE, FALSE, 0);
    - gtk_ticker_set_spacing(GTK_TICKER(ticker), 16);
    - gtk_ticker_start_scroll(GTK_TICKER(ticker));
    - gtk_widget_show_all(ticker);
    -
    - return TRUE;
    -}
    -
    -static void
    -stocker_blist_created(PurpleBuddyList *blist, gpointer data) {
    - stocker_create();
    -}
    -
    -static void
    -stocker_quotes_refresh(GList *symbols) {
    - StockerQuote *quote = NULL;
    - GHashTable *new_quotes = NULL, *temp = NULL;
    - GList *l = NULL;
    -
    - /* this is a bit more complicated than I'd like, but we need a way to be
    - * able to remove quotes that we don't know by name. So we create a new
    - * hashtable, copy all the existing quotes over to it, add the new ones,
    - * then delete the old table and use the new one.
    - */
    -
    - new_quotes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
    - (GDestroyNotify)stocker_quote_unref);
    -
    - for(l = symbols; l; l = l->next) {
    - gchar *symbol = l->data;
    -
    - /* sanity check to make sure we have a symbol */
    - if(!symbol)
    - continue;
    -
    - /* look for a quote */
    - quote = g_hash_table_lookup(quotes, symbol);
    -
    - if(quote) {
    - /* ref the quote so it stays alive */
    - stocker_quote_ref(quote);
    - } else {
    - /* this is a new symbol, create a quote for it */
    - quote = stocker_quote_new(symbol);
    - }
    -
    - /* insert the quote into the new hashtable */
    - g_hash_table_insert(new_quotes, g_strdup(symbol), quote);
    - }
    -
    - /* hold onto the old pointer */
    - temp = quotes;
    -
    - /* update the pointer to the updated list */
    - quotes = new_quotes;
    -
    - /* kill the old table */
    - g_hash_table_destroy(temp);
    -
    - /* refresh everything */
    - stocker_refresh();
    -}
    -
    -static void
    -stocker_quotes_changed_cb(const gchar *name, PurplePrefType type,
    - gconstpointer value, gpointer data)
    -{
    - stocker_quotes_refresh((GList *)value);
    -}
    -
    -static gboolean
    -stocker_refresh_cb(gpointer data) {
    - stocker_refresh();
    -
    - return TRUE;
    -}
    -
    -static void
    -stocker_interval_changed_cb(const gchar *name, PurplePrefType type,
    - gconstpointer value, gpointer data)
    -{
    - gint new_time = GPOINTER_TO_INT(value);
    -
    - /* remove the old timer */
    - purple_timeout_remove(interval_timer);
    -
    - /* add the new one */
    - interval_timer = purple_timeout_add_seconds(new_time * 60,
    - stocker_refresh_cb, NULL);
    -}
    -
    -/******************************************************************************
    - * plugin crap
    - *****************************************************************************/
    -static gboolean
    -stocker_load(PurplePlugin *plugin) {
    - void *prefs_handle = purple_prefs_get_handle();
    - gint interval;
    -
    - stocker_create();
    -
    - quotes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
    - (GDestroyNotify)stocker_quote_unref);
    -
    - purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-created",
    - plugin,
    - PURPLE_CALLBACK(stocker_blist_created), NULL);
    -
    - quotes_id = purple_prefs_connect_callback(prefs_handle, PREF_SYMBOLS,
    - stocker_quotes_changed_cb,
    - NULL);
    - interval_id = purple_prefs_connect_callback(prefs_handle, PREF_INTERVAL,
    - stocker_interval_changed_cb,
    - NULL);
    -
    - interval = 60 * purple_prefs_get_int(PREF_INTERVAL);
    - interval_timer = purple_timeout_add_seconds(interval, stocker_refresh_cb,
    - NULL);
    -
    - stocker_quotes_refresh(purple_prefs_get_string_list(PREF_SYMBOLS));
    -
    - stocker_refresh();
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -stocker_unload(PurplePlugin *plugin) {
    - return FALSE;
    -}
    -
    -static void
    -stocker_destroy(PurplePlugin *plugin) {
    - purple_timeout_remove(interval_timer);
    -
    - if(GTK_IS_WIDGET(ticker))
    - gtk_widget_destroy(ticker);
    - ticker = NULL;
    -}
    -
    -static PidginPluginUiInfo stocker_ui_info = { stocker_prefs_get_frame };
    -
    -static PurplePluginInfo stocker_info =
    -{
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD,
    - PIDGIN_PLUGIN_TYPE,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    -
    - "gtk-plugin_pack-stocker",
    - NULL,
    - PP_VERSION,
    - NULL,
    - NULL,
    - "Gary Kramlich <grim@reaperworld.com>",
    - PP_WEBSITE,
    -
    - stocker_load,
    - stocker_unload,
    - stocker_destroy,
    -
    - &stocker_ui_info,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void
    -stocker_init(PurplePlugin *plugin) {
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - stocker_info.name = _("Stocker");
    - stocker_info.summary = _("A stock ticker");
    - stocker_info.description =
    - _("Adds a stock ticker similar to the one in the Windows AIM client to"
    - " the bottom of the buddy list.");
    -
    - stocker_prefs_init();
    -}
    -
    -PURPLE_INIT_PLUGIN(stocker, stocker_init, stocker_info)
    --- a/stocker/stocker_prefs.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,323 +0,0 @@
    -/*
    - * Stocker - Adds a stock ticker to the buddy list
    - * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com>
    - *
    - * 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 of the
    - * License, 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
    - * 02111-1301, USA.
    - */
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include "stocker_prefs.h"
    -
    -#include <gtkprefs.h>
    -#include <gtkutils.h>
    -
    -/******************************************************************************
    - * Structs
    - *****************************************************************************/
    -#define STOCKER_PREFS(obj) ((StockerPrefs *)(obj))
    -
    -typedef struct {
    - GtkWidget *entry;
    - GtkWidget *list;
    - GtkListStore *symbols;
    -} StockerPrefs;
    -
    -/******************************************************************************
    - * helpers
    - *****************************************************************************/
    -static void
    -stocker_prefs_update_list(StockerPrefs *prefs) {
    - GtkTreeIter iter;
    - GList *l;
    - gchar *symbol;
    -
    - gtk_list_store_clear(prefs->symbols);
    -
    - for(l = purple_prefs_get_string_list(PREF_SYMBOLS); l; l = l->next) {
    - symbol = (gchar *)l->data;
    -
    - gtk_list_store_append(prefs->symbols, &iter);
    - gtk_list_store_set(prefs->symbols, &iter,
    - 0, symbol,
    - -1);
    - }
    -}
    -
    -static gboolean
    -stocker_prefs_apply_helper(GtkTreeModel *model, GtkTreePath *path,
    - GtkTreeIter *iter, gpointer data)
    -{
    - GList **symbols = (GList **)data;
    - gchar *symbol;
    -
    - gtk_tree_model_get(model, iter,
    - 0, &symbol,
    - -1);
    - *symbols = g_list_append(*symbols, symbol);
    -
    - return FALSE;
    -}
    -
    -static void
    -stocker_prefs_apply_cb(GtkButton *button, gpointer data) {
    - StockerPrefs *prefs = (StockerPrefs *)data;
    - GList *symbols = NULL, *l;
    -
    - gtk_tree_model_foreach(GTK_TREE_MODEL(prefs->symbols),
    - stocker_prefs_apply_helper, &symbols);
    -
    - purple_prefs_set_string_list(PREF_SYMBOLS, symbols);
    -
    - for(l = symbols; l; l = l->next)
    - g_free(l->data);
    - g_list_free(symbols);
    -}
    -
    -static void
    -stocker_prefs_add_cb(GtkButton *button, gpointer data) {
    - StockerPrefs *prefs = (StockerPrefs *)data;
    - GtkTreeIter iter;
    - const gchar *symbol;
    -
    - symbol = gtk_entry_get_text(GTK_ENTRY(prefs->entry));
    - if(g_utf8_strlen(symbol, -1) <= 0)
    - return;
    -
    - gtk_list_store_append(prefs->symbols, &iter);
    - gtk_list_store_set(prefs->symbols, &iter,
    - 0, symbol,
    - -1);
    - gtk_entry_set_text(GTK_ENTRY(prefs->entry), "");
    -}
    -
    -static void
    -stocker_prefs_remove_cb(GtkButton *button, gpointer data) {
    - StockerPrefs *prefs = (StockerPrefs *)data;
    - GtkTreeSelection *sel;
    - GtkTreeIter iter;
    - gchar *symbol;
    -
    - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(prefs->list));
    - if(!gtk_tree_selection_get_selected(sel, NULL, &iter))
    - return;
    -
    - gtk_tree_model_get(GTK_TREE_MODEL(prefs->symbols), &iter,
    - 0, &symbol,
    - -1);
    - gtk_entry_set_text(GTK_ENTRY(prefs->entry), symbol);
    - g_free(symbol);
    -
    - gtk_list_store_remove(prefs->symbols, &iter);
    -}
    -
    -static void
    -stocker_prefs_move_up_cb(GtkButton *button, gpointer data) {
    - StockerPrefs *prefs = STOCKER_PREFS(data);
    - GtkTreeSelection *sel;
    - GtkTreeIter siter, diter;
    - GtkTreePath *path;
    -
    - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(prefs->list));
    - if(!gtk_tree_selection_get_selected(sel, NULL, &siter)) {
    - return;
    - }
    -
    - path = gtk_tree_model_get_path(GTK_TREE_MODEL(prefs->symbols), &siter);
    - if(!path)
    - return;
    -
    - if(!gtk_tree_path_prev(path))
    - return;
    -
    - if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(prefs->symbols), &diter, path))
    - {
    - gtk_tree_path_free(path);
    - return;
    - }
    -
    - gtk_tree_path_free(path);
    -
    -#if GTK_CHECK_VERSION(2,2,0)
    - gtk_list_store_swap(prefs->symbols, &siter, &diter);
    -#else
    -# warning Someone make me work on gtk < 2.2.0
    -#endif
    -}
    -
    -static void
    -stocker_prefs_move_down_cb(GtkButton *button, gpointer data) {
    - StockerPrefs *prefs = STOCKER_PREFS(data);
    - GtkTreeSelection *sel;
    - GtkTreeIter siter, diter;
    - GtkTreePath *path;
    -
    - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(prefs->list));
    - if(!gtk_tree_selection_get_selected(sel, NULL, &siter)) {
    - return;
    - }
    -
    - path = gtk_tree_model_get_path(GTK_TREE_MODEL(prefs->symbols), &siter);
    - if(!path)
    - return;
    -
    - gtk_tree_path_next(path);
    -
    - if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(prefs->symbols), &diter, path))
    - {
    - gtk_tree_path_free(path);
    - return;
    - }
    -
    - gtk_tree_path_free(path);
    -
    -#if GTK_CHECK_VERSION(2,2,0)
    - gtk_list_store_swap(prefs->symbols, &siter, &diter);
    -#else
    -# warning Someone make me work on gtk < 2.2.0
    -#endif
    -}
    -
    -static void
    -stocker_prefs_destroyed(gpointer data) {
    - StockerPrefs *prefs = STOCKER_PREFS(data);
    -
    - g_object_unref(G_OBJECT(prefs->symbols));
    -
    - g_free(prefs);
    -}
    -
    -/******************************************************************************
    - * api
    - *****************************************************************************/
    -void
    -stocker_prefs_init(void) {
    - GList *def_syms = NULL;
    -
    - def_syms = g_list_append(def_syms, "GOOG");
    - def_syms = g_list_append(def_syms, "YHOO");
    - def_syms = g_list_append(def_syms, "RHT");
    - def_syms = g_list_append(def_syms, "VMW");
    -
    - purple_prefs_add_none(PREF_MY);
    - purple_prefs_add_none(PREF_ROOT);
    - purple_prefs_add_string_list(PREF_SYMBOLS, def_syms);
    - purple_prefs_add_int(PREF_INTERVAL, 30);
    - g_list_free(def_syms);
    -}
    -
    -GtkWidget *
    -stocker_prefs_get_frame(PurplePlugin *plugin) {
    - StockerPrefs *prefs = g_new0(StockerPrefs, 1);
    - GtkWidget *ret, *vbox, *hbox, *box, *frame, *sw, *label, *button;
    - GtkSizeGroup *sg;
    - GtkTreeViewColumn *col;
    - GtkCellRenderer *rend;
    -
    - sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    -
    - ret = gtk_vbox_new(FALSE, 4);
    - gtk_container_set_border_width(GTK_CONTAINER(ret), 12);
    - g_object_set_data_full(G_OBJECT(ret), "prefs", prefs,
    - stocker_prefs_destroyed);
    -
    - /**********************************
    - * symbols frame
    - *********************************/
    - frame = pidgin_make_frame(ret, _("Symbols"));
    -
    - box = gtk_hbox_new(FALSE, 4);
    - gtk_box_pack_start(GTK_BOX(frame), box, FALSE, FALSE, 0);
    -
    - vbox = gtk_vbox_new(FALSE, 4);
    - gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, FALSE, 0);
    -
    - /* symbol entry */
    - hbox = gtk_hbox_new(FALSE, 4);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new(_("Symbol:"));
    - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - prefs->entry = gtk_entry_new();
    - gtk_box_pack_start(GTK_BOX(hbox), prefs->entry, FALSE, FALSE, 0);
    -
    - /* symbols list */
    - sw = gtk_scrolled_window_new(NULL, NULL);
    - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
    - gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
    -
    - /* yes we purposely keep a reference.... */
    - prefs->symbols = gtk_list_store_new(1, G_TYPE_STRING);
    - stocker_prefs_update_list(prefs);
    -
    - prefs->list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(prefs->symbols));
    - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(prefs->list), FALSE);
    - gtk_tree_view_set_reorderable(GTK_TREE_VIEW(prefs->list), TRUE);
    - gtk_container_add(GTK_CONTAINER(sw), prefs->list);
    -
    - rend = gtk_cell_renderer_text_new();
    - col = gtk_tree_view_column_new_with_attributes("Symbol", rend,
    - "text", 0,
    - NULL);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(prefs->list), col);
    -
    - /* buttons */
    - vbox = gtk_vbox_new(FALSE, 4);
    - gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_from_stock(GTK_STOCK_ADD);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(stocker_prefs_add_cb), prefs);
    - gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(stocker_prefs_remove_cb), prefs);
    - gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(stocker_prefs_move_down_cb), prefs);
    - gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(stocker_prefs_move_up_cb), prefs);
    - gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
    -
    - button = gtk_button_new_from_stock(GTK_STOCK_APPLY);
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(stocker_prefs_apply_cb), prefs);
    - gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
    -
    - /**********************************
    - * options frame
    - *********************************/
    - frame = pidgin_make_frame(ret, _("Options"));
    -
    - pidgin_prefs_labeled_spin_button(frame, "Update interval (minutes):",
    - PREF_INTERVAL, 1, 1440, sg);
    -
    - /* show and return it already! */
    - gtk_widget_show_all(ret);
    -
    - return ret;
    -}
    --- a/stocker/stocker_prefs.h Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,39 +0,0 @@
    -/*
    - * Stocker - Adds a stock ticker to the buddy list
    - * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com>
    - *
    - * 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 of the
    - * License, 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
    - * 02111-1301, USA.
    - */
    -#ifndef STOCKER_PREFS_H
    -#define STOCKER_PREFS_H
    -
    -#include <gtk/gtk.h>
    -
    -#include <plugin.h>
    -
    -#define PREF_MY "/plugins/gtk/plugin_pack"
    -#define PREF_ROOT "/plugins/gtk/plugin_pack/stocker"
    -#define PREF_SYMBOLS "/plugins/gtk/plugin_pack/stocker/symbols"
    -#define PREF_INTERVAL "/plugins/gtk/plugin_pack/stocker/interval"
    -
    -G_BEGIN_DECLS
    -
    -void stocker_prefs_init(void);
    -GtkWidget *stocker_prefs_get_frame(PurplePlugin *plugin);
    -
    -G_END_DECLS
    -
    -#endif /* STOCKER_PREFS_H */
    --- a/stress/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# stress
    -
    -status: abusive
    -dependencies: libpurple
    -authors: Gary Kramlich
    -introduced: 2.6.0
    -
    -Stresses libpurple.
    -
    --- a/stress/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if TYPES.contains('abusive') and PURPLE.found()
    - stress = shared_library('stress',
    - 'stress.c',
    - dependencies : [PURPLE, GLIB],
    - name_prefix : '',
    - install : true,
    - install_dir : PURPLE_LIBDIR)
    - PP_PURPLE_BUILD += 'stress'
    -endif
    --- a/stress/stress.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,494 +0,0 @@
    -/*
    - * stress - stresses libpurple's event system
    - * Copyright (C) 2008 Gary Kramlich <grim@reaperworld.com>
    - *
    - * 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
    - * of the License, 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 02111-1301, USA.
    - */
    -
    -#include "../common/pp_internal.h"
    -
    -#include <stdlib.h>
    -#include <time.h>
    -
    -#include <accountopt.h>
    -#include <blist.h>
    -#include <debug.h>
    -#include <plugin.h>
    -#include <prpl.h>
    -#include <status.h>
    -#include <util.h>
    -
    -#define STRESS_BUDDY(buddy) \
    - ((StressBuddy *)purple_buddy_get_protocol_data(buddy))
    -
    -/******************************************************************************
    - * Structs
    - *****************************************************************************/
    -typedef struct {
    - PurpleBuddy *buddy;
    - guint timer_id;
    - gint nevents;
    - gint maxevents;
    -} StressBuddy;
    -
    -typedef struct {
    - gboolean running;
    - GList *buddies;
    - gint nevents;
    - gint message_min;
    - gint mexxage_max;
    -} StressData;
    -
    -/******************************************************************************
    - * Enums
    - *****************************************************************************/
    -enum {
    - STRESS_EVENT_SIGN_ON,
    - STRESS_EVENT_SIGN_OFF,
    - STRESS_EVENT_IDLE,
    - STRESS_EVENT_UNIDLE,
    - STRESS_EVENT_AWAY,
    - STRESS_EVENT_BACK,
    - STRESS_EVENT_TYPING,
    - STRESS_EVENT_STOPPED_TYPING,
    - STRESS_EVENT_SEND_MESSAGE,
    -};
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -static GList *buddies = NULL;
    -static GList *events = NULL;
    -static gint nevents = 0;
    -static gint message_min = 0;
    -static gint message_max = 0;
    -
    -/******************************************************************************
    - * helpers
    - *****************************************************************************/
    -static inline void
    -stress_send_im(PurpleAccount *account, PurpleBuddy *buddy, const gchar *name) {
    - PurpleConnection *pc = NULL;
    - GString *msg = NULL;
    - gint length = 0, i = 0;
    -
    - /* build the message */
    - msg = g_string_new("");
    - length = (rand() % (message_max - message_min)) + message_min;
    -
    - for(i = 0; i < length; i += 4) {
    - gint value = rand() % 65536;
    -
    - g_string_append_printf(msg, "%04x", value);
    - }
    -
    - /* send the im */
    - pc = purple_account_get_connection(account);
    - serv_got_im(pc, name, msg->str, 0, time(NULL));
    -
    - /* cleanup */
    - g_string_free(msg, TRUE);
    -}
    -
    -static inline void
    -stress_close_convs(PurpleAccount *account, const gchar *name) {
    - PurpleConversation *conv = NULL;
    -
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name,
    - account);
    -
    - if(conv)
    - purple_conversation_destroy(conv);
    -}
    -
    -static gboolean
    -stress_event_cb(gpointer data) {
    - StressBuddy *sb = (StressBuddy *)data;
    - PurpleAccount *account = purple_buddy_get_account(sb->buddy);
    - PurpleStatus *status = NULL;
    - gint event = rand() % nevents;
    - const gchar *name = purple_buddy_get_name(sb->buddy), *sevent = NULL;
    - gchar *msg = NULL;
    -
    - /* increment our event counter */
    - sb->nevents++;
    -
    - event = GPOINTER_TO_INT(g_list_nth_data(events, event));
    -
    - switch(event) {
    - case STRESS_EVENT_SIGN_ON: sevent = "signon"; break;
    - case STRESS_EVENT_SIGN_OFF: sevent = "signoff"; break;
    - case STRESS_EVENT_IDLE: sevent = "idle"; break;
    - case STRESS_EVENT_UNIDLE: sevent = "unidle"; break;
    - case STRESS_EVENT_AWAY: sevent = "away"; break;
    - case STRESS_EVENT_BACK: sevent = "back"; break;
    - case STRESS_EVENT_TYPING: sevent = "typing"; break;
    - case STRESS_EVENT_STOPPED_TYPING: sevent = "stopped typing"; break;
    - case STRESS_EVENT_SEND_MESSAGE: sevent = "message"; break;
    - }
    -
    - purple_debug_info("stress", "firing '%s' for '%s' (event %d of %d)\n",
    - sevent, name, sb->nevents, sb->maxevents);
    -
    - switch(event) {
    - case STRESS_EVENT_SIGN_ON:
    - case STRESS_EVENT_BACK:
    - purple_prpl_got_user_status(account, name, "available", NULL);
    - break;
    - case STRESS_EVENT_SIGN_OFF:
    - purple_prpl_got_user_status(account, name, "offline", NULL);
    - break;
    - case STRESS_EVENT_IDLE:
    - purple_prpl_got_user_idle(account, name, TRUE, 0);
    - break;
    - case STRESS_EVENT_UNIDLE:
    - purple_prpl_got_user_idle(account, name, FALSE, 0);
    - break;
    - case STRESS_EVENT_AWAY:
    - purple_prpl_got_user_status(account, name, "away", NULL);
    - break;
    - case STRESS_EVENT_SEND_MESSAGE:
    - stress_send_im(account, sb->buddy, name);
    - break;
    - }
    -
    - msg = g_strdup_printf("event %d of %d", sb->nevents, sb->maxevents);
    - status = purple_presence_get_active_status(sb->buddy->presence);
    - purple_status_set_attr_string(status, "message", msg);
    - g_free(msg);
    -
    - if(sb->maxevents > 0 && sb->nevents >= sb->maxevents) {
    - purple_prpl_got_user_status(account, name, "available",
    - "message", _("Done"),
    - NULL);
    - purple_prpl_got_user_idle(account, name, FALSE, 0);
    -
    - stress_close_convs(account, name);
    -
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/******************************************************************************
    - * PRPL Stuff
    - *****************************************************************************/
    -static const gchar *
    -stress_list_icon(PurpleAccount *account, PurpleBuddy *b) {
    - return NULL;
    -}
    -
    -static gchar *
    -stress_status_text(PurpleBuddy *buddy) {
    - PurplePresence *presence = purple_buddy_get_presence(buddy);
    - PurpleStatus *status = purple_presence_get_active_status(presence);
    - const gchar *msg = NULL;
    -
    - msg = purple_status_get_attr_string(status, "message");
    -
    - return (msg) ? g_strdup(msg) : NULL;
    -}
    -
    -#define add_event(setting, e1, e2) G_STMT_START { \
    - if(purple_account_get_bool(account, (setting), TRUE)) { \
    - events = g_list_prepend(events, GINT_TO_POINTER((e1))); \
    - if(e2 > -1) \
    - events = g_list_prepend(events, GINT_TO_POINTER((e2))); \
    - } \
    -} G_STMT_END
    -
    -static void
    -stress_login(PurpleAccount *account) {
    - PurpleConnection *pc = NULL;
    - PurpleGroup *g = NULL;
    - gint n_buddies = 0, i = 0, interval = 0, maxevents = 0;
    -
    - /* build our possible events from the account settings */
    - add_event("trigger_signon", STRESS_EVENT_SIGN_ON, STRESS_EVENT_SIGN_OFF);
    - add_event("trigger_idle", STRESS_EVENT_IDLE, STRESS_EVENT_UNIDLE);
    - add_event("trigger_away", STRESS_EVENT_AWAY, STRESS_EVENT_BACK);
    - add_event("trigger_typing", STRESS_EVENT_TYPING, STRESS_EVENT_STOPPED_TYPING);
    - add_event("send_messages", STRESS_EVENT_SEND_MESSAGE, -1);
    -
    - nevents = g_list_length(events);
    -
    - /* get our connection and set it as online */
    - pc = purple_account_get_connection(account);
    - purple_connection_set_state(pc, PURPLE_CONNECTED);
    -
    - /* grab the account settings we need for buddies */
    - n_buddies = purple_account_get_int(account, "nbuddies", 50);
    - maxevents = purple_account_get_int(account, "maxevents", 100);
    - interval = (guint)purple_account_get_int(account, "interval", 500);
    - message_min = purple_account_get_int(account, "message_min", 16);
    - message_max = purple_account_get_int(account, "message_max", 128);
    -
    - g = purple_group_new("prpl-stress");
    -
    - for(i = 0; i < n_buddies; i++) {
    - PurpleBuddy *b = NULL;
    - StressBuddy *sb = NULL;
    - gchar *name = NULL;
    -
    - /* create the buddy and it's name */
    - name = g_strdup_printf("stress-%04x", i);
    - b = purple_buddy_new(account, name, NULL);
    - g_free(name);
    -
    - /* add our data to the buddy */
    - sb = g_new0(StressBuddy, 1);
    - sb->buddy = b;
    - sb->maxevents = maxevents;
    - purple_buddy_set_protocol_data(b, sb);
    -
    - /* add the buddy to our list and the purple blist */
    - buddies = g_list_prepend(buddies, sb);
    - purple_blist_add_buddy(b, NULL, g, NULL);
    -
    - /* add our event timer to the buddy */
    - sb->timer_id = g_timeout_add(interval, stress_event_cb, sb);
    - }
    -}
    -
    -static void
    -stress_close(PurpleConnection *pc) {
    - GList *l = NULL;
    - PurpleGroup *g = NULL;
    -
    - for(l = buddies; l; l = l->next) {
    - StressBuddy *sb = l->data;
    - purple_blist_remove_buddy(sb->buddy);
    - }
    -
    - g_list_free(buddies);
    -
    - g = purple_find_group("prpl-stress");
    - purple_blist_remove_group(g);
    -
    - buddies = NULL;
    -}
    -
    -static void
    -stress_buddy_free(PurpleBuddy *buddy) {
    - StressBuddy *sb = STRESS_BUDDY(buddy);
    -
    - if(!sb)
    - return;
    -
    - if(sb->timer_id > 0)
    - g_source_remove(sb->timer_id);
    -
    - g_free(sb);
    -}
    -
    -static GList *
    -stress_status_types(PurpleAccount *account) {
    - GList *types = NULL;
    - PurpleStatusType *type = NULL;
    -
    - g_return_val_if_fail(account != NULL, NULL);
    -
    - type =
    - purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NULL,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING),
    - NULL);
    - types = g_list_prepend(types, type);
    -
    - type =
    - purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, NULL,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING),
    - NULL);
    - types = g_list_prepend(types, type);
    -
    - type =
    - purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NULL,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(PURPLE_TYPE_STRING),
    - NULL);
    - types = g_list_prepend(types, type);
    -
    - return types;
    -}
    -
    -static PurplePluginProtocolInfo prpl_info = {
    - OPT_PROTO_NO_PASSWORD,
    - NULL,
    - NULL,
    - NO_BUDDY_ICONS,
    - stress_list_icon,
    - NULL,
    - stress_status_text,
    - NULL,
    - stress_status_types,
    - NULL,
    - NULL,
    - NULL,
    - stress_login,
    - stress_close,
    - NULL, /* stress_send_im, */
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL, /* stress_add_buddies, */
    - NULL,
    - NULL, /* stress_remove_buddies, */
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - stress_buddy_free,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - 0,
    - NULL,
    - NULL,
    - NULL,
    -};
    -
    -/******************************************************************************
    - * Plugin Stuff
    - *****************************************************************************/
    -static gboolean
    -plugin_load(PurplePlugin *plugin) {
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin) {
    - return TRUE;
    -}
    -
    -static GList *
    -stress_actions(PurplePlugin *plugin, gpointer context) {
    - GList *menu = NULL;
    - PurplePluginAction *act = NULL;
    -
    - action = purple_plugin_action_new(_("Start"));
    - menu = g_list_append(menu, action);
    -
    - return menu;
    -}
    -
    -static PurplePluginInfo info = {
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_PROTOCOL,
    - NULL,
    - 0,
    - NULL,
    - PURPLE_PRIORITY_DEFAULT,
    -
    - "core-plugin_pack-stress",
    - NULL,
    - PP_VERSION,
    - NULL,
    - NULL,
    - "Gary Kramlich <grim@reaperworld.com>",
    - PP_WEBSITE,
    -
    - plugin_load,
    - plugin_unload,
    - NULL,
    -
    - NULL,
    - &prpl_info,
    - NULL,
    - stress_actions,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    -};
    -
    -#define add_option(type, label, name, def) G_STMT_START { \
    - option = purple_account_option_##type##_new((label), (name), (def)); \
    - prpl_info.protocol_options = g_list_prepend(prpl_info.protocol_options, (option)); \
    -} G_STMT_END
    -
    -static void
    -init_plugin(PurplePlugin *plugin) {
    - PurpleAccountOption *option = NULL;
    -
    -#ifdef ENABLE_NLS
    - bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    -#endif /* ENABLE_NLS */
    -
    - /* seed the randome number generator */
    - srand(time(NULL));
    -
    - info.name = _("Stress");
    - info.summary = _("A PRPL to stress libpurple");
    - info.description = info.summary;
    -
    - add_option(int, _("Buddies to stress with"), "nbuddies", 50);
    - add_option(int, _("Event interval, in milliseconds"), "interval", 500);
    - add_option(int, _("Max events per buddy"), "maxevents", 100);
    - add_option(bool, _("Trigger signoff/signoff"), "trigger_signon", TRUE);
    - add_option(bool, _("Trigger idle/unidle"), "trigger_idle", TRUE);
    - add_option(bool, _("Trigger away/back"), "trigger_away", TRUE);
    - add_option(bool, _("Trigger typing/stopped typing"), "trigger_typing", TRUE);
    - add_option(bool, _("Send messages"), "send_messages", TRUE);
    - add_option(int, _("Minimum message length"), "message_min", 16);
    - add_option(int, _("Maxium message length"), "message_max", 128);
    -
    - prpl_info.protocol_options = g_list_reverse(prpl_info.protocol_options);
    -}
    -
    -PURPLE_INIT_PLUGIN(stress, init_plugin, info)
    --- a/talkfilters/README.md Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -# GNU Talk Filters
    -
    -dependencies: pidgin, talkfilters
    -authors: Mark Lindner, Peter Lawler
    -introduced: 1.0beta1
    -notes: Requires GNU Talkfilters
    -
    -The GNU Talk Filters are filter programs that convert ordinary English text into text that mimics a stereotyped or otherwise humorous dialect. These filters have been in the public domain for many years, and have been made available as a single integrated package. The filters include austro, b1ff, brooklyn, chef, cockney, drawl, dubya, fudd, funetak, jethro, jive, kraut, pansy, pirate, postmodern, redneck, valspeak, and warez.
    -
    --- a/talkfilters/meson.build Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -TALKFILTERS = dependency('talkfilters', required : false)
    -
    -if TYPES.contains('default') and PIDGIN.found() and TALKFILTERS.found()
    - talkfilters = shared_module('talkfilters',
    - 'talkfilters.c',
    - dependencies : [TALKFILTERS, PIDGIN, GTK],
    - name_prefix : '',
    - install : true,
    - install_dir : PIDGIN_LIBDIR)
    - PP_PIDGIN_BUILD += 'talkfilters'
    -endif
    --- a/talkfilters/talkfilters.c Thu Mar 05 06:36:45 2020 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,416 +0,0 @@
    -/*
    - * A neat little Purple plugin to integrate with GNU Talk Filters.
    - * http://www.hyperrealm.com/talkfilters/talkfilters.html
    - *
    - * Mark Lindner <markl@gnu.org> 1/6/04
    - * Updates for the purple plugin pack (C) 2005 by
    - * Peter Lawler <bleeter from users.sf.net>
    - */
    -
    -/* TODO:
    --- slash commands (allowing it to be a one liner)
    --- allow saving different filters for different buddies (or accounts)
    -*/
    -
    -/* If you can't figure out what this line is for, DON'T TOUCH IT. */
    -#include "../common/pp_internal.h"
    -
    -#include <stdio.h>
    -#include <string.h>
    -#ifndef _WIN32
    -#include <strings.h>
    -#endif
    -#include <sys/types.h>
    -#include <sys/stat.h>
    -
    -
    -#include <debug.h>
    -#include <signals.h>
    -#include <util.h>
    -
    -#include <gtkplugin.h>
    -#include <gtkutils.h>
    -
    -#include <talkfilters.h>
    -
    -#define PREF_PREFIX "/plugins/gtk/bleeter/talkfilters"
    -#define PREF_ENABLED PREF_PREFIX "/enabled"
    -#define PREF_FILTER PREF_PREFIX "/filter"
    -
    -#define PROP_FILTER "talkfilter::filter"
    -
    -static const gtf_filter_t *current_filter = NULL;
    -static const gtf_filter_t *filter_list = NULL;
    -static int filter_count = 0;
    -
    -static void
    -translate_message(char **message, const gtf_filter_t *filter) {
    - if (message == NULL || *message == NULL) {
    - purple_debug_info("talkfilters","Null message\n");
    - return;
    - }
    -
    - if(filter != NULL) {
    - gchar *tmp;
    -
    - size_t len = strlen(*message);
    - if(len < 40)
    - len += 40;
    - else
    - len *= 2;
    -
    - /* XXX: Is it always true, or are we just hoping it is? */
    - tmp = (gchar *)g_malloc(len);
    -
    - filter->filter(*message, tmp, len);
    - g_free(*message);
    - *message = tmp;
    - } else {
    - purple_debug_info("talkfilters","No filter set\n");
    - }
    -}
    -
    -static void translate_message_im(PurpleAccount *account, char *who,
    - char **message, gpointer dontcare) {
    - PurpleConversation *conv;
    - PidginConversation *gtkconv;
    -
    - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account);
    - if (!conv)
    - return;
    - gtkconv = PIDGIN_CONVERSATION(conv);
    - if (!gtkconv)
    - return;
    -
    - translate_message(message, g_object_get_data(G_OBJECT(gtkconv->imhtml), PROP_FILTER));
    -}
    -
    -static void translate_message_chat(PurpleAccount *account, char **message,
    - int id, gpointer dontcare) {
    - PurpleConversation *conv;
    - PidginConversation *gtkconv;
    -
    - conv = purple_find_chat(account->gc, id);
    - if (!conv)
    - return;
    -
    - gtkconv = PIDGIN_CONVERSATION(conv);
    - if (!gtkconv)
    - return;
    -
    - translate_message(message, g_object_get_data(G_OBJECT(gtkconv->imhtml), PROP_FILTER));
    -}
    -
    -static void
    -update_selected_filter() {
    - const gtf_filter_t *filter;
    - gint ct;
    - const char *val = purple_prefs_get_string(PREF_FILTER);
    -
    - current_filter = NULL;
    - ct = filter_count;
    - for(filter = filter_list; ct; filter++, ct--) {
    - /* XXX: Is this overkill? Is strcmp enough? */
    - if (g_utf8_collate(val, filter->name) == 0) {
    - current_filter = filter;
    - purple_debug_info("talkfilters", "found default filter \"%s\"\n", filter->name);
    - break;
    - }
    - }
    -}
    -
    -static void
    -filter_changed_cb(const char *name, PurplePrefType type, gconstpointer val, gpointer dontcare) {
    - update_selected_filter();
    -}
    -
    -static gboolean writing_im_msg(PurpleAccount *account, const char *who, char **message,
    - PurpleConversation *conv, PurpleMessageFlags flags, gpointer dontcare) {
    - if (flags & PURPLE_MESSAGE_SEND)
    - {
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    - if (!gtkconv)
    - return FALSE;
    -
    - translate_message(message, g_object_get_data(G_OBJECT(gtkconv->imhtml), PROP_FILTER));
    - }
    - return FALSE;
    -}
    -
    -static void
    -menu_filter_changed_cb(GtkWidget *w, PidginWindow *win)
    -{
    - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
    - {
    - PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
    - g_object_set_data(G_OBJECT(gtkconv->imhtml), PROP_FILTER,
    - g_object_get_data(G_OBJECT(w), PROP_FILTER));
    - }
    -}
    -
    -static void
    -regenerate_talkfilter_menu(PidginConversation *gtkconv)
    -{
    - PidginWindow *win;
    - GtkWidget *menu;
    - int count;
    - const gtf_filter_t *filter;
    - const gtf_filter_t *curfilter;
    - GtkWidget *mitem, *item;
    - GSList *list = NULL;
    -
    - if (gtkconv == NULL)
    - return;
    -
    - win = pidgin_conv_get_window(gtkconv);
    - if (win == NULL)
    - return;
    -
    - mitem = g_object_get_data(G_OBJECT(win->window), PROP_FILTER);
    - if (mitem == NULL)
    - {
    - mitem = gtk_menu_item_new_with_mnemonic(_("_Talkfilters")); /* XXX: or is it "Talk Filters"? */
    - gtk_menu_shell_insert(GTK_MENU_SHELL(win->menu.menubar), mitem, 3);
    - g_object_set_data(G_OBJECT(win->window), PROP_FILTER, mitem);
    - gtk_widget_show(mitem);
    - }
    - else
    - return;
    -
    - menu = gtk_menu_new();
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), menu);
    -
    - curfilter = g_object_get_data(G_OBJECT(gtkconv->imhtml), PROP_FILTER);
    -
    - item = gtk_radio_menu_item_new_with_label(list, _("(None)"));
    - list = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    - g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(menu_filter_changed_cb), win);
    -
    - for (count = filter_count, filter = filter_list; count; filter++, count--)
    - {
    - item = gtk_radio_menu_item_new_with_label(list, filter->desc);
    - g_object_set_data(G_OBJECT(item), PROP_FILTER, (gpointer)filter);
    - list = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
    -
    - g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(menu_filter_changed_cb), win);
    -
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    - }
    -
    - gtk_widget_show_all(menu);
    -}
    -
    -static void
    -update_talkfilter_selection(PidginConversation *gtkconv)
    -{
    - PidginWindow *win;
    - GtkWidget *menu;
    - GList *item;
    - const gtf_filter_t *filter;
    -
    - if (gtkconv == NULL)
    - return;
    -
    - win = pidgin_conv_get_window(gtkconv);
    - if (win == NULL)
    - return;
    -
    - menu = g_object_get_data(G_OBJECT(win->window), PROP_FILTER);
    - if (menu == NULL)
    - return;
    - menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu));
    -
    - filter = g_object_get_data(G_OBJECT(gtkconv->imhtml), PROP_FILTER);
    -
    - for (item = gtk_container_get_children(GTK_CONTAINER(menu));
    - item; item = item->next)
    - {
    - if (filter == g_object_get_data(G_OBJECT(item->data), PROP_FILTER))
    - {
    - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->data), TRUE);
    - break;
    - }
    - }
    -}
    -
    -static void
    -conversation_switched_cb(PurpleConversation *conv)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    - regenerate_talkfilter_menu(gtkconv);
    - update_talkfilter_selection(gtkconv);
    -}
    -
    -static void
    -conversation_created_cb(PurpleConversation *conv)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    -
    - /* hopefully fix a crash related to persistent convs */
    - if(gtkconv == NULL)
    - return;
    -
    - g_object_set_data(G_OBJECT(gtkconv->imhtml), PROP_FILTER, (gpointer)current_filter);
    - update_talkfilter_selection(gtkconv);
    -}
    -
    -static void attach_talkfilter_menu(gpointer data, gpointer dontcare)
    -{
    - PidginWindow *win = data;
    - PidginConversation *gtkconv;
    -
    - gtkconv = pidgin_conv_window_get_active_gtkconv(win);
    - regenerate_talkfilter_menu(gtkconv);
    - update_talkfilter_selection(gtkconv);
    -}
    -
    -static gboolean plugin_load(PurplePlugin *plugin)
    -{
    - void *conv_handle = purple_conversations_get_handle();
    -
    - filter_list = gtf_filter_list();
    - filter_count = gtf_filter_count();
    - update_selected_filter();
    -
    - purple_signal_connect(conv_handle, "sending-im-msg",
    - plugin, PURPLE_CALLBACK(translate_message_im), NULL);
    - purple_signal_connect(conv_handle, "sending-chat-msg",
    - plugin, PURPLE_CALLBACK(translate_message_chat), NULL);
    -
    - /* XXX: This is necessary because the changed message isn't displayed locally.
    - * This doesn't always show the exact filtered message that is sent, but
    - * I guess it's better than no indication that the message was filtered.
    - * -- sadrul
    - */
    - purple_signal_connect(conv_handle, "writing-im-msg", plugin,
    - PURPLE_CALLBACK(writing_im_msg), NULL);
    -
    - purple_prefs_connect_callback(plugin, PREF_FILTER,
    - filter_changed_cb, NULL);
    -
    - /* Add a `Talkfilters' menu in the conversation window */
    - purple_signal_connect(conv_handle, "conversation-created", plugin,
    - PURPLE_CALLBACK(conversation_created_cb), NULL);
    - purple_signal_connect(pidgin_conversations_get_handle(), "conversation-switched",
    - plugin, PURPLE_CALLBACK(conversation_switched_cb), NULL);
    -
    - g_list_foreach(pidgin_conv_windows_get_list(), attach_talkfilter_menu, NULL);
    -
    - return TRUE;
    -}
    -
    -static void remove_talkfilter_menu(gpointer data, gpointer dontcare)
    -{
    - PidginWindow *win = data;
    - GtkWidget *menu;
    -
    - menu = g_object_get_data(G_OBJECT(win->window), PROP_FILTER);
    - if (menu)
    - {
    - gtk_widget_destroy(menu);
    - g_object_set_data(G_OBJECT(win->window), PROP_FILTER, NULL);
    -
    - /* XXX: Do we need to set PROP_FILTER data to NULL for each gtkconv->imhtml as well?
    - * It doesn't seem to be necessary right now. The GTF library probably gets loaded
    - * at the very beginning when Purple starts, and not when this plugin is loaded. */
    - }
    -}
    -
    -static gboolean plugin_unload(PurplePlugin *plugin)
    -{
    - purple_prefs_disconnect_by_handle(plugin);
    -
    - g_list_foreach(pidgin_conv_windows_get_list(), remove_talkfilter_menu, NULL);
    -
    - return TRUE;
    -}
    -
    -static PurplePluginPrefFrame *
    -get_plugin_pref_frame(PurplePlugin *plugin)
    -{
    - PurplePluginPrefFrame *frame;
    - PurplePluginPref *pref;
    - const gtf_filter_t *filter;
    - gint ct;
    -
    - frame = purple_plugin_pref_frame_new();
    -
    - pref = purple_plugin_pref_new_with_label(_("Talk Filters"));
    - purple_plugin_pref_frame_add(frame, pref);
    -
    - pref = purple_plugin_pref_new_with_name_and_label(PREF_FILTER, _("Active filter:"));
    - purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_CHOICE);
    -
    - purple_plugin_pref_add_choice(pref, _("(None)"), "");
    - ct = filter_count;
    - for(filter = filter_list; ct; filter++, ct--)
    - {
    - purple_plugin_pref_add_choice(pref, filter->desc, (gpointer)filter->name);
    - }
    -
    - purple_plugin_pref_frame_add(frame, pref);
    -
    - return frame;
    -}
    -
    -static PurplePluginUiInfo prefs_info = {
    - get_plugin_pref_frame,
    - 0,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static PurplePluginInfo talkfilters_info = {
    - PURPLE_PLUGIN_MAGIC,
    - PURPLE_MAJOR_VERSION,
    - PURPLE_MINOR_VERSION,
    - PURPLE_PLUGIN_STANDARD, /* type */
    - PIDGIN_PLUGIN_TYPE, /* ui requirement */
    - 0, /* flags */
    - NULL, /* dependencies */
    - PURPLE_PRIORITY_DEFAULT, /* priority */
    - "gtk-plugin_pack-talkfilters", /* id */
    - NULL, /* name */
    - PP_VERSION,
    - NULL, /* summary */
    - NULL, /* description */
    - "Mark Lindner <markl@gnu.org>, "
    - "Peter Lawler <bleeter@users.sf.net>",
    - PP_WEBSITE,
    - plugin_load,
    - plugin_unload,
    - NULL,
    -
    - NULL,
    - NULL,
    - &prefs_info,
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -static void init_plugin(PurplePlugin *plugin) {
    - purple_prefs_add_none("/plugins/gtk/bleeter");
    - purple_prefs_add_none("/plugins/gtk/bleeter/talkfilters");
    - purple_prefs_add_bool("/plugins/gtk/bleeter/talkfilters/enabled", FALSE);
    - purple_prefs_add_string("/plugins/gtk/bleeter/talkfilters/filter", "");
    - talkfilters_info.name = _("GNU Talk Filters");
    - talkfilters_info.summary =
    - _("Translates text in outgoing messages into humorous dialects.");
    - talkfilters_info.description =
    - _("The GNU Talk Filters are filter programs that convert ordinary "
    - "English text into text that mimics a stereotyped or otherwise "
    - "humorous dialect. These filters have been in the public domain for "
    - "many years, and have been made available as a single integrated "
    - "package. The filters include austro, b1ff, brooklyn, chef, cockney, "
    - "drawl, dubya, fudd, funetak, jethro, jive, kraut, pansy, pirate, "
    - "postmodern, redneck, valspeak, and warez.");
    -}
    -
    -PURPLE_INIT_PLUGIN(talkfilters, init_plugin, talkfilters_info)