diff options
Diffstat (limited to 'src')
32 files changed, 412 insertions, 1453 deletions
diff --git a/src/core/settings.c b/src/core/settings.c index e65ceb2c..4e0717cd 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -585,6 +585,7 @@ void settings_check_module(const char *module) for (; tmp != NULL; tmp = next) { node = tmp->data; next = config_node_next(tmp); + if (node->key == NULL) continue; set = g_hash_table_lookup(settings, node->key); if (backwards_compatibility(module, node, parent)) diff --git a/src/fe-common/core/completion.c b/src/fe-common/core/completion.c index c87fdb50..76dfbb79 100644 --- a/src/fe-common/core/completion.c +++ b/src/fe-common/core/completion.c @@ -37,8 +37,11 @@ static int last_want_space, last_line_pos; #define isseparator_notspace(c) \ ((c) == ',') +#define isseparator_space(c) \ + ((c) == ' ') + #define isseparator(c) \ - ((c) == ' ' || isseparator_notspace(c)) + (isseparator_space(c) || isseparator_notspace(c)) void chat_completion_init(void); void chat_completion_deinit(void); @@ -153,20 +156,23 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i word = NULL; linestart = NULL; } else { + char* old_wordstart; + /* get the word we want to complete */ word = get_word_at(line, *pos, &wordstart); + old_wordstart = wordstart; + startpos = (int) (wordstart-line); wordlen = strlen(word); - /* get the start of line until the word we're completing */ - if (isseparator(*line)) { - /* empty space at the start of line */ - if (wordstart == line) - wordstart += strlen(wordstart); - } else { - while (wordstart > line && isseparator(wordstart[-1])) - wordstart--; - } + /* remove trailing spaces from linestart */ + while (wordstart > line && isseparator_space(wordstart[-1])) + wordstart--; + + /* unless everything was spaces */ + if (old_wordstart > line && wordstart == line) + wordstart = old_wordstart - 1; + linestart = g_strndup(line, (int) (wordstart-line)); /* completions usually add space after the word, that makes @@ -175,19 +181,24 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i BUT if we start completion with "/msg "<tab>, we don't want to complete the /msg word, but instead complete empty word with /msg being in linestart. */ - if (!erase && *pos > 0 && line[*pos-1] == ' ' && - (*linestart == '\0' || wordstart[-1] != ' ')) { + if (!erase && *pos > 0 && isseparator_space(line[*pos-1]) && + (*linestart == '\0' || !isseparator_space(wordstart[-1]))) { char *old; - old = linestart; + old = linestart; linestart = *linestart == '\0' ? g_strdup(word) : - g_strconcat(linestart, " ", word, NULL); + g_strdup_printf("%s%c%s", + /* do not accidentally duplicate the word separator */ + line == wordstart - 1 ? "" : linestart, + wordstart[-1], word); g_free(old); g_free(word); word = g_strdup(""); - startpos = strlen(linestart)+1; + + startpos = *linestart == '\0' ? 0 : + strlen(linestart)+1; wordlen = 0; } @@ -334,7 +345,9 @@ GList *filename_complete(const char *path, const char *default_path) (dp->d_name[1] == '.' && dp->d_name[2] == '\0')) continue; /* skip . and .. */ - if (basename[0] != '.') + /* Skip the dotfiles unless the user explicitly asked us + * to do so. Basename might be './', beware of that */ + if (basename[0] != '.' || basename[1] == '\0') continue; } @@ -397,7 +410,8 @@ static GList *completion_get_aliases(const char *alias, char cmdchar) continue; if (g_ascii_strncasecmp(node->key, alias, len) == 0) { - word = g_strdup_printf("%c%s", cmdchar, node->key); + word = cmdchar == '\0' ? g_strdup(node->key) : + g_strdup_printf("%c%s", cmdchar, node->key); /* add matching alias to completion list, aliases will be appended after command completions and kept in uppercase to show it's an alias */ @@ -589,13 +603,19 @@ static void sig_complete_word(GList **list, WINDOW_REC *window, /* command completion? */ cmdchars = settings_get_str("cmdchars"); - if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) { + if (*word != '\0' && ((*linestart == '\0' && strchr(cmdchars, *word)) || + (*linestart != '\0' && linestart[1] == '\0' && + strchr(cmdchars, *linestart)))) { + gboolean skip = *linestart == '\0' ? TRUE : FALSE; + /* complete /command */ - *list = completion_get_commands(word+1, *word); + *list = completion_get_commands(word + (skip ? 1 : 0), + skip ? *word : '\0'); /* complete aliases, too */ *list = g_list_concat(*list, - completion_get_aliases(word+1, *word)); + completion_get_aliases(word + (skip ? 1 : 0), + skip ? *word : '\0')); if (*list != NULL) signal_stop(); return; diff --git a/src/fe-common/core/fe-channels.c b/src/fe-common/core/fe-channels.c index d87c4ce5..00aac885 100644 --- a/src/fe-common/core/fe-channels.c +++ b/src/fe-common/core/fe-channels.c @@ -248,9 +248,7 @@ static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item) } } -/* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>] - <channel> <network> [<password>] */ -static void cmd_channel_add(const char *data) +static void cmd_channel_add_modify(const char *data, gboolean add) { GHashTable *optlist; CHATNET_REC *chatnetrec; @@ -259,18 +257,19 @@ static void cmd_channel_add(const char *data) void *free_arg; if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS, - "channel add", &optlist, &channel, &chatnet, &password)) + "channel add", &optlist, &channel, &chatnet, &password)) return; - if (*chatnet == '\0' || *channel == '\0') + if (*chatnet == '\0' || *channel == '\0') { cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + } chatnetrec = chatnet_find(chatnet); if (chatnetrec == NULL) { printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_UNKNOWN_CHATNET, chatnet); + TXT_UNKNOWN_CHATNET, chatnet); cmd_params_free(free_arg); - return; + return; } botarg = g_hash_table_lookup(optlist, "bots"); @@ -278,6 +277,13 @@ static void cmd_channel_add(const char *data) rec = channel_setup_find(channel, chatnet); if (rec == NULL) { + if (add == FALSE) { + cmd_params_free(free_arg); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CHANSETUP_NOT_FOUND, channel, chatnet); + return; + } + rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup(); rec->name = g_strdup(channel); rec->chatnet = g_strdup(chatnet); @@ -301,6 +307,18 @@ static void cmd_channel_add(const char *data) cmd_params_free(free_arg); } +/* SYNTAX: CHANNEL ADD|MODIFY [-auto | -noauto] [-bots <masks>] [-botcmd <command>] + <channel> <network> [<password>] */ +static void cmd_channel_add(const char *data) +{ + cmd_channel_add_modify(data, TRUE); +} + +static void cmd_channel_modify(const char *data) +{ + cmd_channel_add_modify(data, FALSE); +} + /* SYNTAX: CHANNEL REMOVE <channel> <network> */ static void cmd_channel_remove(const char *data) { @@ -619,12 +637,14 @@ void fe_channels_init(void) command_bind("join", NULL, (SIGNAL_FUNC) cmd_join); command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel); command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add); + command_bind("channel modify", NULL, (SIGNAL_FUNC) cmd_channel_modify); command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove); command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list); command_bind("names", NULL, (SIGNAL_FUNC) cmd_names); command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle); command_set_options("channel add", "auto noauto -bots -botcmd"); + command_set_options("channel modify", "auto noauto -bots -botcmd"); command_set_options("names", "count ops halfops voices normal"); command_set_options("join", "invite window"); } @@ -640,6 +660,7 @@ void fe_channels_deinit(void) command_unbind("join", (SIGNAL_FUNC) cmd_join); command_unbind("channel", (SIGNAL_FUNC) cmd_channel); command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add); + command_unbind("channel modify", (SIGNAL_FUNC) cmd_channel_modify); command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove); command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list); command_unbind("names", (SIGNAL_FUNC) cmd_names); diff --git a/src/fe-common/core/fe-ignore.c b/src/fe-common/core/fe-ignore.c index a809ac91..52b11e6b 100644 --- a/src/fe-common/core/fe-ignore.c +++ b/src/fe-common/core/fe-ignore.c @@ -215,7 +215,7 @@ static void cmd_unignore(const char *data) { IGNORE_REC *rec; GSList *tmp; - char *mask; + char *mask, *mask_orig; void *free_arg; if (!cmd_get_params(data, &free_arg, 1, &mask)) @@ -224,6 +224,10 @@ static void cmd_unignore(const char *data) if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + /* Save the mask string here since it might be modified in the code + * below and we need it to print meaningful error messages. */ + mask_orig = mask; + if (is_numeric(mask, ' ')) { /* with index number */ tmp = g_slist_nth(ignores, atoi(mask)-1); @@ -248,7 +252,7 @@ static void cmd_unignore(const char *data) ignore_update_rec(rec); } else { printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_IGNORE_NOT_FOUND, mask); + TXT_IGNORE_NOT_FOUND, mask_orig); } cmd_params_free(free_arg); } diff --git a/src/fe-common/core/fe-messages.c b/src/fe-common/core/fe-messages.c index bc2a30d6..487a5754 100644 --- a/src/fe-common/core/fe-messages.c +++ b/src/fe-common/core/fe-messages.c @@ -603,9 +603,6 @@ static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick) char *nickhost, *p; int n; - if (nick->host == NULL) - return; - firstnick = g_hash_table_lookup(channel->nicks, nick->nick); if (firstnick->next == NULL) return; @@ -618,6 +615,9 @@ static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick) return; /* nope, we have it */ } + if (nick->host == NULL) + return; + /* identical nick already exists, have to change it somehow.. */ p = strchr(nick->host, '@'); if (p == NULL) p = nick->host; else p++; diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c index 429e6dac..468cb707 100644 --- a/src/fe-common/core/fe-server.c +++ b/src/fe-common/core/fe-server.c @@ -104,7 +104,7 @@ static SERVER_SETUP_REC *create_server_setup(GHashTable *optlist) return server; } -static void cmd_server_add(const char *data) +static void cmd_server_add_modify(const char *data, gboolean add) { GHashTable *optlist; SERVER_SETUP_REC *rec; @@ -113,7 +113,7 @@ static void cmd_server_add(const char *data) int port; if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS, - "server add", &optlist, &addr, &portstr, &password)) + "server add", &optlist, &addr, &portstr, &password)) return; if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); @@ -124,6 +124,13 @@ static void cmd_server_add(const char *data) rec = server_setup_find(addr, port, chatnet); if (rec == NULL) { + if (add == FALSE) { + cmd_params_free(free_arg); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_SETUPSERVER_NOT_FOUND, addr, port); + return; + } + rec = create_server_setup(optlist); if (rec == NULL) { cmd_params_free(free_arg); @@ -205,6 +212,16 @@ static void cmd_server_add(const char *data) cmd_params_free(free_arg); } +static void cmd_server_add(const char *data) +{ + cmd_server_add_modify(data, TRUE); +} + +static void cmd_server_modify(const char *data) +{ + cmd_server_add_modify(data, FALSE); +} + /* SYNTAX: SERVER REMOVE <address> [<port>] [<network>] */ static void cmd_server_remove(const char *data) { @@ -388,10 +405,12 @@ void fe_server_init(void) command_bind("server", NULL, (SIGNAL_FUNC) cmd_server); command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect); command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add); + command_bind("server modify", NULL, (SIGNAL_FUNC) cmd_server_modify); command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove); command_bind_first("server", NULL, (SIGNAL_FUNC) server_command); command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command); command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); + command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting); @@ -412,6 +431,7 @@ void fe_server_deinit(void) command_unbind("server", (SIGNAL_FUNC) cmd_server); command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect); command_unbind("server add", (SIGNAL_FUNC) cmd_server_add); + command_unbind("server modify", (SIGNAL_FUNC) cmd_server_modify); command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove); command_unbind("server", (SIGNAL_FUNC) server_command); command_unbind("disconnect", (SIGNAL_FUNC) server_command); diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index 3e88426f..9aa7698d 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -131,6 +131,8 @@ void unformat_24bit_color(char **ptr, int off, int *fgcolor, int *bgcolor, int * unsigned char rgbx[4]; unsigned int i; for (i = 0; i < 4; ++i) { + if ((*ptr)[i + off] == '\0') + return; rgbx[i] = (*ptr)[i + off]; } rgbx[3] -= 0x20; @@ -1341,6 +1343,9 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) bgcolor = *ptr==(char)0xff ? -1 : *ptr-'0'; } } + if (*ptr == '\0') + break; + ptr++; break; case 6: diff --git a/src/fe-common/irc/fe-irc-channels.c b/src/fe-common/irc/fe-irc-channels.c index a2737fc3..0ec30003 100644 --- a/src/fe-common/irc/fe-irc-channels.c +++ b/src/fe-common/irc/fe-irc-channels.c @@ -41,7 +41,7 @@ int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target) statusmsg = g_hash_table_lookup(server->isupport, "statusmsg"); if (statusmsg == NULL) - statusmsg = "@+"; + statusmsg = "@"; return strchr(statusmsg, *target) != NULL; } @@ -61,12 +61,9 @@ const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target) statusmsg = g_hash_table_lookup(server->isupport, "statusmsg"); /* Hack: for bahamut 1.4 which sends neither STATUSMSG nor - * WALLCHOPS in 005, accept @#chan and @+#chan (but not +#chan) */ - if (statusmsg == NULL && *target != '@') - return target; - + * WALLCHOPS in 005 */ if (statusmsg == NULL) - statusmsg = "@+"; + statusmsg = "@"; /* Strip the leading statusmsg prefixes */ while (strchr(statusmsg, *target) != NULL) { diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c index 2cb99a2f..2e22d6f2 100644 --- a/src/fe-common/irc/fe-irc-server.c +++ b/src/fe-common/irc/fe-irc-server.c @@ -50,12 +50,13 @@ const char *get_visible_target(IRC_SERVER_REC *server, const char *target) return target; } -/* SYNTAX: SERVER ADD [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>] - [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>] - [-ssl_ciphers <list>] - [-auto | -noauto] [-network <network>] [-host <hostname>] - [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>] - <address> [<port> [<password>]] */ + +/* SYNTAX: SERVER ADD|MODIFY [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>] + [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>] + [-ssl_ciphers <list>] + [-auto | -noauto] [-network <network>] [-host <hostname>] + [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>] + <address> [<port> [<password>]] */ /* NOTE: -network replaces the old -ircnet flag. */ static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec, GHashTable *optlist) diff --git a/src/fe-common/irc/fe-ircnet.c b/src/fe-common/irc/fe-ircnet.c index 4d7037d5..b70a9ea7 100644 --- a/src/fe-common/irc/fe-ircnet.c +++ b/src/fe-common/irc/fe-ircnet.c @@ -88,14 +88,7 @@ static void cmd_network_list(void) printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETWORK_FOOTER); } -/* SYNTAX: NETWORK ADD [-nick <nick>] [-user <user>] [-realname <name>] - [-host <host>] [-usermode <mode>] [-autosendcmd <cmd>] - [-querychans <count>] [-whois <count>] [-msgs <count>] - [-kicks <count>] [-modes <count>] [-cmdspeed <ms>] - [-cmdmax <count>] [-sasl_mechanism <mechanism>] - [-sasl_username <username>] [-sasl_password <password>] - <name> */ -static void cmd_network_add(const char *data) +static void cmd_network_add_modify(const char *data, gboolean add) { GHashTable *optlist; char *name, *value; @@ -103,12 +96,20 @@ static void cmd_network_add(const char *data) IRC_CHATNET_REC *rec; if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, - "network add", &optlist, &name)) + "network add", &optlist, &name)) return; + if (*name == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); rec = ircnet_find(name); if (rec == NULL) { + if (add == FALSE) { + cmd_params_free(free_arg); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NETWORK_NOT_FOUND, name); + return; + } + rec = g_new0(IRC_CHATNET_REC, 1); rec->name = g_strdup(name); } else { @@ -174,6 +175,23 @@ static void cmd_network_add(const char *data) cmd_params_free(free_arg); } +/* SYNTAX: NETWORK ADD|MODIFY [-nick <nick>] [-user <user>] [-realname <name>] + [-host <host>] [-usermode <mode>] [-autosendcmd <cmd>] + [-querychans <count>] [-whois <count>] [-msgs <count>] + [-kicks <count>] [-modes <count>] [-cmdspeed <ms>] + [-cmdmax <count>] [-sasl_mechanism <mechanism>] + [-sasl_username <username>] [-sasl_password <password>] + <name> */ +static void cmd_network_add(const char *data) +{ + cmd_network_add_modify(data, TRUE); +} + +static void cmd_network_modify(const char *data) +{ + cmd_network_add_modify(data, FALSE); +} + /* SYNTAX: NETWORK REMOVE <network> */ static void cmd_network_remove(const char *data) { @@ -206,10 +224,13 @@ void fe_ircnet_init(void) command_bind("network", NULL, (SIGNAL_FUNC) cmd_network); command_bind("network list", NULL, (SIGNAL_FUNC) cmd_network_list); command_bind("network add", NULL, (SIGNAL_FUNC) cmd_network_add); + command_bind("network modify", NULL, (SIGNAL_FUNC) cmd_network_modify); command_bind("network remove", NULL, (SIGNAL_FUNC) cmd_network_remove); command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed " "-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password"); + command_set_options("network modify", "-kicks -msgs -modes -whois -cmdspeed " + "-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password"); } void fe_ircnet_deinit(void) @@ -218,5 +239,6 @@ void fe_ircnet_deinit(void) command_unbind("network", (SIGNAL_FUNC) cmd_network); command_unbind("network list", (SIGNAL_FUNC) cmd_network_list); command_unbind("network add", (SIGNAL_FUNC) cmd_network_add); + command_unbind("network modify", (SIGNAL_FUNC) cmd_network_modify); command_unbind("network remove", (SIGNAL_FUNC) cmd_network_remove); } diff --git a/src/fe-text/Makefile.am b/src/fe-text/Makefile.am index b9538e60..bdd3df22 100644 --- a/src/fe-text/Makefile.am +++ b/src/fe-text/Makefile.am @@ -4,8 +4,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/core/ \ -I$(top_srcdir)/src/fe-common/core/ \ - $(GLIB_CFLAGS) \ - $(CURSES_INCLUDEDIR) + $(GLIB_CFLAGS) irssi_DEPENDENCIES = \ @COMMON_LIBS@ \ @@ -22,25 +21,11 @@ irssi_LDADD = \ @PROG_LIBS@ \ @TEXTUI_LIBS@ -tparm_sources = \ - tparm.c - terminfo_sources = \ term-terminfo.c \ terminfo-core.c -curses_sources = \ - term-curses.c - -if NEED_TPARM -use_tparm_sources = $(tparm_sources) -endif - -if USE_CURSES -use_term_sources = $(curses_sources) -else use_term_sources = $(terminfo_sources) -endif irssi_SOURCES = \ gui-entry.c \ @@ -56,8 +41,6 @@ irssi_SOURCES = \ statusbar-config.c \ statusbar-items.c \ term.c \ - term-dummy.c \ - $(use_tparm_sources) \ $(use_term_sources) \ textbuffer.c \ textbuffer-commands.c \ @@ -85,6 +68,4 @@ noinst_HEADERS = \ module-formats.h EXTRA_DIST = \ - $(tparm_sources) \ - $(terminfo_sources) \ - $(curses_sources) + $(terminfo_sources) diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c index 82645a8e..f05decd2 100644 --- a/src/fe-text/gui-entry.c +++ b/src/fe-text/gui-entry.c @@ -655,7 +655,7 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_ tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1); memcpy(tmp->cutbuffer, tmpcutbuffer, tmp->cutbuffer_len * sizeof(unichar)); - memcpy(tmp->cutbuffer + tmp->cutbuffer_len * sizeof(unichar), + memcpy(tmp->cutbuffer + tmp->cutbuffer_len, entry->text + entry->pos - size, size * sizeof(unichar)); tmp->cutbuffer_len = cutbuffer_new_size; diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c index cad271c9..ad79e0c4 100644 --- a/src/fe-text/irssi.c +++ b/src/fe-text/irssi.c @@ -74,10 +74,7 @@ void mainwindow_activity_deinit(void); void mainwindows_layout_init(void); void mainwindows_layout_deinit(void); -void term_dummy_init(void); -void term_dummy_deinit(void); - -static int dirty, full_redraw, dummy; +static int dirty, full_redraw; static GMainLoop *main_loop; int quitting; @@ -122,7 +119,7 @@ void irssi_set_dirty(void) static void dirty_check(void) { - if (!dirty || dummy) + if (!dirty) return; term_resize_dirty(); @@ -171,27 +168,22 @@ static void textui_finish_init(void) { quitting = FALSE; - if (dummy) - term_dummy_init(); - else { - term_refresh_freeze(); - textbuffer_init(); - textbuffer_view_init(); - textbuffer_commands_init(); - gui_expandos_init(); - gui_printtext_init(); - gui_readline_init(); - lastlog_init(); - mainwindows_init(); - mainwindow_activity_init(); - mainwindows_layout_init(); - gui_windows_init(); - statusbar_init(); - term_refresh_thaw(); - - /* don't check settings with dummy mode */ - settings_check(); - } + term_refresh_freeze(); + textbuffer_init(); + textbuffer_view_init(); + textbuffer_commands_init(); + gui_expandos_init(); + gui_printtext_init(); + gui_readline_init(); + lastlog_init(); + mainwindows_init(); + mainwindow_activity_init(); + mainwindows_layout_init(); + gui_windows_init(); + statusbar_init(); + term_refresh_thaw(); + + settings_check(); module_register("core", "fe-text"); @@ -233,25 +225,21 @@ static void textui_deinit(void) dirty_check(); /* one last time to print any quit messages */ signal_remove("gui exit", (SIGNAL_FUNC) sig_exit); - if (dummy) - term_dummy_deinit(); - else { - lastlog_deinit(); - statusbar_deinit(); - gui_printtext_deinit(); - gui_readline_deinit(); - gui_windows_deinit(); - mainwindows_layout_deinit(); - mainwindow_activity_deinit(); - mainwindows_deinit(); - gui_expandos_deinit(); - textbuffer_commands_deinit(); - textbuffer_view_deinit(); - textbuffer_deinit(); - - term_refresh_thaw(); - term_deinit(); - } + lastlog_deinit(); + statusbar_deinit(); + gui_printtext_deinit(); + gui_readline_deinit(); + gui_windows_deinit(); + mainwindows_layout_deinit(); + mainwindow_activity_deinit(); + mainwindows_deinit(); + gui_expandos_deinit(); + textbuffer_commands_deinit(); + textbuffer_view_deinit(); + textbuffer_deinit(); + + term_refresh_thaw(); + term_deinit(); theme_unregister(); @@ -276,7 +264,6 @@ int main(int argc, char **argv) { static int version = 0; static GOptionEntry options[] = { - { "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, "Use the dummy terminal mode", NULL }, { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display irssi version", NULL }, { NULL } }; @@ -295,7 +282,6 @@ int main(int argc, char **argv) srand(time(NULL)); - dummy = FALSE; quitting = FALSE; core_preinit(argv[0]); @@ -319,9 +305,8 @@ int main(int argc, char **argv) loglev = g_log_set_always_fatal(G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL); textui_init(); - if (!dummy && !term_init()) { - fprintf(stderr, "Can't initialize screen handling, quitting.\n"); - fprintf(stderr, "You can still use the dummy mode with -d parameter\n"); + if (!term_init()) { + fprintf(stderr, "Can't initialize screen handling.\n"); return 1; } @@ -332,9 +317,9 @@ int main(int argc, char **argv) /* Does the same as g_main_run(main_loop), except we can call our dirty-checker after each iteration */ while (!quitting) { - if (!dummy) term_refresh_freeze(); + term_refresh_freeze(); g_main_iteration(TRUE); - if (!dummy) term_refresh_thaw(); + term_refresh_thaw(); if (reload_config) { /* SIGHUP received, do /RELOAD */ diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c index e3e0c2a6..0db4f63a 100644 --- a/src/fe-text/statusbar-items.c +++ b/src/fe-text/statusbar-items.c @@ -289,6 +289,10 @@ static void sig_statusbar_more_updated(void) { int visible; + /* no active window, for example during /window hide */ + if (active_win == NULL) + return; + visible = g_slist_find(more_visible, WINDOW_MAIN(active_win)) != NULL; if (WINDOW_GUI(active_win)->view->more_text != visible) statusbar_items_redraw("more"); diff --git a/src/fe-text/term-curses.c b/src/fe-text/term-curses.c deleted file mode 100644 index 752edd7f..00000000 --- a/src/fe-text/term-curses.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - term-curses.c : irssi - - Copyright (C) 1999-2001 Timo Sirainen - - 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 02110-1301 USA. -*/ - -#include "module.h" -#include "signals.h" -#include "settings.h" - -#include "term.h" -#include "mainwindows.h" - -#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) -# include <ncurses.h> -#else -# include <curses.h> -#endif -#include <termios.h> -#include <signal.h> - -#ifndef COLOR_PAIRS -# define COLOR_PAIRS 64 -#endif - -#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM) -# define USE_RESIZE_TERM -#endif - -#ifndef _POSIX_VDISABLE -# define _POSIX_VDISABLE 0 -#endif - -struct _TERM_WINDOW { - int x, y; - int width, height; - WINDOW *win; -}; - -TERM_WINDOW *root_window; - -static int curs_x, curs_y; -static int freeze_refresh; -static struct termios old_tio; - -static int init_curses(void) -{ - char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - int num; - struct termios tio; - - if (!initscr()) - return FALSE; - - cbreak(); noecho(); idlok(stdscr, 1); -#ifdef HAVE_CURSES_IDCOK - /*idcok(stdscr, 1); - disabled currently, causes redrawing problems with NetBSD */ -#endif - intrflush(stdscr, FALSE); nodelay(stdscr, TRUE); - - /* Disable INTR, QUIT, VDSUSP and SUSP keys */ - if (tcgetattr(0, &old_tio) == 0) { - memcpy(&tio, &old_tio, sizeof(tio)); - tio.c_cc[VINTR] = _POSIX_VDISABLE; - tio.c_cc[VQUIT] = _POSIX_VDISABLE; -#ifdef VDSUSP - tio.c_cc[VDSUSP] = _POSIX_VDISABLE; -#endif -#ifdef VSUSP - tio.c_cc[VSUSP] = _POSIX_VDISABLE; -#endif - tcsetattr(0, TCSADRAIN, &tio); - } - - if (has_colors()) - start_color(); - else if (term_use_colors) - term_use_colors = FALSE; - -#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS - /* this lets us to use the "default" background color for colors <= 7 so - background pixmaps etc. show up right */ - use_default_colors(); - - for (num = 1; num < COLOR_PAIRS; num++) - init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]); - - init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more - people want dark grey than white on white.. */ -#else - for (num = 1; num < COLOR_PAIRS; num++) - init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]); - init_pair(63, 0, 0); -#endif - - clear(); - return TRUE; -} - -static int term_init_int(void) -{ - int ret; - - ret = init_curses(); - if (!ret) return 0; - - curs_x = curs_y = 0; - freeze_refresh = 0; - - root_window = g_new0(TERM_WINDOW, 1); - root_window->win = stdscr; - - term_width = COLS; - term_height = LINES; - return ret; -} - -static void term_deinit_int(void) -{ - tcsetattr(0, TCSADRAIN, &old_tio); - - endwin(); - g_free_and_null(root_window); -} - -int term_init(void) -{ - if (!term_init_int()) - return FALSE; - - settings_add_int("lookandfeel", "default_color", 7); - term_common_init(); - return TRUE; -} - -void term_deinit(void) -{ - term_common_deinit(); - term_deinit_int(); -} - -/* Resize terminal - if width or height is negative, - the new size is unknown and should be figured out somehow */ -void term_resize(int width, int height) -{ -#ifdef HAVE_CURSES_RESIZETERM - if (width < 0 || height < 0) { -#endif - term_deinit_int(); - term_init_int(); -#ifdef HAVE_CURSES_RESIZETERM - } else if (term_width != width || term_height != height) { - term_width = width; - term_height = height; - resizeterm(term_height, term_width); - } -#endif -} - -void term_resize_final(int width, int height) -{ -#ifdef HAVE_CURSES_RESIZETERM - if (width < 0 || height < 0) - mainwindows_recreate(); -#else - mainwindows_recreate(); -#endif -} - -/* Returns TRUE if terminal has colors */ -int term_has_colors(void) -{ - return has_colors(); -} - -/* Force the colors on any way you can */ -void term_force_colors(int set) -{ - /* don't do anything with curses */ -} - -/* Clear screen */ -void term_clear(void) -{ - term_set_color(root_window, 0); - clear(); -} - -/* Beep */ -void term_beep(void) -{ - beep(); -} - -/* Create a new window in terminal */ -TERM_WINDOW *term_window_create(int x, int y, int width, int height) -{ - TERM_WINDOW *window; - - window = g_new0(TERM_WINDOW, 1); - window->x = x; window->y = y; - window->width = width; window->height = height; - window->win = newwin(height, width, y, x); - if (window->win == NULL) - g_error("newwin() failed: %d,%d %d,%d", x, y, width, height); - idlok(window->win, 1); - - return window; -} - -/* Destroy a terminal window */ -void term_window_destroy(TERM_WINDOW *window) -{ - delwin(window->win); - g_free(window); -} - -/* Move/resize a window */ -void term_window_move(TERM_WINDOW *window, int x, int y, - int width, int height) -{ - /* some checks to make sure the window is visible in screen, - otherwise curses could get nasty and not show our window anymore. */ - if (width < 1) width = 1; - if (height < 1) height = 1; - if (x+width > term_width) x = term_width-width; - if (y+height > term_height) y = term_height-height; - -#ifdef HAVE_CURSES_WRESIZE - if (window->width != width || window->height != height) - wresize(window->win, height, width); - if (window->x != x || window->y != y) - mvwin(window->win, y, x); -#else - if (window->width != width || window->height != height || - window->x != x || window->y != y) { - delwin(window->win); - window->win = newwin(height, width, y, x); - idlok(window->win, 1); - } -#endif - window->x = x; window->y = y; - window->width = width; window->height = height; -} - -/* Clear window */ -void term_window_clear(TERM_WINDOW *window) -{ - werase(window->win); -} - -/* Scroll window up/down */ -void term_window_scroll(TERM_WINDOW *window, int count) -{ - scrollok(window->win, TRUE); - wscrl(window->win, count); - scrollok(window->win, FALSE); -} - -static int get_attr(int color) -{ - int attr; - - if ((color & FG_MASK) >> 4) - color = (color & ~FG_MASK) | term_color256map[color & FG_MASK]; - if ((color & BG_MASK) >> (BG_SHIFT + 4)) - color = (color & ~BG_MASK) | (term_color256map[(color & BG_MASK) >> BG_SHIFT] << BG_SHIFT); - if (!term_use_colors) - attr = (color & (0x7 << BG_SHIFT)) ? A_REVERSE : 0; - else if ((color & ((0xf << BG_SHIFT) | 0xf)) == 8 || (color & (FG_MASK | BG_MASK | ATTR_RESETFG)) == 0) - attr = COLOR_PAIR(63); - else if ((color & ((0x7 << BG_SHIFT) | 0x7)) == 0) - attr = A_NORMAL; - else { - if (color & ATTR_RESETFG) { - color &= ~FG_MASK; - color |= settings_get_int("default_color"); - } - attr = COLOR_PAIR((color&0x7) | ((color&(0x7<<BG_SHIFT))>>BG_SHIFT<<3)); - } - - if ((color & 0x8) || (color & ATTR_BOLD)) attr |= A_BOLD; - if (color & ATTR_BLINK) attr |= A_BLINK; - - if (color & ATTR_UNDERLINE) attr |= A_UNDERLINE; - if (color & ATTR_REVERSE) attr |= A_REVERSE; -#ifdef A_ITALIC - if (color & ATTR_ITALIC) attr |= A_ITALIC; -#endif - return attr; -} - -/* Change active color */ -void term_set_color(TERM_WINDOW *window, int col) -{ - wattrset(window->win, get_attr(col)); - wbkgdset(window->win, ' ' | get_attr(col)); -} - -void term_move(TERM_WINDOW *window, int x, int y) -{ - wmove(window->win, y, x); -} - -void term_addch(TERM_WINDOW *window, char chr) -{ - waddch(window->win, chr); -} - -void term_add_unichar(TERM_WINDOW *window, unichar chr) -{ -#ifdef WIDEC_CURSES - cchar_t wch; - wchar_t temp[2]; - temp[0] = chr; - temp[1] = 0; - if (setcchar(&wch, temp, A_NORMAL, 0, NULL) == OK) - wadd_wch(window->win, &wch); - else -#endif - waddch(window->win, chr); -} - -void term_addstr(TERM_WINDOW *window, const char *str) -{ - waddstr(window->win, (const char *) str); -} - -void term_clrtoeol(TERM_WINDOW *window) -{ - wclrtoeol(window->win); -} - -void term_move_cursor(int x, int y) -{ - curs_x = x; - curs_y = y; -} - -void term_refresh_freeze(void) -{ - freeze_refresh++; -} - -void term_refresh_thaw(void) -{ - if (freeze_refresh > 0) { - freeze_refresh--; - if (freeze_refresh == 0) term_refresh(NULL); - } -} - -void term_refresh(TERM_WINDOW *window) -{ - if (window != NULL) - wnoutrefresh(window->win); - - if (freeze_refresh == 0) { - move(curs_y, curs_x); - wnoutrefresh(stdscr); - doupdate(); - } -} - -void term_stop(void) -{ - term_deinit_int(); - kill(getpid(), SIGTSTP); - term_init_int(); - irssi_redraw(); -} - -void term_set_input_type(int type) -{ -} - -void term_gets(GArray *buffer, int *line_count) -{ -#ifdef WIDEC_CURSES - wint_t key; -#else - int key; -#endif - - for (;;) { -#ifdef WIDEC_CURSES - if (get_wch(&key) == ERR) -#else - if ((key = getch()) == ERR) -#endif - break; -#ifdef KEY_RESIZE - if (key == KEY_RESIZE) - continue; -#endif - - g_array_append_val(buffer, key); - if (key == '\r' || key == '\n') - (*line_count)++; - } -} diff --git a/src/fe-text/term-dummy.c b/src/fe-text/term-dummy.c deleted file mode 100644 index 6a56af88..00000000 --- a/src/fe-text/term-dummy.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - term-dummy.c : irssi - - Copyright (C) 2001 Timo Sirainen - - 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 02110-1301 USA. -*/ - -#include "module.h" -#include "signals.h" - -#include "fe-windows.h" - -static int newline; - -static GIOChannel *stdin_channel; -static int readtag; -static GString *input; - -static void sig_gui_printtext(WINDOW_REC *window, void *fgcolor, - void *bgcolor, void *pflags, - char *str, void *level) -{ - if (newline) { - newline = FALSE; - printf("\r"); - } - - printf("%s", str); -} - -static void sig_gui_printtext_finished(WINDOW_REC *window) -{ - printf("\n"); - newline = TRUE; -} - -static void sig_window_created(WINDOW_REC *window) -{ - window->width = 80; - window->height = 25; -} - -static void readline(void) -{ - unsigned char buffer[128]; - char *p; - int ret, i; - - ret = read(0, buffer, sizeof(buffer)); - if (ret == 0 || (ret == -1 && errno != EINTR)) { - /* lost terminal */ - signal_emit("command quit", 1, "Lost terminal"); - return; - } - - for (i = 0; i < ret; i++) - g_string_append_c(input, buffer[i]); - - p = strchr(input->str, '\n'); - if (p != NULL) { - *p = '\0'; - signal_emit("send command", 3, input->str, - active_win->active_server, active_win->active); - *p = '\n'; - g_string_erase(input, 0, (int) (p-input->str)+1); - } -} - -void term_dummy_init(void) -{ - newline = TRUE; - input = g_string_new(NULL); - - signal_add("gui print text", (SIGNAL_FUNC) sig_gui_printtext); - signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished); - signal_add("window created", (SIGNAL_FUNC) sig_window_created); - - stdin_channel = g_io_channel_unix_new(0); - readtag = g_input_add_full(stdin_channel, - G_PRIORITY_HIGH, G_INPUT_READ, - (GInputFunction) readline, NULL); - g_io_channel_unref(stdin_channel); -} - -void term_dummy_deinit(void) -{ - signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_printtext); - signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished); - signal_remove("window created", (SIGNAL_FUNC) sig_window_created); - - g_source_remove(readtag); - g_string_free(input, TRUE); -} diff --git a/src/fe-text/terminfo-core.c b/src/fe-text/terminfo-core.c index 15b00081..9c9179a5 100644 --- a/src/fe-text/terminfo-core.c +++ b/src/fe-text/terminfo-core.c @@ -17,7 +17,6 @@ inline static int term_putchar(int c) char *tparm(); int tputs(); -#ifdef HAVE_TERMINFO int setupterm(); char *tigetstr(); int tigetnum(); @@ -25,15 +24,6 @@ int tigetflag(); #define term_getstr(x, buffer) tigetstr(x.ti_name) #define term_getnum(x) tigetnum(x.ti_name); #define term_getflag(x) tigetflag(x.ti_name); -#else -int tgetent(); -char *tgetstr(); -int tgetnum(); -int tgetflag(); -#define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer) -#define term_getnum(x) tgetnum(x.tc_name) -#define term_getflag(x) tgetflag(x.tc_name) -#endif #define CAP_TYPE_FLAG 0 #define CAP_TYPE_INT 1 @@ -415,9 +405,6 @@ static void term_fill_capabilities(TERM_REC *term) char *sval; void *ptr; -#ifndef HAVE_TERMINFO - char *tptr = term->buffer2; -#endif for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) { ptr = G_STRUCT_MEMBER_P(term, tcaps[i].offset); @@ -583,9 +570,7 @@ void terminfo_stop(TERM_REC *term) static int term_setup(TERM_REC *term) { GString *str; -#ifdef HAVE_TERMINFO int err; -#endif char *term_env; term_env = getenv("TERM"); @@ -594,18 +579,10 @@ static int term_setup(TERM_REC *term) return 0; } -#ifdef HAVE_TERMINFO if (setupterm(term_env, 1, &err) != 0) { fprintf(stderr, "setupterm() failed for TERM=%s: %d\n", term_env, err); return 0; } -#else - if (tgetent(term->buffer1, term_env) < 1) - { - fprintf(stderr, "Termcap not found for TERM=%s\n", term_env); - return 0; - } -#endif term_fill_capabilities(term); diff --git a/src/fe-text/tparm.c b/src/fe-text/tparm.c deleted file mode 100644 index 97c790da..00000000 --- a/src/fe-text/tparm.c +++ /dev/null @@ -1,740 +0,0 @@ -/* - * tparm.c - * - * By Ross Ridge - * Public Domain - * 92/02/01 07:30:36 - * - */ -#include <ctype.h> -#include <stdarg.h> -#include <stdio.h> -#include <string.h> - -#ifndef MAX_PUSHED -#define MAX_PUSHED 32 -#endif - -#define ARG 1 -#define NUM 2 - -#define INTEGER 1 -#define STRING 2 - -#define MAX_LINE 640 - -typedef void* anyptr; - -typedef struct stack_str { - int type; - int argnum; - int value; -} stack; - -static stack S[MAX_PUSHED]; -static stack vars['z'-'a'+1]; -static int pos = 0; - -static struct arg_str { - int type; - int integer; - char *string; -} arg_list[10]; - -static int argcnt; - -static va_list tparm_args; - -static int pusharg(int arg) -{ - if (pos == MAX_PUSHED) - return 1; - S[pos].type = ARG; - S[pos++].argnum = arg; - return 0; -} - -static int pushnum(int num) -{ - if (pos == MAX_PUSHED) - return 1; - S[pos].type = NUM; - S[pos++].value = num; - return 0; -} - -/* VARARGS2 */ -static int getarg(int argnum, int type, anyptr p) -{ - while (argcnt < argnum) { - arg_list[argcnt].type = INTEGER; - arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); - } - if (argcnt > argnum) { - if (arg_list[argnum].type != type) - return 1; - else if (type == STRING) - *(char **)p = arg_list[argnum].string; - else - *(int *)p = arg_list[argnum].integer; - } else { - arg_list[argcnt].type = type; - if (type == STRING) - *(char **)p = arg_list[argcnt++].string - = (char *) va_arg(tparm_args, char *); - else - *(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); - } - return 0; -} - - -static int popstring(char **str) -{ - if (pos-- == 0) - return 1; - if (S[pos].type != ARG) - return 1; - return(getarg(S[pos].argnum, STRING, (anyptr) str)); -} - -static int popnum(int *num) -{ - if (pos-- == 0) - return 1; - switch (S[pos].type) { - case ARG: - return (getarg(S[pos].argnum, INTEGER, (anyptr) num)); - case NUM: - *num = S[pos].value; - return 0; - } - return 1; -} - -static int cvtchar(const char *sp, char *c) -{ - switch(*sp) { - case '\\': - switch(*++sp) { - case '\'': - case '$': - case '\\': - case '%': - *c = *sp; - return 2; - case '\0': - *c = '\\'; - return 1; - case '0': - if (sp[1] == '0' && sp[2] == '0') { - *c = '\0'; - return 4; - } - *c = '\200'; /* '\0' ???? */ - return 2; - default: - *c = *sp; - return 2; - } - default: - *c = *sp; - return 1; - } -} - -static int termcap; - -/* sigh... this has got to be the ugliest code I've ever written. - Trying to handle everything has its cost, I guess. - - It actually isn't to hard to figure out if a given % code is supposed - to be interpeted with its termcap or terminfo meaning since almost - all terminfo codes are invalid unless something has been pushed on - the stack and termcap strings will never push things on the stack - (%p isn't used by termcap). So where we have a choice we make the - decision by whether or not somthing has been pushed on the stack. - The static variable termcap keeps track of this; it starts out set - to 1 and is incremented as each argument processed by a termcap % code, - however if something is pushed on the stack it's set to 0 and the - rest of the % codes are interpeted as terminfo % codes. Another way - of putting it is that if termcap equals one we haven't decided either - way yet, if it equals zero we're looking for terminfo codes, and if - its greater than 1 we're looking for termcap codes. - - Terminfo % codes: - - %% output a '%' - %[[:][-+# ][width][.precision]][doxXs] - output pop according to the printf format - %c output pop as a char - %'c' push character constant c. - %{n} push decimal constant n. - %p[1-9] push parameter [1-9] - %g[a-z] push variable [a-z] - %P[a-z] put pop in variable [a-z] - %l push the length of pop (a string) - %+ add pop to pop and push the result - %- subtract pop from pop and push the result - %* multiply pop and pop and push the result - %& bitwise and pop and pop and push the result - %| bitwise or pop and pop and push the result - %^ bitwise xor pop and pop and push the result - %~ push the bitwise not of pop - %= compare if pop and pop are equal and push the result - %> compare if pop is less than pop and push the result - %< compare if pop is greater than pop and push the result - %A logical and pop and pop and push the result - %O logical or pop and pop and push the result - %! push the logical not of pop - %? condition %t if_true [%e if_false] %; - if condition evaulates as true then evaluate if_true, - else evaluate if_false. elseif's can be done: -%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %; - %i add one to parameters 1 and 2. (ANSI) - - Termcap Codes: - - %% output a % - %. output parameter as a character - %d output parameter as a decimal number - %2 output parameter in printf format %02d - %3 output parameter in printf format %03d - %+x add the character x to parameter and output it as a character -(UW) %-x subtract parameter FROM the character x and output it as a char -(UW) %ax add the character x to parameter -(GNU) %a[+*-/=][cp]x - GNU arithmetic. -(UW) %sx subtract parameter FROM the character x - %>xy if parameter > character x then add character y to parameter - %B convert to BCD (parameter = (parameter/10)*16 + parameter%16) - %D Delta Data encode (parameter = parameter - 2*(parameter%16)) - %i increment the first two parameters by one - %n xor the first two parameters by 0140 -(GNU) %m xor the first two parameters by 0177 - %r swap the first two parameters -(GNU) %b backup to previous parameter -(GNU) %f skip this parameter - - Note the two definitions of %a, the GNU definition is used if the characters - after the 'a' are valid, otherwise the UW definition is used. - - (GNU) used by GNU Emacs termcap libraries - (UW) used by the University of Waterloo (MFCF) termcap libraries - -*/ - -char *tparm(const char *str, ...) { - static char OOPS[] = "OOPS"; - static char buf[MAX_LINE]; - register const char *sp; - register char *dp; - register char *fmt; - char conv_char; - char scan_for; - int scan_depth = 0, if_depth; - static int i, j; - static char *s, c; - char fmt_buf[MAX_LINE]; - char sbuf[MAX_LINE]; - - va_start(tparm_args, str); - - sp = str; - dp = buf; - scan_for = 0; - if_depth = 0; - argcnt = 0; - pos = 0; - termcap = 1; - while (*sp != '\0') { - switch(*sp) { - case '\\': - if (scan_for) { - if (*++sp != '\0') - sp++; - break; - } - *dp++ = *sp++; - if (*sp != '\0') - *dp++ = *sp++; - break; - case '%': - sp++; - if (scan_for) { - if (*sp == scan_for && if_depth == scan_depth) { - if (scan_for == ';') - if_depth--; - scan_for = 0; - } else if (*sp == '?') - if_depth++; - else if (*sp == ';') { - if (if_depth == 0) - return OOPS; - else - if_depth--; - } - sp++; - break; - } - fmt = NULL; - switch(*sp) { - case '%': - *dp++ = *sp++; - break; - case '+': - if (!termcap) { - if (popnum(&j) || popnum(&i)) - return OOPS; - i += j; - if (pushnum(i)) - return OOPS; - sp++; - break; - } - ;/* FALLTHROUGH */ - case 'C': - if (*sp == 'C') { - if (getarg(termcap - 1, INTEGER, &i)) - return OOPS; - if (i >= 96) { - i /= 96; - if (i == '$') - *dp++ = '\\'; - *dp++ = i; - } - } - fmt = "%c"; - /* FALLTHROUGH */ - case 'a': - if (!termcap) - return OOPS; - if (getarg(termcap - 1, INTEGER, (anyptr) &i)) - return OOPS; - if (*++sp == '\0') - return OOPS; - if ((sp[1] == 'p' || sp[1] == 'c') - && sp[2] != '\0' && fmt == NULL) { - /* GNU aritmitic parameter, what they - really need is terminfo. */ - int val, lc; - if (sp[1] == 'p' - && getarg(termcap - 1 + sp[2] - '@', - INTEGER, (anyptr) &val)) - return OOPS; - if (sp[1] == 'c') { - lc = cvtchar(sp + 2, &c) + 2; - /* Mask out 8th bit so \200 can be - used for \0 as per GNU doc's */ - val = c & 0177; - } else - lc = 2; - switch(sp[0]) { - case '=': - break; - case '+': - val = i + val; - break; - case '-': - val = i - val; - break; - case '*': - val = i * val; - break; - case '/': - val = i / val; - break; - default: - /* Not really GNU's %a after all... */ - lc = cvtchar(sp, &c); - val = c + i; - break; - } - arg_list[termcap - 1].integer = val; - sp += lc; - break; - } - sp += cvtchar(sp, &c); - arg_list[termcap - 1].integer = c + i; - if (fmt == NULL) - break; - sp--; - /* FALLTHROUGH */ - case '-': - if (!termcap) { - if (popnum(&j) || popnum(&i)) - return OOPS; - i -= j; - if (pushnum(i)) - return OOPS; - sp++; - break; - } - fmt = "%c"; - /* FALLTHROUGH */ - case 's': - if (termcap && (fmt == NULL || *sp == '-')) { - if (getarg(termcap - 1, INTEGER, &i)) - return OOPS; - if (*++sp == '\0') - return OOPS; - sp += cvtchar(sp, &c); - arg_list[termcap - 1].integer = c - i; - if (fmt == NULL) - break; - sp--; - } - if (!termcap) - return OOPS; - ;/* FALLTHROUGH */ - case '.': - if (termcap && fmt == NULL) - fmt = "%c"; - ;/* FALLTHROUGH */ - case 'd': - if (termcap && fmt == NULL) - fmt = "%d"; - ;/* FALLTHROUGH */ - case '2': - if (termcap && fmt == NULL) - fmt = "%02d"; - ;/* FALLTHROUGH */ - case '3': - if (termcap && fmt == NULL) - fmt = "%03d"; - ;/* FALLTHROUGH */ - case ':': case ' ': case '#': case 'u': - case 'x': case 'X': case 'o': case 'c': - case '0': case '1': case '4': case '5': - case '6': case '7': case '8': case '9': - if (fmt == NULL) { - if (termcap) - return OOPS; - if (*sp == ':') - sp++; - fmt = fmt_buf; - *fmt++ = '%'; - while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') { - if (*sp == '\0') - return OOPS; - *fmt++ = *sp++; - } - *fmt++ = *sp; - *fmt = '\0'; - fmt = fmt_buf; - } - conv_char = fmt[strlen(fmt) - 1]; - if (conv_char == 's') { - if (popstring(&s)) - return OOPS; - sprintf(sbuf, fmt, s); - } else { - if (termcap) { - if (getarg(termcap++ - 1, - INTEGER, &i)) - return OOPS; - } else - if (popnum(&i)) - return OOPS; - if (i == 0 && conv_char == 'c') - *sbuf = 0; - else - sprintf(sbuf, fmt, i); - } - sp++; - fmt = sbuf; - while(*fmt != '\0') { - if (*fmt == '$') - *dp++ = '\\'; - *dp++ = *fmt++; - } - break; - case 'r': - if (!termcap || getarg(1, INTEGER, &i)) - return OOPS; - arg_list[1].integer = arg_list[0].integer; - arg_list[0].integer = i; - sp++; - break; - case 'i': - if (getarg(1, INTEGER, &i) - || arg_list[0].type != INTEGER) - return OOPS; - arg_list[1].integer++; - arg_list[0].integer++; - sp++; - break; - case 'n': - if (!termcap || getarg(1, INTEGER, &i)) - return OOPS; - arg_list[0].integer ^= 0140; - arg_list[1].integer ^= 0140; - sp++; - break; - case '>': - if (!termcap) { - if (popnum(&j) || popnum(&i)) - return OOPS; - i = (i > j); - if (pushnum(i)) - return OOPS; - sp++; - break; - } - if (getarg(termcap-1, INTEGER, &i)) - return OOPS; - sp += cvtchar(sp, &c); - if (i > c) { - sp += cvtchar(sp, &c); - arg_list[termcap-1].integer += c; - } else - sp += cvtchar(sp, &c); - sp++; - break; - case 'B': - if (!termcap || getarg(termcap-1, INTEGER, &i)) - return OOPS; - arg_list[termcap-1].integer = 16*(i/10)+i%10; - sp++; - break; - case 'D': - if (!termcap || getarg(termcap-1, INTEGER, &i)) - return OOPS; - arg_list[termcap-1].integer = i - 2 * (i % 16); - sp++; - break; - case 'p': - if (termcap > 1) - return OOPS; - if (*++sp == '\0') - return OOPS; - if (*sp == '0') - i = 9; - else - i = *sp - '1'; - if (i < 0 || i > 9) - return OOPS; - if (pusharg(i)) - return OOPS; - termcap = 0; - sp++; - break; - case 'P': - if (termcap || *++sp == '\0') - return OOPS; - i = *sp++ - 'a'; - if (i < 0 || i > 25) - return OOPS; - if (pos-- == 0) - return OOPS; - switch(vars[i].type = S[pos].type) { - case ARG: - vars[i].argnum = S[pos].argnum; - break; - case NUM: - vars[i].value = S[pos].value; - break; - } - break; - case 'g': - if (termcap || *++sp == '\0') - return OOPS; - i = *sp++ - 'a'; - if (i < 0 || i > 25) - return OOPS; - switch(vars[i].type) { - case ARG: - if (pusharg(vars[i].argnum)) - return OOPS; - break; - case NUM: - if (pushnum(vars[i].value)) - return OOPS; - break; - } - break; - case '\'': - if (termcap > 1) - return OOPS; - if (*++sp == '\0') - return OOPS; - sp += cvtchar(sp, &c); - if (pushnum(c) || *sp++ != '\'') - return OOPS; - termcap = 0; - break; - case '{': - if (termcap > 1) - return OOPS; - i = 0; - sp++; - while(isdigit((int) (unsigned char) *sp)) - i = 10 * i + *sp++ - '0'; - if (*sp++ != '}' || pushnum(i)) - return OOPS; - termcap = 0; - break; - case 'l': - if (termcap || popstring(&s)) - return OOPS; - i = strlen(s); - if (pushnum(i)) - return OOPS; - sp++; - break; - case '*': - if (termcap || popnum(&j) || popnum(&i)) - return OOPS; - i *= j; - if (pushnum(i)) - return OOPS; - sp++; - break; - case '/': - if (termcap || popnum(&j) || popnum(&i)) - return OOPS; - i /= j; - if (pushnum(i)) - return OOPS; - sp++; - break; - case 'm': - if (termcap) { - if (getarg(1, INTEGER, &i)) - return OOPS; - arg_list[0].integer ^= 0177; - arg_list[1].integer ^= 0177; - sp++; - break; - } - if (popnum(&j) || popnum(&i)) - return OOPS; - i %= j; - if (pushnum(i)) - return OOPS; - sp++; - break; - case '&': - if (popnum(&j) || popnum(&i)) - return OOPS; - i &= j; - if (pushnum(i)) - return OOPS; - sp++; - break; - case '|': - if (popnum(&j) || popnum(&i)) - return OOPS; - i |= j; - if (pushnum(i)) - return OOPS; - sp++; - break; - case '^': - if (popnum(&j) || popnum(&i)) - return OOPS; - i ^= j; - if (pushnum(i)) - return OOPS; - sp++; - break; - case '=': - if (popnum(&j) || popnum(&i)) - return OOPS; - i = (i == j); - if (pushnum(i)) - return OOPS; - sp++; - break; - case '<': - if (popnum(&j) || popnum(&i)) - return OOPS; - i = (i < j); - if (pushnum(i)) - return OOPS; - sp++; - break; - case 'A': - if (popnum(&j) || popnum(&i)) - return OOPS; - i = (i && j); - if (pushnum(i)) - return OOPS; - sp++; - break; - case 'O': - if (popnum(&j) || popnum(&i)) - return OOPS; - i = (i || j); - if (pushnum(i)) - return OOPS; - sp++; - break; - case '!': - if (popnum(&i)) - return OOPS; - i = !i; - if (pushnum(i)) - return OOPS; - sp++; - break; - case '~': - if (popnum(&i)) - return OOPS; - i = ~i; - if (pushnum(i)) - return OOPS; - sp++; - break; - case '?': - if (termcap > 1) - return OOPS; - termcap = 0; - if_depth++; - sp++; - break; - case 't': - if (popnum(&i) || if_depth == 0) - return OOPS; - if (!i) { - scan_for = 'e'; - scan_depth = if_depth; - } - sp++; - break; - case 'e': - if (if_depth == 0) - return OOPS; - scan_for = ';'; - scan_depth = if_depth; - sp++; - break; - case ';': - if (if_depth-- == 0) - return OOPS; - sp++; - break; - case 'b': - if (--termcap < 1) - return OOPS; - sp++; - break; - case 'f': - if (!termcap++) - return OOPS; - sp++; - break; - } - break; - default: - if (scan_for) - sp++; - else - *dp++ = *sp++; - break; - } - } - va_end(tparm_args); - *dp = '\0'; - return buf; -} diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index feb7cee8..79aeb227 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -89,8 +89,10 @@ static int ischannel_func(SERVER_REC *server, const char *data) chantypes = "#&!+"; /* normal, local, secure, modeless */ statusmsg = g_hash_table_lookup(irc_server->isupport, "statusmsg"); - if (statusmsg != NULL) - data += strspn(data, statusmsg); + if (statusmsg == NULL) + statusmsg = "@"; + + data += strspn(data, statusmsg); /* strchr(3) considers the trailing NUL as part of the string, make sure * we didn't advance too much. */ @@ -444,6 +446,8 @@ static void sig_disconnected(IRC_SERVER_REC *server) gslist_free_full(server->cap_queue, (GDestroyNotify) g_free); server->cap_queue = NULL; + g_free_and_null(server->sasl_buffer); + /* these are dynamically allocated only if isupport was sent */ g_hash_table_foreach(server->isupport, (GHFunc) isupport_destroy_hash, server); diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h index 41c4b9c2..bb100f86 100644 --- a/src/irc/core/irc-servers.h +++ b/src/irc/core/irc-servers.h @@ -67,6 +67,7 @@ struct _IRC_SERVER_REC { unsigned int nick_collision:1; /* We're just now being killed because of nick collision */ unsigned int motd_got:1; /* We've received MOTD */ unsigned int isupport_sent:1; /* Server has sent us an isupport reply */ + unsigned int cap_complete:1; /* We've done the initial CAP negotiation */ int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */ int max_modes_in_cmd; /* max. number of mode changes in one /MODE command */ @@ -76,9 +77,9 @@ struct _IRC_SERVER_REC { GSList *cap_supported; /* A list of caps supported by the server */ GSList *cap_active; /* A list of caps active for this session */ GSList *cap_queue; /* A list of caps to request on connection */ - int cap_complete:1; /* We've done the initial CAP negotiation */ - guint sasl_timeout; /* Holds the source id of the running timeout */ + GString *sasl_buffer; /* Buffer used to reassemble a fragmented SASL payload */ + guint sasl_timeout; /* Holds the source id of the running timeout */ /* Command sending queue */ int cmdcount; /* number of commands in `cmdqueue'. Can be more than diff --git a/src/irc/core/sasl.c b/src/irc/core/sasl.c index f080ae59..a1c16cdd 100644 --- a/src/irc/core/sasl.c +++ b/src/irc/core/sasl.c @@ -26,6 +26,19 @@ #include "irc-servers.h" #include "sasl.h" +/* + * Based on IRCv3 SASL Extension Specification: + * http://ircv3.net/specs/extensions/sasl-3.1.html + */ +#define AUTHENTICATE_CHUNK_SIZE 400 // bytes + +/* + * Maximum size to allow the buffer to grow to before the next fragment comes in. Note that + * due to the way fragmentation works, the maximum message size will actually be: + * floor(AUTHENTICATE_MAX_SIZE / AUTHENTICATE_CHUNK_SIZE) + AUTHENTICATE_CHUNK_SIZE - 1 + */ +#define AUTHENTICATE_MAX_SIZE 8192 // bytes + #define SASL_TIMEOUT (20 * 1000) // ms static gboolean sasl_timeout(IRC_SERVER_REC *server) @@ -105,19 +118,106 @@ static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *f cap_finish_negotiation(server); } -static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from) +/* + * Responsible for reassembling incoming SASL requests. SASL requests must be split + * into 400 byte requests to stay below the IRC command length limit of 512 bytes. + * The spec says that if there is 400 bytes, then there is expected to be a + * continuation in the next chunk. If a message is exactly a multiple of 400 bytes, + * there must be a blank message of "AUTHENTICATE +" to indicate the end. + * + * This function returns the fully reassembled and decoded AUTHENTICATION message if + * completed or NULL if there are more messages expected. + */ +static gboolean sasl_reassemble_incoming(IRC_SERVER_REC *server, const char *fragment, GString **decoded) { - IRC_SERVER_CONNECT_REC *conn; - GString *req; - char *enc_req; + GString *enc_req; + gsize fragment_len; + + fragment_len = strlen(fragment); + + /* Check if there is an existing fragment to prepend. */ + if (server->sasl_buffer != NULL) { + if (g_strcmp0("+", fragment) == 0) { + enc_req = server->sasl_buffer; + } else { + enc_req = g_string_append_len(server->sasl_buffer, fragment, fragment_len); + } + server->sasl_buffer = NULL; + } else { + enc_req = g_string_new_len(fragment, fragment_len); + } - conn = server->connrec; + /* + * Fail authentication with this server. They have sent too much data. + */ + if (enc_req->len > AUTHENTICATE_MAX_SIZE) { + return FALSE; + } - /* Stop the timer */ - if (server->sasl_timeout != 0) { - g_source_remove(server->sasl_timeout); - server->sasl_timeout = 0; + /* + * If the the request is exactly the chunk size, this is a fragment + * and more data is expected. + */ + if (fragment_len == AUTHENTICATE_CHUNK_SIZE) { + server->sasl_buffer = enc_req; + return TRUE; + } + + if (enc_req->len == 1 && *enc_req->str == '+') { + *decoded = g_string_new_len("", 0); + } else { + gsize dec_len; + gchar *tmp; + + tmp = (gchar *) g_base64_decode(enc_req->str, &dec_len); + *decoded = g_string_new_len(tmp, dec_len); + } + + g_string_free(enc_req, TRUE); + return TRUE; +} + +/* + * Splits the response into appropriately sized chunks for the AUTHENTICATION + * command to be sent to the IRC server. If |response| is NULL, then the empty + * response is sent to the server. + */ +void sasl_send_response(IRC_SERVER_REC *server, GString *response) +{ + char *enc; + size_t offset, enc_len, chunk_len; + + if (response == NULL) { + irc_send_cmdv(server, "AUTHENTICATE +"); + return; + } + + enc = g_base64_encode((guchar *) response->str, response->len); + enc_len = strlen(enc); + + for (offset = 0; offset < enc_len; offset += AUTHENTICATE_CHUNK_SIZE) { + chunk_len = enc_len - offset; + if (chunk_len > AUTHENTICATE_CHUNK_SIZE) + chunk_len = AUTHENTICATE_CHUNK_SIZE; + + irc_send_cmdv(server, "AUTHENTICATE %.*s", (int) chunk_len, enc + offset); + } + + if (offset == enc_len) { + irc_send_cmdv(server, "AUTHENTICATE +"); } + g_free(enc); +} + +/* + * Called when the incoming SASL request is completely received. + */ +static void sasl_step_complete(IRC_SERVER_REC *server, GString *data) +{ + IRC_SERVER_CONNECT_REC *conn; + GString *resp; + + conn = server->connrec; switch (conn->sasl_mechanism) { case SASL_MECHANISM_PLAIN: @@ -125,29 +225,57 @@ static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from * The PLAIN mechanism expects a NULL-separated string composed by the authorization identity, the * authentication identity and the password. * The authorization identity field is explicitly set to the user provided username. - * The whole request is then encoded in base64. */ - - req = g_string_new(NULL); + */ - g_string_append(req, conn->sasl_username); - g_string_append_c(req, '\0'); - g_string_append(req, conn->sasl_username); - g_string_append_c(req, '\0'); - g_string_append(req, conn->sasl_password); + resp = g_string_new(NULL); - enc_req = g_base64_encode((const guchar *)req->str, req->len); + g_string_append(resp, conn->sasl_username); + g_string_append_c(resp, '\0'); + g_string_append(resp, conn->sasl_username); + g_string_append_c(resp, '\0'); + g_string_append(resp, conn->sasl_password); - irc_send_cmdv(server, "AUTHENTICATE %s", enc_req); + sasl_send_response(server, resp); + g_string_free(resp, TRUE); - g_free(enc_req); - g_string_free(req, TRUE); break; case SASL_MECHANISM_EXTERNAL: /* Empty response */ - irc_send_cmdv(server, "AUTHENTICATE +"); + sasl_send_response(server, NULL); break; } +} + +static void sasl_step_fail(IRC_SERVER_REC *server) +{ + irc_send_cmd_now(server, "AUTHENTICATE *"); + cap_finish_negotiation(server); + + server->sasl_timeout = 0; + + signal_emit("server sasl failure", 2, server, "The server sent an invalid payload"); +} + +static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from) +{ + GString *req = NULL; + + /* Stop the timer */ + if (server->sasl_timeout != 0) { + g_source_remove(server->sasl_timeout); + server->sasl_timeout = 0; + } + + if (!sasl_reassemble_incoming(server, data, &req)) { + sasl_step_fail(server); + return; + } + + if (req != NULL) { + sasl_step_complete(server, req); + g_string_free(req, TRUE); + } /* We expect a response within a reasonable time */ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server); diff --git a/src/perl/Makefile.am b/src/perl/Makefile.am index b17fd664..427c5492 100644 --- a/src/perl/Makefile.am +++ b/src/perl/Makefile.am @@ -127,7 +127,7 @@ am_v_pl__show_gen = $(am__v_pl__show_gen_$(V)) am_v_pl__hide_gen = $(am__v_pl__hide_gen_$(V)) am__v_pl__show_gen_ = $(am__v_pl__show_gen_$(AM_DEFAULT_VERBOSITY)) am__v_pl__hide_gen_ = $(am__v_pl__hide_gen_$(AM_DEFAULT_VERBOSITY)) -am__v_pl__show_gen_0 = echo " GEN " $$dir ; +am__v_pl__show_gen_0 = echo " GEN " $$dir ; am__v_pl__hide_gen_0 = > /dev/null am__v_pl__show_gen_1 = am__v_pl__hide_gen_1 = diff --git a/src/perl/Makefile_silent.pm b/src/perl/Makefile_silent.pm index 372e4046..b5d71d66 100644 --- a/src/perl/Makefile_silent.pm +++ b/src/perl/Makefile_silent.pm @@ -3,7 +3,7 @@ my $verb = $AM_DEFAULT_VERBOSITY; { package MY; sub _center { my $z = shift; - length $z == 2 ? " $z " : length $z == 4 ? " $z " : " $z " + (length $z == 2 ? " $z " : length $z == 4 ? " $z " : " $z ").' ' } sub _silent_cmd { my $z = shift; diff --git a/src/perl/common/Expando.xs b/src/perl/common/Expando.xs index e8e8f751..26800b05 100644 --- a/src/perl/common/Expando.xs +++ b/src/perl/common/Expando.xs @@ -74,15 +74,20 @@ static char *perl_expando_event(PerlExpando *rec, SERVER_REC *server, ret = NULL; if (SvTRUE(ERRSV)) { + PERL_SCRIPT_REC *script = rec->script; + (void) POPs; /* call putback before emitting script error signal as that * could manipulate the perl stack. */ PUTBACK; /* make sure we don't get back here */ - if (rec->script != NULL) - script_unregister_expandos(rec->script); + if (script != NULL) + script_unregister_expandos(script); + /* rec has been freed now */ - signal_emit("script error", 2, rec->script, SvPV_nolen(ERRSV)); + char *error = g_strdup(SvPV_nolen(ERRSV)); + signal_emit("script error", 2, script, error); + g_free(error); } else if (retcount > 0) { ret = g_strdup(POPp); *free_ret = TRUE; diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs index 3f8ccc2e..8b3b0c45 100644 --- a/src/perl/irc/Irc.xs +++ b/src/perl/irc/Irc.xs @@ -11,12 +11,15 @@ static void perl_irc_connect_fill_hash(HV *hv, IRC_SERVER_CONNECT_REC *conn) static void perl_irc_server_fill_hash(HV *hv, IRC_SERVER_REC *server) { - perl_irc_connect_fill_hash(hv, server->connrec); - perl_server_fill_hash(hv, (SERVER_REC *) server); + AV *av; + GSList *tmp; + + perl_irc_connect_fill_hash(hv, server->connrec); + perl_server_fill_hash(hv, (SERVER_REC *) server); - (void) hv_store(hv, "real_address", 12, new_pv(server->real_address), 0); - (void) hv_store(hv, "usermode", 8, new_pv(server->usermode), 0); - (void) hv_store(hv, "userhost", 8, new_pv(server->userhost), 0); + (void) hv_store(hv, "real_address", 12, new_pv(server->real_address), 0); + (void) hv_store(hv, "usermode", 8, new_pv(server->usermode), 0); + (void) hv_store(hv, "userhost", 8, new_pv(server->userhost), 0); (void) hv_store(hv, "max_cmds_at_once", 16, newSViv(server->max_cmds_at_once), 0); (void) hv_store(hv, "cmd_queue_speed", 15, newSViv(server->cmd_queue_speed), 0); @@ -27,6 +30,18 @@ static void perl_irc_server_fill_hash(HV *hv, IRC_SERVER_REC *server) (void) hv_store(hv, "max_modes_in_cmd", 16, newSViv(server->max_modes_in_cmd), 0); (void) hv_store(hv, "max_whois_in_cmd", 16, newSViv(server->max_whois_in_cmd), 0); (void) hv_store(hv, "isupport_sent", 13, newSViv(server->isupport_sent), 0); + + (void) hv_store(hv, "cap_complete", 12, newSViv(server->cap_complete), 0); + + av = newAV(); + for (tmp = server->cap_supported; tmp != NULL; tmp = tmp->next) + av_push(av, new_pv(tmp->data)); + (void) hv_store(hv, "cap_supported", 13, newRV_noinc((SV*)av), 0); + + av = newAV(); + for (tmp = server->cap_active; tmp != NULL; tmp = tmp->next) + av_push(av, new_pv(tmp->data)); + (void) hv_store(hv, "cap_active", 10, newRV_noinc((SV*)av), 0); } static void perl_ban_fill_hash(HV *hv, BAN_REC *ban) diff --git a/src/perl/irc/Server.xs b/src/perl/irc/Server.xs index 0e9ec672..33417bf5 100644 --- a/src/perl/irc/Server.xs +++ b/src/perl/irc/Server.xs @@ -149,3 +149,12 @@ CODE: OUTPUT: RETVAL +int +irc_server_cap_toggle(server, cap, enable) + Irssi::Irc::Server server + char *cap + int enable +CODE: + RETVAL = cap_toggle(server, cap, enable); +OUTPUT: + RETVAL diff --git a/src/perl/irc/module.h b/src/perl/irc/module.h index 91c19c7a..a2454545 100644 --- a/src/perl/irc/module.h +++ b/src/perl/irc/module.h @@ -6,6 +6,7 @@ #include "irc-queries.h" #include "irc-nicklist.h" #include "irc-masks.h" +#include "irc-cap.h" #include "bans.h" #include "modes.h" diff --git a/src/perl/perl-core.c b/src/perl/perl-core.c index cb690906..2c61df70 100644 --- a/src/perl/perl-core.c +++ b/src/perl/perl-core.c @@ -393,7 +393,7 @@ int perl_get_api_version(void) return IRSSI_PERL_API_VERSION; } -static void perl_scripts_autorun(void) +void perl_scripts_autorun(void) { DIR *dirp; struct dirent *dp; diff --git a/src/perl/perl-core.h b/src/perl/perl-core.h index b451cc5c..7390a6fd 100644 --- a/src/perl/perl-core.h +++ b/src/perl/perl-core.h @@ -16,6 +16,8 @@ extern GSList *perl_scripts; void perl_scripts_init(void); /* Destroy all perl scripts and deinitialize perl interpreter */ void perl_scripts_deinit(void); +/* Load all the scripts in the autorun/ folder */ +void perl_scripts_autorun(void); /* Load a perl script, path must be a full path. */ PERL_SCRIPT_REC *perl_script_load_file(const char *path); diff --git a/src/perl/perl-fe.c b/src/perl/perl-fe.c index 04305b63..396c80b7 100644 --- a/src/perl/perl-fe.c +++ b/src/perl/perl-fe.c @@ -119,8 +119,20 @@ static void cmd_script_unload(const char *data) static void cmd_script_reset(const char *data) { + void *free_arg; + GHashTable *optlist; + + if (!cmd_get_params(data, &free_arg, 0 | PARAM_FLAG_OPTIONS, + "script reset", &optlist)) + return; + perl_scripts_deinit(); perl_scripts_init(); + + if (g_hash_table_lookup(optlist, "autorun") != NULL) + perl_scripts_autorun(); + + cmd_params_free(free_arg); } static void cmd_script_list(void) @@ -251,6 +263,7 @@ void fe_perl_init(void) command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list); command_bind("load", NULL, (SIGNAL_FUNC) cmd_load); command_set_options("script exec", "permanent"); + command_set_options("script reset", "autorun"); signal_add("script error", (SIGNAL_FUNC) sig_script_error); signal_add("complete command script load", (SIGNAL_FUNC) sig_complete_load); diff --git a/src/perl/perl-signals.c b/src/perl/perl-signals.c index 007f7ad5..8f993660 100644 --- a/src/perl/perl-signals.c +++ b/src/perl/perl-signals.c @@ -433,8 +433,9 @@ static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec) } #define sv_func_cmp(f1, f2) \ - (f1 == f2 || (SvPOK(f1) && SvPOK(f2) && \ - g_strcmp0(SvPV_nolen(f1), SvPV_nolen(f2)) == 0)) + ((SvROK(f1) && SvROK(f2) && SvRV(f1) == SvRV(f2)) || \ + (SvPOK(f1) && SvPOK(f2) && \ + g_strcmp0(SvPV_nolen(f1), SvPV_nolen(f2)) == 0)) static void perl_signal_remove_list(GSList **list, SV *func) { diff --git a/src/perl/textui/Statusbar.xs b/src/perl/textui/Statusbar.xs index a449e11d..8b0e5f65 100644 --- a/src/perl/textui/Statusbar.xs +++ b/src/perl/textui/Statusbar.xs @@ -77,7 +77,10 @@ static void perl_statusbar_event(char *function, SBAR_ITEM_REC *item, /* make sure we don't get back here */ script_unregister_statusbars(script); } - signal_emit("script error", 2, script, SvPV_nolen(ERRSV)); + + char *error = g_strdup(SvPV_nolen(ERRSV)); + signal_emit("script error", 2, script, error); + g_free(error); } else { /* min_size and max_size can be changed, move them to SBAR_ITEM_REC */ hv = hvref(item_sv); |