diff options
Diffstat (limited to 'src/common/chanopt.c')
-rw-r--r-- | src/common/chanopt.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/src/common/chanopt.c b/src/common/chanopt.c new file mode 100644 index 00000000..a4fd8faa --- /dev/null +++ b/src/common/chanopt.c @@ -0,0 +1,431 @@ +/* per-channel/dialog settings :: /CHANOPT */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include "xchat.h" + +#include "cfgfiles.h" +#include "server.h" +#include "text.h" +#include "util.h" +#include "xchatc.h" + + +static GSList *chanopt_list = NULL; +static gboolean chanopt_open = FALSE; +static gboolean chanopt_changed = FALSE; + + +typedef struct +{ + char *name; + char *alias; /* old names from 2.8.4 */ + int offset; +} channel_options; + +#define S_F(xx) STRUCT_OFFSET_STR(struct session,xx) + +static const channel_options chanopt[] = +{ + {"alert_beep", "BEEP", S_F(alert_beep)}, + {"alert_taskbar", NULL, S_F(alert_taskbar)}, + {"alert_tray", "TRAY", S_F(alert_tray)}, + + {"text_hidejoinpart", "CONFMODE", S_F(text_hidejoinpart)}, + {"text_logging", NULL, S_F(text_logging)}, + {"text_scrollback", NULL, S_F(text_scrollback)}, +}; + +#undef S_F + +static char * +chanopt_value (guint8 val) +{ + switch (val) + { + case SET_OFF: + return "OFF"; + case SET_ON: + return "ON"; + default: + return "{unset}"; + } +} + +/* handle the /CHANOPT command */ + +int +chanopt_command (session *sess, char *tbuf, char *word[], char *word_eol[]) +{ + int dots, i = 0, j, p = 0; + guint8 val; + int offset = 2; + char *find; + gboolean quiet = FALSE; + int newval = -1; + + if (!strcmp (word[2], "-quiet")) + { + quiet = TRUE; + offset++; + } + + find = word[offset++]; + + if (word[offset][0]) + { + if (!strcasecmp (word[offset], "ON")) + newval = 1; + else if (!strcasecmp (word[offset], "OFF")) + newval = 0; + else if (word[offset][0] == 'u') + newval = SET_DEFAULT; + else + newval = atoi (word[offset]); + } + + if (!quiet) + PrintTextf (sess, "\002Network\002: %s \002Channel\002: %s\n", + sess->server->network ? server_get_network (sess->server, TRUE) : _("<none>"), + sess->channel[0] ? sess->channel : _("<none>")); + + while (i < sizeof (chanopt) / sizeof (channel_options)) + { + if (find[0] == 0 || match (find, chanopt[i].name) || (chanopt[i].alias && match (find, chanopt[i].alias))) + { + if (newval != -1) /* set new value */ + { + *(guint8 *)G_STRUCT_MEMBER_P(sess, chanopt[i].offset) = newval; + } + + if (!quiet) /* print value */ + { + strcpy (tbuf, chanopt[i].name); + p = strlen (tbuf); + + tbuf[p++] = 3; + tbuf[p++] = '2'; + + dots = 20 - strlen (chanopt[i].name); + + for (j = 0; j < dots; j++) + tbuf[p++] = '.'; + tbuf[p++] = 0; + + val = G_STRUCT_MEMBER (guint8, sess, chanopt[i].offset); + PrintTextf (sess, "%s\0033:\017 %s", tbuf, chanopt_value (val)); + } + } + i++; + } + + return TRUE; +} + +/* is a per-channel setting set? Or is it UNSET and + * the global version is set? */ + +gboolean +chanopt_is_set (unsigned int global, guint8 per_chan_setting) +{ + if (per_chan_setting == SET_DEFAULT) + return global; + + return per_chan_setting; +} + +/* additive version */ + +gboolean +chanopt_is_set_a (unsigned int global, guint8 per_chan_setting) +{ + if (per_chan_setting == SET_DEFAULT) + return global; + + return per_chan_setting || global; +} + +/* === below is LOADING/SAVING stuff only === */ + +typedef struct +{ + /* Per-Channel Alerts */ + /* use a byte, because we need a pointer to each element */ + guint8 alert_beep; + guint8 alert_taskbar; + guint8 alert_tray; + + /* Per-Channel Settings */ + guint8 text_hidejoinpart; + guint8 text_logging; + guint8 text_scrollback; + + char *network; + char *channel; + +} chanopt_in_memory; + + +static chanopt_in_memory * +chanopt_find (char *network, char *channel, gboolean add_new) +{ + GSList *list; + chanopt_in_memory *co; + int i; + + for (list = chanopt_list; list; list = list->next) + { + co = list->data; + if (!strcasecmp (co->channel, channel) && + !strcasecmp (co->network, network)) + return co; + } + + if (!add_new) + return NULL; + + /* allocate a new one */ + co = g_malloc0 (sizeof (chanopt_in_memory)); + co->channel = g_strdup (channel); + co->network = g_strdup (network); + + /* set all values to SET_DEFAULT */ + i = 0; + while (i < sizeof (chanopt) / sizeof (channel_options)) + { + *(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = SET_DEFAULT; + i++; + } + + chanopt_list = g_slist_prepend (chanopt_list, co); + chanopt_changed = TRUE; + + return co; +} + +static void +chanopt_add_opt (chanopt_in_memory *co, char *var, int new_value) +{ + int i; + + i = 0; + while (i < sizeof (chanopt) / sizeof (channel_options)) + { + if (!strcmp (var, chanopt[i].name)) + { + *(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = new_value; + + } + i++; + } +} + +/* load chanopt.conf from disk into our chanopt_list GSList */ + +static void +chanopt_load_all (void) +{ + int fh; + char buf[256]; + char *eq; + char *network = NULL; + chanopt_in_memory *current = NULL; + + /* 1. load the old file into our GSList */ + fh = xchat_open_file ("chanopt.conf", O_RDONLY, 0, 0); + if (fh != -1) + { + while (waitline (fh, buf, sizeof buf, FALSE) != -1) + { + eq = strchr (buf, '='); + if (!eq) + continue; + eq[0] = 0; + + if (eq != buf && eq[-1] == ' ') + eq[-1] = 0; + + if (!strcmp (buf, "network")) + { + g_free (network); + network = g_strdup (eq + 2); + } + else if (!strcmp (buf, "channel")) + { + current = chanopt_find (network, eq + 2, TRUE); + chanopt_changed = FALSE; + } + else + { + if (current) + chanopt_add_opt (current, buf, atoi (eq + 2)); + } + + } + close (fh); + g_free (network); + } +} + +void +chanopt_load (session *sess) +{ + int i; + guint8 val; + chanopt_in_memory *co; + char *network; + + if (sess->channel[0] == 0) + return; + + network = server_get_network (sess->server, FALSE); + if (!network) + return; + + if (!chanopt_open) + { + chanopt_open = TRUE; + chanopt_load_all (); + } + + co = chanopt_find (network, sess->channel, FALSE); + if (!co) + return; + + /* fill in all the sess->xxxxx fields */ + i = 0; + while (i < sizeof (chanopt) / sizeof (channel_options)) + { + val = G_STRUCT_MEMBER(guint8, co, chanopt[i].offset); + *(guint8 *)G_STRUCT_MEMBER_P(sess, chanopt[i].offset) = val; + i++; + } +} + +void +chanopt_save (session *sess) +{ + int i; + guint8 vals; + guint8 valm; + chanopt_in_memory *co; + char *network; + + if (sess->channel[0] == 0) + return; + + network = server_get_network (sess->server, FALSE); + if (!network) + return; + + /* 2. reconcile sess with what we loaded from disk */ + + co = chanopt_find (network, sess->channel, TRUE); + + i = 0; + while (i < sizeof (chanopt) / sizeof (channel_options)) + { + vals = G_STRUCT_MEMBER(guint8, sess, chanopt[i].offset); + valm = G_STRUCT_MEMBER(guint8, co, chanopt[i].offset); + + if (vals != valm) + { + *(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = vals; + chanopt_changed = TRUE; + } + + i++; + } +} + +static void +chanopt_save_one_channel (chanopt_in_memory *co, int fh) +{ + int i; + char buf[256]; + guint8 val; + + snprintf (buf, sizeof (buf), "%s = %s\n", "network", co->network); + write (fh, buf, strlen (buf)); + + snprintf (buf, sizeof (buf), "%s = %s\n", "channel", co->channel); + write (fh, buf, strlen (buf)); + + i = 0; + while (i < sizeof (chanopt) / sizeof (channel_options)) + { + val = G_STRUCT_MEMBER (guint8, co, chanopt[i].offset); + if (val != SET_DEFAULT) + { + snprintf (buf, sizeof (buf), "%s = %d\n", chanopt[i].name, val); + write (fh, buf, strlen (buf)); + } + i++; + } +} + +void +chanopt_save_all (void) +{ + int i; + int num_saved; + int fh; + GSList *list; + chanopt_in_memory *co; + guint8 val; + + if (!chanopt_list || !chanopt_changed) + { + return; + } + + fh = xchat_open_file ("chanopt.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE); + if (fh == -1) + { + return; + } + + for (num_saved = 0, list = chanopt_list; list; list = list->next) + { + co = list->data; + + i = 0; + while (i < sizeof (chanopt) / sizeof (channel_options)) + { + val = G_STRUCT_MEMBER (guint8, co, chanopt[i].offset); + /* not using global/default setting, must save */ + if (val != SET_DEFAULT) + { + if (num_saved != 0) + write (fh, "\n", 1); + + chanopt_save_one_channel (co, fh); + num_saved++; + goto cont; + } + i++; + } + +cont: + g_free (co->network); + g_free (co->channel); + g_free (co); + } + + close (fh); + + /* we're quiting, no need to free */ + + /*g_slist_free (chanopt_list); + chanopt_list = NULL; + + chanopt_open = FALSE; + chanopt_changed = FALSE;*/ +} |