summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xautogen.sh8
-rw-r--r--src/common.h2
-rw-r--r--src/core/misc.c13
-rw-r--r--src/core/misc.h3
-rw-r--r--src/fe-common/core/themes.c59
-rw-r--r--src/fe-common/irc/fe-netjoin.c13
-rw-r--r--src/fe-common/irc/fe-netsplit.c12
-rw-r--r--src/fe-text/gui-entry.c596
-rw-r--r--src/fe-text/gui-entry.h11
-rw-r--r--src/fe-text/irssi.c2
-rw-r--r--src/fe-text/mainwindows.c91
-rw-r--r--src/irc/core/irc-cap.c176
-rw-r--r--src/irc/core/irc-servers.c6
-rw-r--r--src/irc/core/irc-servers.h3
-rw-r--r--src/perl/irc/Irc.xs17
-rw-r--r--src/perl/textui/TextUI.xs68
16 files changed, 905 insertions, 175 deletions
diff --git a/autogen.sh b/autogen.sh
index 9feb0872..17f60f89 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -57,3 +57,11 @@ if test x$NOCONFIGURE = x; then
else
echo Skipping configure process.
fi
+
+if grep -q '==\|\[\[' "$srcdir"/build-aux/test-driver; then
+ echo
+ echo "************************************************************************"
+ echo "**Warning**: your build is not portable, please do not make dist"
+ echo " see https://bugzilla.opensuse.org/show_bug.cgi?id=1076146"
+ echo "************************************************************************"
+fi
diff --git a/src/common.h b/src/common.h
index ba5557e6..746aad4e 100644
--- a/src/common.h
+++ b/src/common.h
@@ -6,7 +6,7 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
-#define IRSSI_ABI_VERSION 13
+#define IRSSI_ABI_VERSION 15
#define DEFAULT_SERVER_ADD_PORT 6667
#define DEFAULT_SERVER_ADD_TLS_PORT 6697
diff --git a/src/core/misc.c b/src/core/misc.c
index 4e9f4bbe..27741220 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -218,6 +218,19 @@ GSList *gslist_remove_string (GSList *list, const char *str)
return list;
}
+GSList *gslist_delete_string (GSList *list, const char *str, GDestroyNotify free_func)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(list, str, (GCompareFunc) g_strcmp0);
+ if (l != NULL) {
+ free_func(l->data);
+ return g_slist_delete_link(list, l);
+ }
+
+ return list;
+}
+
/* `list' contains pointer to structure with a char* to string. */
char *gslistptr_to_string(GSList *list, int offset, const char *delimiter)
{
diff --git a/src/core/misc.h b/src/core/misc.h
index 375744db..a46a1432 100644
--- a/src/core/misc.h
+++ b/src/core/misc.h
@@ -21,7 +21,8 @@ GSList *gslist_find_string(GSList *list, const char *key);
GSList *gslist_find_icase_string(GSList *list, const char *key);
GList *glist_find_string(GList *list, const char *key);
GList *glist_find_icase_string(GList *list, const char *key);
-GSList *gslist_remove_string (GSList *list, const char *str);
+GSList *gslist_remove_string (GSList *list, const char *str) G_GNUC_DEPRECATED;
+GSList *gslist_delete_string (GSList *list, const char *str, GDestroyNotify free_func);
void gslist_free_full (GSList *list, GDestroyNotify free_func);
diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c
index 6692985b..5a817daa 100644
--- a/src/fe-common/core/themes.c
+++ b/src/fe-common/core/themes.c
@@ -382,7 +382,8 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
} else {
theme_format_append_next(theme, str, format,
reset, reset,
- &dummy, &dummy, 0);
+ &dummy, &dummy,
+ EXPAND_FLAG_IGNORE_REPLACES);
continue;
}
@@ -400,12 +401,15 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
return ret;
}
+static char *theme_format_expand_data_rec(THEME_REC *theme, const char **format,
+ theme_rm_col default_fg, theme_rm_col default_bg,
+ theme_rm_col *save_last_fg, theme_rm_col *save_last_bg,
+ int flags, GTree *block_list);
+
/* expand a single {abstract ...data... } */
-static char *theme_format_expand_abstract(THEME_REC *theme,
- const char **formatp,
- theme_rm_col *last_fg,
- theme_rm_col *last_bg,
- int flags)
+static char *theme_format_expand_abstract(THEME_REC *theme, const char **formatp,
+ theme_rm_col *last_fg, theme_rm_col *last_bg, int flags,
+ GTree *block_list)
{
GString *str;
const char *p, *format;
@@ -439,12 +443,20 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
}
*formatp = format+len;
+ if (block_list == NULL) {
+ block_list = g_tree_new_full((GCompareDataFunc) g_strcmp0, NULL, g_free, NULL);
+ } else {
+ g_tree_ref(block_list);
+ }
+
/* get the abstract data */
data = g_hash_table_lookup(theme->abstracts, abstract);
- g_free(abstract);
- if (data == NULL) {
+ if (data == NULL || g_tree_lookup(block_list, abstract) != NULL) {
/* unknown abstract, just display the data */
data = "$0-";
+ g_free(abstract);
+ } else {
+ g_tree_insert(block_list, abstract, abstract);
}
abstract = g_strdup(data);
@@ -473,7 +485,7 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
str = g_string_new(NULL);
p = ret;
while (*p != '\0') {
- if (*p == '\\') {
+ if (*p == '\\' && p[1] != '\0') {
int chr;
p++;
chr = expand_escape(&p);
@@ -488,18 +500,17 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
/* abstract may itself contain abstracts or replaces */
p = abstract;
- ret = theme_format_expand_data(theme, &p, default_fg, default_bg,
- last_fg, last_bg,
- flags | EXPAND_FLAG_LASTCOLOR_ARG);
+ ret = theme_format_expand_data_rec(theme, &p, default_fg, default_bg, last_fg, last_bg,
+ flags | EXPAND_FLAG_LASTCOLOR_ARG, block_list);
g_free(abstract);
+ g_tree_unref(block_list);
return ret;
}
-/* expand the data part in {abstract data} */
-char *theme_format_expand_data(THEME_REC *theme, const char **format,
- theme_rm_col default_fg, theme_rm_col default_bg,
- theme_rm_col *save_last_fg, theme_rm_col *save_last_bg,
- int flags)
+static char *theme_format_expand_data_rec(THEME_REC *theme, const char **format,
+ theme_rm_col default_fg, theme_rm_col default_bg,
+ theme_rm_col *save_last_fg, theme_rm_col *save_last_bg,
+ int flags, GTree *block_list)
{
GString *str;
char *ret, *abstract;
@@ -545,9 +556,8 @@ char *theme_format_expand_data(THEME_REC *theme, const char **format,
break; /* error */
/* get a single {...} */
- abstract = theme_format_expand_abstract(theme, format,
- &last_fg, &last_bg,
- recurse_flags);
+ abstract = theme_format_expand_abstract(theme, format, &last_fg, &last_bg,
+ recurse_flags, block_list);
if (abstract != NULL) {
g_string_append(str, abstract);
g_free(abstract);
@@ -565,6 +575,15 @@ char *theme_format_expand_data(THEME_REC *theme, const char **format,
return ret;
}
+/* expand the data part in {abstract data} */
+char *theme_format_expand_data(THEME_REC *theme, const char **format, theme_rm_col default_fg,
+ theme_rm_col default_bg, theme_rm_col *save_last_fg,
+ theme_rm_col *save_last_bg, int flags)
+{
+ return theme_format_expand_data_rec(theme, format, default_fg, default_bg, save_last_bg,
+ save_last_bg, flags, NULL);
+}
+
#define IS_OLD_FORMAT(code, last_fg, last_bg) \
(((code) == 'n' && (last_fg) == 'n' && (last_bg) == 'n') || \
((code) != 'n' && ((code) == (last_fg) || (code) == (last_bg))))
diff --git a/src/fe-common/irc/fe-netjoin.c b/src/fe-common/irc/fe-netjoin.c
index bc39b27c..9ea633b4 100644
--- a/src/fe-common/irc/fe-netjoin.c
+++ b/src/fe-common/irc/fe-netjoin.c
@@ -253,12 +253,17 @@ static void sig_print_starting(TEXT_DEST_REC *dest)
if (!IS_IRC_SERVER(dest->server))
return;
- if (!server_ischannel(dest->server, dest->target))
- return;
-
rec = netjoin_find_server(IRC_SERVER(dest->server));
- if (rec != NULL && rec->netjoins != NULL)
+ if (rec != NULL && rec->netjoins != NULL) {
+ /* if netjoins exists, the server rec should be
+ still valid. otherwise, calling server->ischannel
+ may not be safe. */
+ if (dest->target != NULL &&
+ !server_ischannel((SERVER_REC *) rec->server, dest->target))
+ return;
+
print_netjoins(rec, NULL);
+ }
}
static int sig_check_netjoins(void)
diff --git a/src/fe-common/irc/fe-netsplit.c b/src/fe-common/irc/fe-netsplit.c
index ac3330e5..258d0d57 100644
--- a/src/fe-common/irc/fe-netsplit.c
+++ b/src/fe-common/irc/fe-netsplit.c
@@ -255,12 +255,16 @@ static void sig_print_starting(TEXT_DEST_REC *dest)
if (!IS_IRC_SERVER(dest->server))
return;
- if (!server_ischannel(dest->server, dest->target))
- return;
-
rec = IRC_SERVER(dest->server);
- if (rec->split_servers != NULL)
+ if (rec->split_servers != NULL) {
+ /* if split_servers exists, the server rec should be
+ still valid. otherwise, calling server->ischannel
+ may not be safe. */
+ if (dest->target != NULL && !server_ischannel((SERVER_REC *) rec, dest->target))
+ return;
+
print_splits(rec, NULL);
+ }
}
static int sig_check_splits(void)
diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c
index e91fcfb3..52a39969 100644
--- a/src/fe-text/gui-entry.c
+++ b/src/fe-text/gui-entry.c
@@ -65,6 +65,10 @@ static void entry_text_grow(GUI_ENTRY_REC *entry, int grow_size)
entry->text_alloc = nearest_power(entry->text_alloc+grow_size);
entry->text = g_realloc(entry->text,
sizeof(unichar) * entry->text_alloc);
+
+ if (entry->uses_extents)
+ entry->extents = g_realloc(entry->extents,
+ sizeof(char *) * entry->text_alloc);
}
GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
@@ -74,14 +78,30 @@ GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
rec = g_new0(GUI_ENTRY_REC, 1);
rec->xpos = xpos;
rec->ypos = ypos;
- rec->width = width;
- rec->text_alloc = 1024;
+ rec->width = width;
+ rec->text_alloc = 1024;
rec->text = g_new(unichar, rec->text_alloc);
- rec->text[0] = '\0';
- rec->utf8 = utf8;
+ rec->extents = NULL;
+ rec->text[0] = '\0';
+ rec->utf8 = utf8;
return rec;
}
+static void destroy_extents(GUI_ENTRY_REC *entry)
+{
+ if (entry->uses_extents) {
+ int i;
+ for (i = 0; i < entry->text_alloc; i++) {
+ if (entry->extents[i] != NULL) {
+ g_free(entry->extents[i]);
+ }
+ }
+ }
+ g_free(entry->extents);
+ entry->extents = NULL;
+ entry->uses_extents = FALSE;
+}
+
void gui_entry_destroy(GUI_ENTRY_REC *entry)
{
GSList *tmp;
@@ -100,9 +120,10 @@ void gui_entry_destroy(GUI_ENTRY_REC *entry)
}
g_slist_free(entry->kill_ring);
- g_free(entry->text);
+ destroy_extents(entry);
+ g_free(entry->text);
g_free(entry->prompt);
- g_free(entry);
+ g_free(entry);
}
/* big5 functions */
@@ -164,15 +185,36 @@ void big5_to_unichars(const char *str, unichar *out)
*out = '\0';
}
+/* Return screen length of plain string */
+static int scrlen_str(const char *str, int utf8)
+{
+ int len = 0;
+ char *stripped;
+ g_return_val_if_fail(str != NULL, 0);
+
+ stripped = strip_codes(str);
+ len = string_width(stripped, utf8 ? TREAT_STRING_AS_UTF8 : TREAT_STRING_AS_BYTES);
+ g_free(stripped);
+ return len;
+}
+
/* ----------------------------- */
-static int pos2scrpos(GUI_ENTRY_REC *entry, int pos)
+static int pos2scrpos(GUI_ENTRY_REC *entry, int pos, int cursor)
{
int i;
int xpos = 0;
+ if (!cursor && pos <= 0)
+ return 0;
+
+ if (entry->uses_extents && entry->extents[0] != NULL) {
+ xpos += scrlen_str(entry->extents[0], entry->utf8);
+ }
+
for (i = 0; i < pos; i++) {
unichar c = entry->text[i];
+ const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
if (term_type == TERM_TYPE_BIG5)
xpos += big5_width(c);
@@ -180,16 +222,26 @@ static int pos2scrpos(GUI_ENTRY_REC *entry, int pos)
xpos += unichar_isprint(c) ? mk_wcwidth(c) : 1;
else
xpos++;
+
+ if (extent != NULL) {
+ xpos += scrlen_str(extent, entry->utf8);
+ }
+
}
return xpos;
}
static int scrpos2pos(GUI_ENTRY_REC *entry, int pos)
{
- int i, width, xpos;
+ int i, width, xpos = 0;
- for (i = 0, xpos = 0; i < entry->text_len; i++) {
+ if (entry->uses_extents && entry->extents[0] != NULL) {
+ xpos += scrlen_str(entry->extents[0], entry->utf8);
+ }
+
+ for (i = 0; i < entry->text_len && xpos < pos; i++) {
unichar c = entry->text[i];
+ const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
if (term_type == TERM_TYPE_BIG5)
width = big5_width(c);
@@ -198,15 +250,13 @@ static int scrpos2pos(GUI_ENTRY_REC *entry, int pos)
else
width = 1;
- if (xpos + width > pos)
- break;
xpos += width;
- }
- if (xpos == pos)
- return i;
- else
- return i-1;
+ if (extent != NULL) {
+ xpos += scrlen_str(extent, entry->utf8);
+ }
+ }
+ return i;
}
/* Fixes the cursor position in screen */
@@ -215,19 +265,19 @@ static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
int old_scrstart;
/* assume prompt len == prompt scrlen */
- int start = pos2scrpos(entry, entry->scrstart);
- int now = pos2scrpos(entry, entry->pos);
+ int start = pos2scrpos(entry, entry->scrstart, FALSE);
+ int now = pos2scrpos(entry, entry->pos, TRUE);
old_scrstart = entry->scrstart;
- if (now-start < entry->width - 2 - entry->promptlen && now-start > 0)
+ if (now-start < entry->width - 2 - entry->promptlen && now-start > 0) {
entry->scrpos = now-start;
- else if (now < entry->width - 1 - entry->promptlen) {
+ } else if (now < entry->width - 1 - entry->promptlen) {
entry->scrstart = 0;
entry->scrpos = now;
} else {
entry->scrstart = scrpos2pos(entry, now-(entry->width -
entry->promptlen)*2/3);
- start = pos2scrpos(entry, entry->scrstart);
+ start = pos2scrpos(entry, entry->scrstart, FALSE);
entry->scrpos = now - start;
}
@@ -235,59 +285,140 @@ static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
entry->redraw_needed_from = 0;
}
+static char *text_effects_only(const char *p)
+{
+ GString *str;
+
+ str = g_string_sized_new(strlen(p));
+ for (; *p != '\0'; p++) {
+ if (*p == 4 && p[1] != '\0') {
+ if (p[1] >= FORMAT_STYLE_SPECIAL) {
+ g_string_append_len(str, p, 2);
+ p++;
+ continue;
+ }
+
+ /* irssi color */
+ if (p[2] != '\0') {
+#ifdef TERM_TRUECOLOR
+ if (p[1] == FORMAT_COLOR_24) {
+ if (p[3] == '\0') p += 2;
+ else if (p[4] == '\0') p += 3;
+ else if (p[5] == '\0') p += 4;
+ else {
+ g_string_append_len(str, p, 6);
+ p += 5;
+ }
+ } else {
+#endif /* TERM_TRUECOLOR */
+ g_string_append_len(str, p, 3);
+ p += 2;
+#ifdef TERM_TRUECOLOR
+ }
+#endif /* TERM_TRUECOLOR */
+ continue;
+ }
+ }
+ }
+
+ return g_string_free(str, FALSE);
+}
+
static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
{
- int i;
- int xpos, end_xpos;
+ int i, start;
+ int start_xpos, xpos, new_xpos, end_xpos;
+ char *tmp;
+ GString *str;
+
+ start = entry->scrstart + pos;
- xpos = entry->xpos + entry->promptlen +
- pos2scrpos(entry, pos + entry->scrstart) -
- pos2scrpos(entry, entry->scrstart);
+ start_xpos = xpos = entry->xpos + entry->promptlen +
+ pos2scrpos(entry, start, FALSE) -
+ pos2scrpos(entry, entry->scrstart, FALSE);
end_xpos = entry->xpos + entry->width;
if (xpos > end_xpos)
return;
+ str = g_string_sized_new(entry->text_alloc);
+
term_set_color(root_window, ATTR_RESET);
- term_move(root_window, xpos, entry->ypos);
+ /* term_move(root_window, xpos, entry->ypos); */
+
+ if (entry->uses_extents && entry->extents[0] != NULL) {
+ g_string_append(str, entry->extents[0]);
+ }
+ for (i = 0; i < start && i < entry->text_len; i++) {
+ const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
+ if (extent != NULL) {
+ g_string_append(str, extent);
+ }
+ }
+ if (i == 0) {
+ xpos += scrlen_str(str->str, entry->utf8);
+ } else {
+ tmp = text_effects_only(str->str);
+ g_string_assign(str, tmp);
+ g_free(tmp);
+ }
- for (i = entry->scrstart + pos; i < entry->text_len; i++) {
+ for (; i < entry->text_len; i++) {
unichar c = entry->text[i];
+ const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
+ new_xpos = xpos;
if (entry->hidden)
- xpos++;
+ new_xpos++;
else if (term_type == TERM_TYPE_BIG5)
- xpos += big5_width(c);
+ new_xpos += big5_width(c);
else if (entry->utf8)
- xpos += unichar_isprint(c) ? mk_wcwidth(c) : 1;
+ new_xpos += unichar_isprint(c) ? mk_wcwidth(c) : 1;
else
- xpos++;
+ new_xpos++;
- if (xpos > end_xpos)
+ if (new_xpos > end_xpos)
break;
if (entry->hidden)
- term_addch(root_window, ' ');
+ g_string_append_c(str, ' ');
else if (unichar_isprint(c))
- term_add_unichar(root_window, c);
+ g_string_append_unichar(str, c);
else {
- term_set_color(root_window, ATTR_RESET|ATTR_REVERSE);
- term_addch(root_window, (c & 127)+'A'-1);
- term_set_color(root_window, ATTR_RESET);
+ g_string_append_c(str, 4);
+ g_string_append_c(str, FORMAT_STYLE_REVERSE);
+ g_string_append_c(str, (c & 127)+'A'-1);
+ g_string_append_c(str, 4);
+ g_string_append_c(str, FORMAT_STYLE_REVERSE);
+ }
+ xpos = new_xpos;
+
+ if (extent != NULL) {
+ new_xpos += scrlen_str(extent, entry->utf8);
+
+ if (new_xpos > end_xpos)
+ break;
+
+ g_string_append(str, extent);
+ xpos = new_xpos;
}
}
/* clear the rest of the input line */
if (xpos < end_xpos) {
- if (end_xpos == term_width)
- term_clrtoeol(root_window);
- else {
+ if (end_xpos == term_width) {
+ g_string_append_c(str, 4);
+ g_string_append_c(str, FORMAT_STYLE_CLRTOEOL);
+ } else {
while (xpos < end_xpos) {
- term_addch(root_window, ' ');
+ g_string_append_c(str, ' ');
xpos++;
}
}
}
+
+ gui_printtext_internal(start_xpos, entry->ypos, str->str);
+ g_string_free(str, TRUE);
}
static void gui_entry_draw(GUI_ENTRY_REC *entry)
@@ -359,19 +490,6 @@ void gui_entry_set_active(GUI_ENTRY_REC *entry)
}
}
-/* Return screen length of plain string */
-static int scrlen_str(const char *str)
-{
- int len = 0;
- char *stripped;
- g_return_val_if_fail(str != NULL, 0);
-
- stripped = strip_codes(str);
- len = string_width(stripped, -1);
- g_free(stripped);
- return len;
-}
-
void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
{
int oldlen;
@@ -382,7 +500,7 @@ void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
if (str != NULL) {
g_free_not_null(entry->prompt);
entry->prompt = g_strdup(str);
- entry->promptlen = scrlen_str(str);
+ entry->promptlen = scrlen_str(str, entry->utf8);
}
if (entry->prompt != NULL)
@@ -416,6 +534,7 @@ void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str)
entry->text_len = 0;
entry->pos = 0;
entry->text[0] = '\0';
+ destroy_extents(entry);
gui_entry_insert_text(entry, str);
}
@@ -488,6 +607,15 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
g_memmove(entry->text + entry->pos + len, entry->text + entry->pos,
(entry->text_len-entry->pos + 1) * sizeof(unichar));
+ /* make space for the color */
+ if (entry->uses_extents) {
+ g_memmove(entry->extents + entry->pos + len + 1, entry->extents + entry->pos + 1,
+ (entry->text_len-entry->pos) * sizeof(char *));
+ for (i = 0; i < len; i++) {
+ entry->extents[entry->pos + i + 1] = NULL;
+ }
+ }
+
if (!entry->utf8) {
if (term_type == TERM_TYPE_BIG5) {
chr = entry->text[entry->pos + len];
@@ -514,7 +642,7 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
{
- g_return_if_fail(entry != NULL);
+ g_return_if_fail(entry != NULL);
if (chr == 0 || chr == 13 || chr == 10)
return; /* never insert NUL, CR or LF characters */
@@ -522,7 +650,7 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
if (entry->utf8 && entry->pos == 0 && mk_wcwidth(chr) == 0)
return;
- gui_entry_redraw_from(entry, entry->pos);
+ gui_entry_redraw_from(entry, entry->pos);
entry_text_grow(entry, 1);
@@ -530,9 +658,15 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
g_memmove(entry->text + entry->pos + 1, entry->text + entry->pos,
(entry->text_len-entry->pos + 1) * sizeof(unichar));
+ if (entry->uses_extents) {
+ g_memmove(entry->extents + entry->pos + 1 + 1, entry->extents + entry->pos + 1,
+ (entry->text_len-entry->pos) * sizeof(char *));
+ entry->extents[entry->pos + 1] = NULL;
+ }
+
entry->text[entry->pos] = chr;
entry->text_len++;
- entry->pos++;
+ entry->pos++;
gui_entry_fix_cursor(entry);
gui_entry_draw(entry);
@@ -631,7 +765,7 @@ static GUI_ENTRY_CUTBUFFER_REC *get_cutbuffer_rec(GUI_ENTRY_REC *entry, CUTBUFFE
void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
- size_t w = 0;
+ size_t i, w = 0;
g_return_if_fail(entry != NULL);
@@ -700,6 +834,23 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_
g_memmove(entry->text + entry->pos - size, entry->text + entry->pos,
(entry->text_len-entry->pos+1) * sizeof(unichar));
+ if (entry->uses_extents) {
+ for (i = entry->pos - size; i < entry->pos; i++) {
+ if (entry->extents[i+1] != NULL) {
+ g_free(entry->extents[i+1]);
+ }
+ }
+ g_memmove(entry->extents + entry->pos - size + 1, entry->extents + entry->pos + 1,
+ (entry->text_len - entry->pos) * sizeof(void *)); /* no null terminator here */
+ for (i = 0; i < size; i++) {
+ entry->extents[entry->text_len - i] = NULL;
+ }
+ if (entry->text_len == size && entry->extents[0] != NULL) {
+ g_free(entry->extents[0]);
+ entry->extents[0] = NULL;
+ }
+ }
+
entry->pos -= size;
entry->text_len -= size;
@@ -719,11 +870,28 @@ void gui_entry_erase_cell(GUI_ENTRY_REC *entry)
mk_wcwidth(entry->text[entry->pos+size]) == 0) size++;
g_memmove(entry->text + entry->pos, entry->text + entry->pos + size,
- (entry->text_len-entry->pos-size+1) * sizeof(unichar));
+ (entry->text_len-entry->pos-size+1) * sizeof(unichar));
+
+ if (entry->uses_extents) {
+ int i;
+ for (i = 0; i < size; i++) {
+ g_free(entry->extents[entry->pos + i + 1]);
+ }
+ g_memmove(entry->extents + entry->pos + 1, entry->extents + entry->pos + size + 1,
+ (entry->text_len-entry->pos-size) * sizeof(char *));
+ for (i = 0; i < size; i++) {
+ entry->extents[entry->text_len - i] = NULL;
+ }
+ if (entry->text_len == size && entry->extents[0] != NULL) {
+ g_free(entry->extents[0]);
+ entry->extents[0] = NULL;
+ }
+ }
entry->text_len -= size;
gui_entry_redraw_from(entry, entry->pos);
+ gui_entry_fix_cursor(entry);
gui_entry_draw(entry);
}
@@ -782,6 +950,7 @@ void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPD
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
{
unichar chr;
+ char *extent;
if (entry->pos == 0 || entry->text_len < 2)
return;
@@ -794,6 +963,12 @@ void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
entry->text[entry->pos] = entry->text[entry->pos-1];
entry->text[entry->pos-1] = chr;
+ if (entry->uses_extents) {
+ extent = entry->extents[entry->pos+1];
+ entry->extents[entry->pos+1] = entry->extents[entry->pos];
+ entry->extents[entry->pos] = extent;
+ }
+
entry->pos++;
gui_entry_redraw_from(entry, entry->pos-2);
@@ -830,31 +1005,60 @@ void gui_entry_transpose_words(GUI_ENTRY_REC *entry)
/* do wordswap if any found */
if (spos1 < epos1 && epos1 < spos2 && spos2 < epos2) {
unichar *first, *sep, *second;
+ char **first_extent, **sep_extent, **second_extent;
int i;
first = (unichar *) g_malloc( (epos1 - spos1) * sizeof(unichar) );
sep = (unichar *) g_malloc( (spos2 - epos1) * sizeof(unichar) );
second = (unichar *) g_malloc( (epos2 - spos2) * sizeof(unichar) );
- for (i = spos1; i < epos1; i++)
+ first_extent = (char **) g_malloc( (epos1 - spos1) * sizeof(char *) );
+ sep_extent = (char **) g_malloc( (spos2 - epos1) * sizeof(char *) );
+ second_extent = (char **) g_malloc( (epos2 - spos2) * sizeof(char *) );
+
+ for (i = spos1; i < epos1; i++) {
first[i-spos1] = entry->text[i];
- for (i = epos1; i < spos2; i++)
+ if (entry->uses_extents)
+ first_extent[i-spos1] = entry->extents[i+1];
+ }
+ for (i = epos1; i < spos2; i++) {
sep[i-epos1] = entry->text[i];
- for (i = spos2; i < epos2; i++)
+ if (entry->uses_extents)
+ sep_extent[i-epos1] = entry->extents[i+1];
+ }
+ for (i = spos2; i < epos2; i++) {
second[i-spos2] = entry->text[i];
+ if (entry->uses_extents)
+ second_extent[i-spos2] = entry->extents[i+1];
+ }
entry->pos = spos1;
- for (i = 0; i < epos2-spos2; i++)
- entry->text[entry->pos++] = second[i];
- for (i = 0; i < spos2-epos1; i++)
- entry->text[entry->pos++] = sep[i];
- for (i = 0; i < epos1-spos1; i++)
- entry->text[entry->pos++] = first[i];
+ for (i = 0; i < epos2-spos2; i++) {
+ entry->text[entry->pos] = second[i];
+ if (entry->uses_extents)
+ entry->extents[entry->pos+1] = second_extent[i];
+ entry->pos++;
+ }
+ for (i = 0; i < spos2-epos1; i++) {
+ entry->text[entry->pos] = sep[i];
+ if (entry->uses_extents)
+ entry->extents[entry->pos+1] = sep_extent[i];
+ entry->pos++;
+ }
+ for (i = 0; i < epos1-spos1; i++) {
+ entry->text[entry->pos] = first[i];
+ if (entry->uses_extents)
+ entry->extents[entry->pos+1] = first_extent[i];
+ entry->pos++;
+ }
g_free(first);
g_free(sep);
g_free(second);
+ g_free(first_extent);
+ g_free(sep_extent);
+ g_free(second_extent);
}
gui_entry_redraw_from(entry, spos1);
@@ -938,11 +1142,17 @@ void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int pos_bytes)
{
- int pos;
+ int pos, extents_alloc;
+ char **extents;
const char *ptr;
g_return_if_fail(entry != NULL);
+ extents = entry->extents;
+ extents_alloc = entry->text_alloc;
+ entry->extents = NULL;
+ entry->uses_extents = FALSE;
+
gui_entry_set_text(entry, str);
if (entry->utf8) {
@@ -953,6 +1163,19 @@ void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int
else
pos = pos_bytes;
+ if (extents != NULL) {
+ entry->uses_extents = TRUE;
+ entry->extents = extents;
+ if (extents_alloc < entry->text_alloc) {
+ int i;
+ entry->extents = g_realloc(entry->extents,
+ sizeof(char *) * entry->text_alloc);
+ for (i = extents_alloc; i < entry->text_alloc; i++) {
+ entry->extents[i] = NULL;
+ }
+ }
+ }
+ gui_entry_redraw_from(entry, 0);
gui_entry_set_pos(entry, pos);
}
@@ -1042,3 +1265,234 @@ void gui_entry_redraw(GUI_ENTRY_REC *entry)
gui_entry_fix_cursor(entry);
gui_entry_draw(entry);
}
+
+static void gui_entry_alloc_extents(GUI_ENTRY_REC *entry)
+{
+ entry->uses_extents = TRUE;
+ entry->extents = g_new0(char *, entry->text_alloc);
+}
+
+void gui_entry_set_extent(GUI_ENTRY_REC *entry, int pos, const char *text)
+{
+ int update = FALSE;
+
+ g_return_if_fail(entry != NULL);
+
+ if (pos < 0 || pos > entry->text_len)
+ return;
+
+ if (text == NULL)
+ return;
+
+ if (!entry->uses_extents) {
+ gui_entry_alloc_extents(entry);
+ }
+
+ if (g_strcmp0(entry->extents[pos], text) != 0) {
+ g_free(entry->extents[pos]);
+ if (*text == '\0') {
+ entry->extents[pos] = NULL;
+ } else {
+ entry->extents[pos] = g_strdup(text);
+ }
+ update = TRUE;
+ }
+
+ if (update) {
+ gui_entry_redraw_from(entry, pos - 1);
+ gui_entry_fix_cursor(entry);
+ gui_entry_draw(entry);
+ }
+}
+
+void gui_entry_set_extents(GUI_ENTRY_REC *entry, int pos, int len, const char *left, const char *right)
+{
+ int end, update = FALSE;
+
+ g_return_if_fail(entry != NULL);
+
+ if (pos < 0 || len < 0 || pos > entry->text_len)
+ return;
+
+ end = pos + len;
+
+ if (end > entry->text_len)
+ end = entry->text_len;
+
+ if (!entry->uses_extents) {
+ gui_entry_alloc_extents(entry);
+ }
+
+ if (g_strcmp0(entry->extents[pos], left) != 0) {
+ g_free(entry->extents[pos]);
+ if (*left == '\0') {
+ entry->extents[pos] = NULL;
+ } else {
+ entry->extents[pos] = g_strdup(left);
+ }
+ update = TRUE;
+ }
+
+ if (pos != end && g_strcmp0(entry->extents[end], right) != 0) {
+ g_free(entry->extents[end]);
+ if (*right == '\0') {
+ entry->extents[end] = NULL;
+ } else {
+ entry->extents[end] = g_strdup(right);
+ }
+ update = TRUE;
+ }
+
+ if (update) {
+ gui_entry_redraw_from(entry, pos - 1);
+ gui_entry_fix_cursor(entry);
+ gui_entry_draw(entry);
+ }
+}
+
+void gui_entry_clear_extents(GUI_ENTRY_REC *entry, int pos, int len)
+{
+ int i, end, update = FALSE;
+
+ g_return_if_fail(entry != NULL);
+
+ if (pos < 0 || len < 0 || pos > entry->text_len)
+ return;
+
+ end = pos + len;
+
+ if (end > entry->text_len)
+ end = entry->text_len;
+
+ if (!entry->uses_extents) {
+ return;
+ }
+
+ for (i = pos; i <= end; i++) {
+ if (entry->extents[i] != NULL) {
+ g_free(entry->extents[i]);
+ entry->extents[i] = NULL;
+ update = TRUE;
+ }
+ }
+
+ if (update) {
+ gui_entry_redraw_from(entry, pos);
+ gui_entry_fix_cursor(entry);
+ gui_entry_draw(entry);
+ }
+}
+
+char *gui_entry_get_extent(GUI_ENTRY_REC *entry, int pos)
+{
+ g_return_val_if_fail(entry != NULL, NULL);
+
+ if (!entry->uses_extents)
+ return NULL;
+
+ if (pos < 0 || pos >= entry->text_len)
+ return NULL;
+
+ return entry->extents[pos];
+}
+
+#define POS_FLAG "%|"
+GSList *gui_entry_get_text_and_extents(GUI_ENTRY_REC *entry)
+{
+ GSList *list = NULL;
+ GString *str;
+ int i;
+
+ g_return_val_if_fail(entry != NULL, NULL);
+
+ if (entry->uses_extents && entry->extents[0] != NULL) {
+ if (entry->pos == 0) {
+ list = g_slist_prepend(list, g_strconcat(entry->extents[0], POS_FLAG, NULL));
+ } else {
+ list = g_slist_prepend(list, g_strdup(entry->extents[0]));
+ }
+ } else {
+ if (entry->pos == 0) {
+ list = g_slist_prepend(list, g_strdup(POS_FLAG));
+ } else {
+ list = g_slist_prepend(list, NULL);
+ }
+ }
+
+ str = g_string_sized_new(entry->text_alloc);
+ for (i = 0; i < entry->text_len; i++) {
+ if (entry->utf8) {
+ g_string_append_unichar(str, entry->text[i]);
+ } else if (term_type == TERM_TYPE_BIG5) {
+ if(entry->text[i] > 0xff)
+ g_string_append_c(str, (entry->text[i] >> 8) & 0xff);
+ g_string_append_c(str, entry->text[i] & 0xff);
+ } else {
+ g_string_append_c(str, entry->text[i]);
+ }
+ if (entry->pos == i+1 || (entry->uses_extents && entry->extents[i+1] != NULL)) {
+ list = g_slist_prepend(list, g_strdup(str->str));
+ g_string_truncate(str, 0);
+ if (entry->uses_extents && entry->extents[i+1] != NULL) {
+ if (entry->pos == i+1) {
+ list = g_slist_prepend(list, g_strconcat(entry->extents[i+1], POS_FLAG, NULL));
+ } else {
+ list = g_slist_prepend(list, g_strdup(entry->extents[i+1]));
+ }
+ } else if (entry->pos == i+1) {
+ list = g_slist_prepend(list, g_strdup(POS_FLAG));
+ }
+ }
+ }
+ if (str->len > 0) {
+ list = g_slist_prepend(list, g_strdup(str->str));
+ }
+ list = g_slist_reverse(list);
+ g_string_free(str, TRUE);
+
+ return list;
+}
+
+void gui_entry_set_text_and_extents(GUI_ENTRY_REC *entry, GSList *list)
+{
+ GSList *tmp;
+ int pos = -1;
+ int is_extent = 1;
+
+ gui_entry_set_text(entry, "");
+ for (tmp = list, is_extent = TRUE; tmp != NULL; tmp = tmp->next, is_extent ^= 1) {
+ if (is_extent) {
+ char *extent;
+ int len;
+
+ if (tmp->data == NULL)
+ continue;
+
+ extent = g_strdup(tmp->data);
+ len = strlen(extent);
+ if (len >= strlen(POS_FLAG) && g_strcmp0(&extent[len-strlen(POS_FLAG)], POS_FLAG) == 0) {
+ char *tmp;
+ tmp = extent;
+ extent = g_strndup(tmp, len - strlen(POS_FLAG));
+ g_free(tmp);
+ pos = entry->pos;
+ }
+
+ if (strlen(extent) > 0) {
+ gui_entry_set_extent(entry, entry->pos, extent);
+ }
+ g_free(extent);
+ } else {
+ gui_entry_insert_text(entry, tmp->data);
+ }
+ }
+ gui_entry_set_pos(entry, pos);
+}
+
+void gui_entry_init(void)
+{
+}
+
+void gui_entry_deinit(void)
+{
+}
diff --git a/src/fe-text/gui-entry.h b/src/fe-text/gui-entry.h
index 000c5f03..dff860d3 100644
--- a/src/fe-text/gui-entry.h
+++ b/src/fe-text/gui-entry.h
@@ -9,6 +9,7 @@ typedef struct {
typedef struct {
int text_len, text_alloc; /* as shorts, not chars */
unichar *text;
+ char **extents;
GSList *kill_ring;
@@ -26,6 +27,7 @@ typedef struct {
unsigned int previous_append_next_kill:1;
unsigned int append_next_kill:1;
unsigned int yank_preceded:1;
+ unsigned int uses_extents:1;
} GUI_ENTRY_REC;
typedef enum {
@@ -77,5 +79,14 @@ void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space);
void gui_entry_redraw(GUI_ENTRY_REC *entry);
+void gui_entry_set_extent(GUI_ENTRY_REC *entry, int pos, const char *text);
+void gui_entry_set_extents(GUI_ENTRY_REC *entry, int pos, int len, const char *left, const char *right);
+void gui_entry_clear_extents(GUI_ENTRY_REC *entry, int pos, int len);
+char *gui_entry_get_extent(GUI_ENTRY_REC *entry, int pos);
+GSList *gui_entry_get_text_and_extents(GUI_ENTRY_REC *entry);
+void gui_entry_set_text_and_extents(GUI_ENTRY_REC *entry, GSList *list);
+
+void gui_entry_init(void);
+void gui_entry_deinit(void);
#endif
diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c
index 0288e4f1..f30ce4b8 100644
--- a/src/fe-text/irssi.c
+++ b/src/fe-text/irssi.c
@@ -165,6 +165,7 @@ static void textui_finish_init(void)
gui_expandos_init();
gui_printtext_init();
gui_readline_init();
+ gui_entry_init();
lastlog_init();
mainwindows_init();
mainwindow_activity_init();
@@ -230,6 +231,7 @@ static void textui_deinit(void)
lastlog_deinit();
statusbar_deinit();
+ gui_entry_deinit();
gui_printtext_deinit();
gui_readline_deinit();
gui_windows_deinit();
diff --git a/src/fe-text/mainwindows.c b/src/fe-text/mainwindows.c
index 5f24674f..83a3e0cc 100644
--- a/src/fe-text/mainwindows.c
+++ b/src/fe-text/mainwindows.c
@@ -915,6 +915,8 @@ static int try_shrink_lower(MAIN_WINDOW_REC *window, int count)
{
MAIN_WINDOW_REC *shrink_win;
+ g_return_val_if_fail(count >= 0, FALSE);
+
shrink_win = mainwindows_find_lower(window);
if (shrink_win != NULL) {
int ok;
@@ -959,6 +961,8 @@ static int try_shrink_upper(MAIN_WINDOW_REC *window, int count)
{
MAIN_WINDOW_REC *shrink_win;
+ g_return_val_if_fail(count >= 0, FALSE);
+
shrink_win = mainwindows_find_upper(window);
if (shrink_win != NULL) {
int ok;
@@ -1063,6 +1067,8 @@ static int try_grow_upper(MAIN_WINDOW_REC *window, int count)
static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count, int resize_lower)
{
+ g_return_val_if_fail(count >= 0, FALSE);
+
if (MAIN_WINDOW_TEXT_HEIGHT(window)-count < WINDOW_MIN_SIZE)
return FALSE;
@@ -1091,6 +1097,8 @@ static int try_rshrink_right(MAIN_WINDOW_REC *window, int count)
{
MAIN_WINDOW_REC *shrink_win;
+ g_return_val_if_fail(count >= 0, FALSE);
+
shrink_win = mainwindows_find_right(window, FALSE);
if (shrink_win != NULL) {
if (MAIN_WINDOW_TEXT_WIDTH(shrink_win)-count < NEW_WINDOW_WIDTH) {
@@ -1111,6 +1119,8 @@ static int try_rshrink_left(MAIN_WINDOW_REC *window, int count)
{
MAIN_WINDOW_REC *shrink_win;
+ g_return_val_if_fail(count >= 0, FALSE);
+
shrink_win = mainwindows_find_left(window, FALSE);
if (shrink_win != NULL) {
if (MAIN_WINDOW_TEXT_WIDTH(shrink_win)-count < NEW_WINDOW_WIDTH) {
@@ -1168,6 +1178,8 @@ static int try_rgrow_left(MAIN_WINDOW_REC *window, int count)
static int mainwindow_rshrink(MAIN_WINDOW_REC *window, int count)
{
+ g_return_val_if_fail(count >= 0, FALSE);
+
if (MAIN_WINDOW_TEXT_WIDTH(window)-count < NEW_WINDOW_WIDTH)
return FALSE;
@@ -1220,19 +1232,29 @@ void mainwindows_redraw_dirty(void)
}
}
+static void mainwindow_grow_int(int count)
+{
+ if (count == 0) {
+ return;
+ } else if (count < 0) {
+ if (!mainwindow_shrink(WINDOW_MAIN(active_win), -count, FALSE)) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL);
+ }
+ } else {
+ if (!mainwindow_grow(WINDOW_MAIN(active_win), count, FALSE)) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL);
+ }
+ }
+}
+
/* SYNTAX: WINDOW GROW [<lines>] */
static void cmd_window_grow(const char *data)
{
- MAIN_WINDOW_REC *window;
int count;
count = *data == '\0' ? 1 : atoi(data);
- window = WINDOW_MAIN(active_win);
- if (!mainwindow_grow(window, count, FALSE)) {
- printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
- TXT_WINDOW_TOO_SMALL);
- }
+ mainwindow_grow_int(count);
}
/* SYNTAX: WINDOW SHRINK [<lines>] */
@@ -1241,16 +1263,14 @@ static void cmd_window_shrink(const char *data)
int count;
count = *data == '\0' ? 1 : atoi(data);
- if (!mainwindow_shrink(WINDOW_MAIN(active_win), count, FALSE)) {
- printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
- TXT_WINDOW_TOO_SMALL);
- }
+ if (count < -INT_MAX) count = -INT_MAX;
+
+ mainwindow_grow_int(-count);
}
/* SYNTAX: WINDOW SIZE <lines> */
static void cmd_window_size(const char *data)
{
- char sizestr[MAX_INT_STRLEN];
int size;
if (!is_numeric(data, 0)) return;
@@ -1258,13 +1278,9 @@ static void cmd_window_size(const char *data)
size -= WINDOW_MAIN(active_win)->height -
WINDOW_MAIN(active_win)->statusbar_lines;
- if (size == 0) return;
+ if (size < -INT_MAX) size = -INT_MAX;
- ltoa(sizestr, size < 0 ? -size : size);
- if (size < 0)
- cmd_window_shrink(sizestr);
- else
- cmd_window_grow(sizestr);
+ mainwindow_grow_int(size);
}
/* SYNTAX: WINDOW BALANCE */
@@ -1393,6 +1409,11 @@ static void _cmd_window_show_opt(const char *data, int right)
}
parent = mainwindow_create(right);
+ if (parent == NULL) {
+ printformat_window(active_win, MSGLEVEL_CLIENTERROR, TXT_WINDOW_TOO_SMALL);
+ return;
+ }
+
parent->active = window;
gui_window_reparent(window, parent);
@@ -1415,16 +1436,29 @@ static void cmd_window_rshow(const char *data)
_cmd_window_show_opt(data, TRUE);
}
+static void window_rgrow_int(int count)
+{
+ if (count == 0) {
+ return;
+ } else if (count < 0) {
+ if (!mainwindow_rshrink(WINDOW_MAIN(active_win), -count)) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL);
+ }
+ } else {
+ if (!mainwindow_rgrow(WINDOW_MAIN(active_win), count)) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL);
+ }
+ }
+}
+
/* SYNTAX: WINDOW RGROW [<columns>] */
static void cmd_window_rgrow(const char *data)
{
int count;
count = *data == '\0' ? 1 : atoi(data);
- if (!mainwindow_rgrow(WINDOW_MAIN(active_win), count)) {
- printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
- TXT_WINDOW_TOO_SMALL);
- }
+
+ window_rgrow_int(count);
}
/* SYNTAX: WINDOW RSHRINK [<lines>] */
@@ -1433,29 +1467,22 @@ static void cmd_window_rshrink(const char *data)
int count;
count = *data == '\0' ? 1 : atoi(data);
- if (!mainwindow_rshrink(WINDOW_MAIN(active_win), count)) {
- printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
- TXT_WINDOW_TOO_SMALL);
- }
+ if (count < -INT_MAX) count = -INT_MAX;
+
+ window_rgrow_int(-count);
}
/* SYNTAX: WINDOW RSIZE <columns> */
static void cmd_window_rsize(const char *data)
{
- char rsizestr[MAX_INT_STRLEN];
int rsize;
if (!is_numeric(data, 0)) return;
rsize = atoi(data);
rsize -= MAIN_WINDOW_TEXT_WIDTH(WINDOW_MAIN(active_win));
- if (rsize == 0) return;
- ltoa(rsizestr, rsize < 0 ? -rsize : rsize);
- if (rsize < 0)
- cmd_window_rshrink(rsizestr);
- else
- cmd_window_rgrow(rsizestr);
+ window_rgrow_int(rsize);
}
/* SYNTAX: WINDOW RBALANCE */
diff --git a/src/irc/core/irc-cap.c b/src/irc/core/irc-cap.c
index 5464e493..1a60d99b 100644
--- a/src/irc/core/irc-cap.c
+++ b/src/irc/core/irc-cap.c
@@ -36,7 +36,7 @@ int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable)
return TRUE;
}
else if (!enable && gslist_find_string(server->cap_queue, cap)) {
- server->cap_queue = gslist_remove_string(server->cap_queue, cap);
+ server->cap_queue = gslist_delete_string(server->cap_queue, cap, g_free);
return TRUE;
}
@@ -45,7 +45,7 @@ int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable)
if (enable && !gslist_find_string(server->cap_active, cap)) {
/* Make sure the required cap is supported by the server */
- if (!gslist_find_string(server->cap_supported, cap))
+ if (!g_hash_table_lookup_extended(server->cap_supported, cap, NULL, NULL))
return FALSE;
irc_send_cmdv(server, "CAP REQ %s", cap);
@@ -79,61 +79,131 @@ static void cap_emit_signal (IRC_SERVER_REC *server, char *cmd, char *args)
g_free(signal_name);
}
+static gboolean parse_cap_name(char *name, char **key, char **val)
+{
+ const char *eq;
+
+ g_return_val_if_fail(name != NULL, FALSE);
+ g_return_val_if_fail(name[0] != '\0', FALSE);
+
+ eq = strchr(name, '=');
+ /* KEY only value */
+ if (eq == NULL) {
+ *key = g_strdup(name);
+ *val = NULL;
+ /* Some values are in a KEY=VALUE form, parse them */
+ } else {
+ *key = g_strndup(name, (gsize)(eq - name));
+ *val = g_strdup(eq + 1);
+ }
+
+ return TRUE;
+}
+
static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *address)
{
GSList *tmp;
GString *cmd;
- char *params, *evt, *list, **caps;
- int i, caps_length, disable, avail_caps;
+ char *params, *evt, *list, *star, **caps;
+ int i, caps_length, disable, avail_caps, multiline;
- params = event_get_params(args, 3, NULL, &evt, &list);
+ params = event_get_params(args, 4, NULL, &evt, &star, &list);
if (params == NULL)
return;
+ /* Multiline responses have an additional parameter and we have to do
+ * this stupid dance to parse them */
+ if (!g_ascii_strcasecmp(evt, "LS") && !strcmp(star, "*")) {
+ multiline = TRUE;
+ }
+ /* This branch covers the '*' parameter isn't present, adjust the
+ * parameter pointer to compensate for this */
+ else if (list[0] == '\0') {
+ multiline = FALSE;
+ list = star;
+ }
+ /* Malformed request, terminate the negotiation */
+ else {
+ cap_finish_negotiation(server);
+ g_warn_if_reached();
+ return;
+ }
+
+ /* The table is created only when needed */
+ if (server->cap_supported == NULL) {
+ server->cap_supported = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ g_free, g_free);
+ }
+
/* Strip the trailing whitespaces before splitting the string, some servers send responses with
* superfluous whitespaces that g_strsplit the interprets as tokens */
caps = g_strsplit(g_strchomp(list), " ", -1);
caps_length = g_strv_length(caps);
- if (!g_strcmp0(evt, "LS")) {
+ if (!g_ascii_strcasecmp(evt, "LS")) {
+ if (!server->cap_in_multiline) {
+ /* Throw away everything and start from scratch */
+ g_hash_table_remove_all(server->cap_supported);
+ }
+
+ server->cap_in_multiline = multiline;
+
/* Create a list of the supported caps */
- for (i = 0; i < caps_length; i++)
- server->cap_supported = g_slist_prepend(server->cap_supported, g_strdup(caps[i]));
+ for (i = 0; i < caps_length; i++) {
+ char *key, *val;
- /* Request the required caps, if any */
- if (server->cap_queue == NULL) {
- cap_finish_negotiation(server);
+ if (!parse_cap_name(caps[i], &key, &val)) {
+ g_warning("Invalid CAP %s key/value pair", evt);
+ continue;
+ }
+
+ if (g_hash_table_lookup_extended(server->cap_supported, key, NULL, NULL)) {
+ /* The specification doesn't say anything about
+ * duplicated values, let's just warn the user */
+ g_warning("The server sent the %s capability twice", key);
+ }
+ g_hash_table_insert(server->cap_supported, key, val);
}
- else {
- cmd = g_string_new("CAP REQ :");
- avail_caps = 0;
+ /* A multiline response is always terminated by a normal one,
+ * wait until we receive that one to require any CAP */
+ if (multiline == FALSE) {
+ /* No CAP has been requested */
+ if (server->cap_queue == NULL) {
+ cap_finish_negotiation(server);
+ }
+ else {
+ cmd = g_string_new("CAP REQ :");
+
+ avail_caps = 0;
- /* Check whether the cap is supported by the server */
- for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
- if (gslist_find_string(server->cap_supported, tmp->data)) {
- if (avail_caps > 0)
- g_string_append_c(cmd, ' ');
- g_string_append(cmd, tmp->data);
+ /* Check whether the cap is supported by the server */
+ for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
+ if (g_hash_table_lookup_extended(server->cap_supported, tmp->data, NULL, NULL)) {
+ if (avail_caps > 0)
+ g_string_append_c(cmd, ' ');
+ g_string_append(cmd, tmp->data);
- avail_caps++;
+ avail_caps++;
+ }
}
- }
- /* Clear the queue here */
- gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
- server->cap_queue = NULL;
+ /* Clear the queue here */
+ gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
+ server->cap_queue = NULL;
- /* If the server doesn't support any cap we requested close the negotiation here */
- if (avail_caps > 0)
- irc_send_cmd_now(server, cmd->str);
- else
- cap_finish_negotiation(server);
+ /* If the server doesn't support any cap we requested close the negotiation here */
+ if (avail_caps > 0)
+ irc_send_cmd_now(server, cmd->str);
+ else
+ cap_finish_negotiation(server);
- g_string_free(cmd, TRUE);
+ g_string_free(cmd, TRUE);
+ }
}
}
- else if (!g_strcmp0(evt, "ACK")) {
+ else if (!g_ascii_strcasecmp(evt, "ACK")) {
int got_sasl = FALSE;
/* Emit a signal for every ack'd cap */
@@ -141,11 +211,11 @@ static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *add
disable = (*caps[i] == '-');
if (disable)
- server->cap_active = gslist_remove_string(server->cap_active, caps[i] + 1);
+ server->cap_active = gslist_delete_string(server->cap_active, caps[i] + 1, g_free);
else
server->cap_active = g_slist_prepend(server->cap_active, g_strdup(caps[i]));
- if (!g_strcmp0(caps[i], "sasl"))
+ if (!strcmp(caps[i], "sasl"))
got_sasl = TRUE;
cap_emit_signal(server, "ack", caps[i]);
@@ -157,7 +227,7 @@ static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *add
if (got_sasl == FALSE)
cap_finish_negotiation(server);
}
- else if (!g_strcmp0(evt, "NAK")) {
+ else if (!g_ascii_strcasecmp(evt, "NAK")) {
g_warning("The server answered with a NAK to our CAP request, this should not happen");
/* A NAK'd request means that a required cap can't be enabled or disabled, don't update the
@@ -165,6 +235,42 @@ static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *add
for (i = 0; i < caps_length; i++)
cap_emit_signal(server, "nak", caps[i]);
}
+ else if (!g_ascii_strcasecmp(evt, "NEW")) {
+ for (i = 0; i < caps_length; i++) {
+ char *key, *val;
+
+ if (!parse_cap_name(caps[i], &key, &val)) {
+ g_warning("Invalid CAP %s key/value pair", evt);
+ continue;
+ }
+
+ g_hash_table_insert(server->cap_supported, key, val);
+ cap_emit_signal(server, "new", key);
+ }
+ }
+ else if (!g_ascii_strcasecmp(evt, "DEL")) {
+ for (i = 0; i < caps_length; i++) {
+ char *key, *val;
+
+ if (!parse_cap_name(caps[i], &key, &val)) {
+ g_warning("Invalid CAP %s key/value pair", evt);
+ continue;
+ }
+
+ g_hash_table_remove(server->cap_supported, key);
+ cap_emit_signal(server, "delete", key);
+ /* The server removed this CAP, remove it from the list
+ * of the active ones if we had requested it */
+ server->cap_active = gslist_delete_string(server->cap_active, key, g_free);
+ /* We don't transfer the ownership of those two
+ * variables this time, just free them when we're done. */
+ g_free(key);
+ g_free(val);
+ }
+ }
+ else {
+ g_warning("Unhandled CAP subcommand %s", evt);
+ }
g_strfreev(caps);
g_free(params);
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index 4eaab712..e154d17f 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -443,8 +443,10 @@ static void sig_disconnected(IRC_SERVER_REC *server)
gslist_free_full(server->cap_active, (GDestroyNotify) g_free);
server->cap_active = NULL;
- gslist_free_full(server->cap_supported, (GDestroyNotify) g_free);
- server->cap_supported = NULL;
+ if (server->cap_supported) {
+ g_hash_table_destroy(server->cap_supported);
+ server->cap_supported = NULL;
+ }
gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
server->cap_queue = NULL;
diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h
index 09f3f81d..1374e846 100644
--- a/src/irc/core/irc-servers.h
+++ b/src/irc/core/irc-servers.h
@@ -68,6 +68,7 @@ struct _IRC_SERVER_REC {
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 */
+ unsigned int cap_in_multiline:1; /* We're waiting for the multiline response to end */
unsigned int sasl_success:1; /* Did we authenticate successfully ? */
int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */
@@ -75,7 +76,7 @@ struct _IRC_SERVER_REC {
int max_whois_in_cmd; /* max. number of nicks in one /WHOIS command */
int max_msgs_in_cmd; /* max. number of targets in one /MSG */
- GSList *cap_supported; /* A list of caps supported by the server */
+ GHashTable *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 */
diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs
index 41690010..3bf81f9a 100644
--- a/src/perl/irc/Irc.xs
+++ b/src/perl/irc/Irc.xs
@@ -12,7 +12,10 @@ 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)
{
AV *av;
+ HV *hv_;
GSList *tmp;
+ GHashTableIter iter;
+ gpointer key_, val_;
perl_irc_connect_fill_hash(hv, server->connrec);
perl_server_fill_hash(hv, (SERVER_REC *) server);
@@ -34,10 +37,16 @@ static void perl_irc_server_fill_hash(HV *hv, IRC_SERVER_REC *server)
(void) hv_store(hv, "cap_complete", 12, newSViv(server->cap_complete), 0);
(void) hv_store(hv, "sasl_success", 12, newSViv(server->sasl_success), 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);
+ if (server->cap_supported != NULL) {
+ hv_ = newHV();
+ g_hash_table_iter_init(&iter, server->cap_supported);
+ while (g_hash_table_iter_next(&iter, &key_, &val_)) {
+ char *key = (char *)key_;
+ char *val = (char *)val_;
+ hv_store(hv_, key, strlen(key), new_pv(val), 0);
+ }
+ (void) hv_store(hv, "cap_supported", 13, newRV_noinc((SV*)hv_), 0);
+ }
av = newAV();
for (tmp = server->cap_active; tmp != NULL; tmp = tmp->next)
diff --git a/src/perl/textui/TextUI.xs b/src/perl/textui/TextUI.xs
index 12732e3f..e2f162a0 100644
--- a/src/perl/textui/TextUI.xs
+++ b/src/perl/textui/TextUI.xs
@@ -124,6 +124,74 @@ gui_input_set(str)
CODE:
gui_entry_set_text(active_entry, str);
+void
+gui_input_set_extent(pos, text)
+ int pos
+ char *text
+PREINIT:
+ char *tt;
+CODE:
+ tt = text != NULL ? format_string_expand(text, NULL) : NULL;
+ gui_entry_set_extent(active_entry, pos, tt);
+ g_free(tt);
+
+void
+gui_input_set_extents(pos, len, left, right)
+ int pos
+ int len
+ char *left
+ char *right
+PREINIT:
+ char *tl;
+ char *tr;
+CODE:
+ tl = left != NULL ? format_string_expand(left, NULL) : NULL;
+ tr = right != NULL ? format_string_expand(right, NULL) : NULL;
+ gui_entry_set_extents(active_entry, pos, len, tl, tr);
+ g_free(tl);
+ g_free(tr);
+
+void
+gui_input_clear_extents(pos, len = 0)
+ int pos
+ int len
+CODE:
+ gui_entry_clear_extents(active_entry, pos, len);
+
+void
+gui_input_get_extent(pos)
+ int pos
+PREINIT:
+ char *ret;
+PPCODE:
+ ret = gui_entry_get_extent(active_entry, pos);
+ XPUSHs(sv_2mortal(new_pv(ret)));
+ g_free(ret);
+
+void
+gui_input_get_text_and_extents()
+PREINIT:
+ GSList *ret, *tmp;
+PPCODE:
+ ret = gui_entry_get_text_and_extents(active_entry);
+ for (tmp = ret; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(new_pv(tmp->data)));
+ }
+ g_slist_free_full(ret, g_free);
+
+void
+gui_input_set_text_and_extents(...)
+PREINIT:
+ GSList *list;
+ int i;
+PPCODE:
+ list = NULL;
+ for (i = items; i > 0; i--) {
+ list = g_slist_prepend(list, SvPV_nolen(ST(i-1)));
+ }
+ gui_entry_set_text_and_extents(active_entry, list);
+ g_slist_free(list);
+
int
gui_input_get_pos()
CODE: