Merged 0.7.99 irssi.
authorPekka Riikonen <priikone@silcnet.org>
Sun, 10 Feb 2002 13:58:05 +0000 (13:58 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sun, 10 Feb 2002 13:58:05 +0000 (13:58 +0000)
247 files changed:
CHANGES
apps/irssi/AUTHORS [new file with mode: 0644]
apps/irssi/COPYING
apps/irssi/Makefile.am
apps/irssi/acconfig.h
apps/irssi/autogen.sh
apps/irssi/config [deleted file]
apps/irssi/configure.in
apps/irssi/curses.m4 [new file with mode: 0644]
apps/irssi/default.theme
apps/irssi/docs/.cvsignore [new file with mode: 0644]
apps/irssi/docs/botnet.txt [new file with mode: 0644]
apps/irssi/docs/crash.txt [new file with mode: 0644]
apps/irssi/docs/design.txt [new file with mode: 0644]
apps/irssi/docs/formats.txt
apps/irssi/docs/help/in/invitelist.in [new file with mode: 0644]
apps/irssi/docs/help/in/perlflush.in [new file with mode: 0644]
apps/irssi/docs/manual.txt
apps/irssi/docs/perl.txt [new file with mode: 0644]
apps/irssi/docs/proxy.txt [new file with mode: 0644]
apps/irssi/docs/signals.txt [new file with mode: 0644]
apps/irssi/docs/special_vars.txt [new file with mode: 0644]
apps/irssi/docs/startup-HOWTO.html
apps/irssi/docs/startup-HOWTO.txt [new file with mode: 0644]
apps/irssi/irssi-config.in
apps/irssi/irssi-icon.png [new file with mode: 0644]
apps/irssi/irssi.spec.in
apps/irssi/scripts/.cvsignore [new file with mode: 0644]
apps/irssi/scripts/Makefile.am [new file with mode: 0644]
apps/irssi/scripts/autoop.pl [new file with mode: 0644]
apps/irssi/scripts/autorejoin.pl [new file with mode: 0644]
apps/irssi/scripts/clones.pl [new file with mode: 0644]
apps/irssi/scripts/hello.pl [new file with mode: 0644]
apps/irssi/scripts/mlock.pl [new file with mode: 0644]
apps/irssi/scripts/privmsg.pl [new file with mode: 0644]
apps/irssi/scripts/quitmsg.pl [new file with mode: 0644]
apps/irssi/scripts/realname.pl [new file with mode: 0644]
apps/irssi/silc.conf [new file with mode: 0644]
apps/irssi/src/Makefile.am
apps/irssi/src/common.h
apps/irssi/src/core/.cvsignore [new file with mode: 0644]
apps/irssi/src/core/Makefile.am
apps/irssi/src/core/args.c
apps/irssi/src/core/channel-rec.h
apps/irssi/src/core/channels-setup.c
apps/irssi/src/core/channels.c
apps/irssi/src/core/chat-commands.c
apps/irssi/src/core/chat-protocols.c
apps/irssi/src/core/chat-protocols.h
apps/irssi/src/core/chatnets.c
apps/irssi/src/core/commands.c
apps/irssi/src/core/commands.h
apps/irssi/src/core/core.c
apps/irssi/src/core/core.h
apps/irssi/src/core/expandos.c
apps/irssi/src/core/expandos.h
apps/irssi/src/core/ignore.c
apps/irssi/src/core/levels.c
apps/irssi/src/core/log-away.c [new file with mode: 0644]
apps/irssi/src/core/log.c
apps/irssi/src/core/log.h
apps/irssi/src/core/memdebug.c [deleted file]
apps/irssi/src/core/memdebug.h [deleted file]
apps/irssi/src/core/misc.c
apps/irssi/src/core/misc.h
apps/irssi/src/core/modules-load.c [new file with mode: 0644]
apps/irssi/src/core/modules-load.h [new file with mode: 0644]
apps/irssi/src/core/modules.c
apps/irssi/src/core/modules.h
apps/irssi/src/core/net-disconnect.c
apps/irssi/src/core/net-sendbuffer.c
apps/irssi/src/core/net-sendbuffer.h
apps/irssi/src/core/network.c
apps/irssi/src/core/nick-rec.h
apps/irssi/src/core/nicklist.c
apps/irssi/src/core/pidwait.c
apps/irssi/src/core/pidwait.h
apps/irssi/src/core/queries.c
apps/irssi/src/core/rawlog.c
apps/irssi/src/core/server-connect-rec.h
apps/irssi/src/core/server-rec.h
apps/irssi/src/core/server-setup-rec.h
apps/irssi/src/core/servers-reconnect.c
apps/irssi/src/core/servers-reconnect.h
apps/irssi/src/core/servers-redirect.c [deleted file]
apps/irssi/src/core/servers-redirect.h [deleted file]
apps/irssi/src/core/servers-setup.c
apps/irssi/src/core/servers-setup.h
apps/irssi/src/core/servers.c
apps/irssi/src/core/servers.h
apps/irssi/src/core/session.c [new file with mode: 0644]
apps/irssi/src/core/session.h [new file with mode: 0644]
apps/irssi/src/core/settings.c
apps/irssi/src/core/settings.h
apps/irssi/src/core/signals.c
apps/irssi/src/core/special-vars.c
apps/irssi/src/core/special-vars.h
apps/irssi/src/core/window-item-rec.h
apps/irssi/src/core/write-buffer.c
apps/irssi/src/fe-common/core/.cvsignore [new file with mode: 0644]
apps/irssi/src/fe-common/core/Makefile.am
apps/irssi/src/fe-common/core/autorun.c
apps/irssi/src/fe-common/core/autorun.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/chat-completion.c
apps/irssi/src/fe-common/core/command-history.c
apps/irssi/src/fe-common/core/command-history.h
apps/irssi/src/fe-common/core/completion.c
apps/irssi/src/fe-common/core/completion.h
apps/irssi/src/fe-common/core/fe-channels.c
apps/irssi/src/fe-common/core/fe-channels.h
apps/irssi/src/fe-common/core/fe-common-core.c
apps/irssi/src/fe-common/core/fe-core-commands.c
apps/irssi/src/fe-common/core/fe-exec.c
apps/irssi/src/fe-common/core/fe-exec.h
apps/irssi/src/fe-common/core/fe-expandos.c
apps/irssi/src/fe-common/core/fe-ignore.c
apps/irssi/src/fe-common/core/fe-log.c
apps/irssi/src/fe-common/core/fe-messages.c
apps/irssi/src/fe-common/core/fe-modules.c
apps/irssi/src/fe-common/core/fe-queries.c
apps/irssi/src/fe-common/core/fe-server.c
apps/irssi/src/fe-common/core/fe-settings.c
apps/irssi/src/fe-common/core/fe-windows.c
apps/irssi/src/fe-common/core/fe-windows.h
apps/irssi/src/fe-common/core/formats.c
apps/irssi/src/fe-common/core/formats.h
apps/irssi/src/fe-common/core/hilight-text.c
apps/irssi/src/fe-common/core/keyboard.c
apps/irssi/src/fe-common/core/module-formats.c
apps/irssi/src/fe-common/core/module-formats.h
apps/irssi/src/fe-common/core/printtext.c
apps/irssi/src/fe-common/core/printtext.h
apps/irssi/src/fe-common/core/themes.c
apps/irssi/src/fe-common/core/themes.h
apps/irssi/src/fe-common/core/translation.c
apps/irssi/src/fe-common/core/window-commands.c
apps/irssi/src/fe-common/core/window-items.c
apps/irssi/src/fe-common/core/windows-layout.c
apps/irssi/src/fe-text/.cvsignore [new file with mode: 0644]
apps/irssi/src/fe-text/Makefile.am
apps/irssi/src/fe-text/gui-entry.c
apps/irssi/src/fe-text/gui-entry.h
apps/irssi/src/fe-text/gui-expandos.c
apps/irssi/src/fe-text/gui-printtext.c
apps/irssi/src/fe-text/gui-printtext.h
apps/irssi/src/fe-text/gui-readline.c
apps/irssi/src/fe-text/gui-readline.h
apps/irssi/src/fe-text/gui-windows.c
apps/irssi/src/fe-text/gui-windows.h
apps/irssi/src/fe-text/lastlog.c
apps/irssi/src/fe-text/mainwindows-layout.c [new file with mode: 0644]
apps/irssi/src/fe-text/mainwindows.c
apps/irssi/src/fe-text/mainwindows.h
apps/irssi/src/fe-text/module-formats.c
apps/irssi/src/fe-text/module-formats.h
apps/irssi/src/fe-text/module.h
apps/irssi/src/fe-text/silc.c
apps/irssi/src/fe-text/statusbar-config.c [new file with mode: 0644]
apps/irssi/src/fe-text/statusbar-config.h [new file with mode: 0644]
apps/irssi/src/fe-text/statusbar-items.c
apps/irssi/src/fe-text/statusbar.c
apps/irssi/src/fe-text/statusbar.h
apps/irssi/src/fe-text/term-curses.c [new file with mode: 0644]
apps/irssi/src/fe-text/term-dummy.c [new file with mode: 0644]
apps/irssi/src/fe-text/term-terminfo.c [new file with mode: 0644]
apps/irssi/src/fe-text/term.c [new file with mode: 0644]
apps/irssi/src/fe-text/term.h [new file with mode: 0644]
apps/irssi/src/fe-text/terminfo-core.c [new file with mode: 0644]
apps/irssi/src/fe-text/terminfo-core.h [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-commands.c
apps/irssi/src/fe-text/textbuffer-reformat.c [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-reformat.h [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-view.c
apps/irssi/src/fe-text/textbuffer-view.h
apps/irssi/src/fe-text/textbuffer.c
apps/irssi/src/fe-text/textbuffer.h
apps/irssi/src/fe-text/tparm.c [new file with mode: 0644]
apps/irssi/src/fe-text/utf8.c [new file with mode: 0644]
apps/irssi/src/fe-text/utf8.h [new file with mode: 0644]
apps/irssi/src/lib-config/.cvsignore [new file with mode: 0644]
apps/irssi/src/lib-config/get.c
apps/irssi/src/lib-config/iconfig.h
apps/irssi/src/lib-config/parse.c
apps/irssi/src/lib-config/write.c
apps/irssi/src/lib-popt/popt.c
apps/irssi/src/lib-popt/poptconfig.c
apps/irssi/src/lib-popt/popthelp.c
apps/irssi/src/lib-popt/poptparse.c
apps/irssi/src/perl/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/Makefile.am [new file with mode: 0644]
apps/irssi/src/perl/common/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/common/Channel.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Core.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Ignore.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Irssi.bs [new file with mode: 0644]
apps/irssi/src/perl/common/Irssi.pm [new file with mode: 0644]
apps/irssi/src/perl/common/Irssi.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Log.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Makefile.PL.in [new file with mode: 0644]
apps/irssi/src/perl/common/Masks.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Query.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Rawlog.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Server.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Settings.xs [new file with mode: 0644]
apps/irssi/src/perl/common/module.h [new file with mode: 0644]
apps/irssi/src/perl/common/typemap [new file with mode: 0644]
apps/irssi/src/perl/get-signals.pl [new file with mode: 0755]
apps/irssi/src/perl/irssi-core.pl [new file with mode: 0644]
apps/irssi/src/perl/irssi-core.pl.h [new file with mode: 0644]
apps/irssi/src/perl/libperl_dynaloader.la [new file with mode: 0644]
apps/irssi/src/perl/libperl_orig.la [new file with mode: 0644]
apps/irssi/src/perl/module-fe.h [new file with mode: 0644]
apps/irssi/src/perl/module-formats.c [new file with mode: 0644]
apps/irssi/src/perl/module-formats.h [new file with mode: 0644]
apps/irssi/src/perl/module.h [new file with mode: 0644]
apps/irssi/src/perl/perl-common.c [new file with mode: 0644]
apps/irssi/src/perl/perl-common.h [new file with mode: 0644]
apps/irssi/src/perl/perl-core.c [new file with mode: 0644]
apps/irssi/src/perl/perl-core.h [new file with mode: 0644]
apps/irssi/src/perl/perl-fe.c [new file with mode: 0644]
apps/irssi/src/perl/perl-signals-list.h [new file with mode: 0644]
apps/irssi/src/perl/perl-signals.c [new file with mode: 0644]
apps/irssi/src/perl/perl-signals.h [new file with mode: 0644]
apps/irssi/src/perl/perl-sources.c [new file with mode: 0644]
apps/irssi/src/perl/perl-sources.h [new file with mode: 0644]
apps/irssi/src/perl/textui/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/textui/Makefile.PL.in [new file with mode: 0644]
apps/irssi/src/perl/textui/Statusbar.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/TextBuffer.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/TextBufferView.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/TextUI.pm [new file with mode: 0644]
apps/irssi/src/perl/textui/TextUI.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/module.h [new file with mode: 0644]
apps/irssi/src/perl/textui/typemap [new file with mode: 0644]
apps/irssi/src/perl/ui/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/ui/Formats.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/Makefile.PL.in [new file with mode: 0644]
apps/irssi/src/perl/ui/Themes.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/UI.pm [new file with mode: 0644]
apps/irssi/src/perl/ui/UI.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/Window.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/module.h [new file with mode: 0644]
apps/irssi/src/perl/ui/typemap [new file with mode: 0644]
apps/irssi/src/silc/core/silc-core.c
apps/irssi/src/silc/core/silc-servers.c
configure.in.pre
prepare

diff --git a/CHANGES b/CHANGES
index aac227279abb865df1b5d8b6de85826289bb2278..4ba77bb01bc561b5b48c01fe404b053004094d43 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+Sun Feb 10 15:48:38 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Merged new irssi from irssi.org's CVS, the version 0.7.99.
+
 Sat Feb  9 14:54:33 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 
        * Allow zero length channel messages inside the Channel Message
 Sat Feb  9 14:54:33 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 
        * Allow zero length channel messages inside the Channel Message
diff --git a/apps/irssi/AUTHORS b/apps/irssi/AUTHORS
new file mode 100644 (file)
index 0000000..b82b08a
--- /dev/null
@@ -0,0 +1 @@
+Timo Sirainen, tss@iki.fi
index d60c31a97a544b53039088d14fe9114583c0efc3..60549be514af76c5db0c17ce6bbe01b2f81e2d9e 100644 (file)
@@ -2,7 +2,7 @@
                       Version 2, June 1991
 
  Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                       Version 2, June 1991
 
  Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
     <one line to give the program's name and a brief idea of what it does.>
 the "copyright" line and a pointer to where the full notice is found.
 
     <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
+    Copyright (C) 19yy  <name of author>
 
     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
 
     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
@@ -313,7 +313,7 @@ Also add information on how to contact you by electronic and paper mail.
 If the program is interactive, make it output a short notice like this
 when it starts in an interactive mode:
 
 If the program is interactive, make it output a short notice like this
 when it starts in an interactive mode:
 
-    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision version 69, Copyright (C) 19yy name of author
     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
index a8a1eff4a59bbf8e340cab5ba9030bb94e7d613d..5d3888cf8347bad23e908322ef00bf600215eaf8 100644 (file)
@@ -1,29 +1,43 @@
 # create default-config.h
 config.h: default-config.h default-theme.h
 
 # create default-config.h
 config.h: default-config.h default-theme.h
 
-default-config.h: $(srcdir)/config
-       $(srcdir)/file2header.sh $(srcdir)/config default_config > default-config.h
+default-config.h: $(srcdir)/silc.conf
+       $(srcdir)/file2header.sh $(srcdir)/silc.conf default_config > default-config.h
 default-theme.h: $(srcdir)/default.theme
        $(srcdir)/file2header.sh $(srcdir)/default.theme default_theme > default-theme.h
 
 default-theme.h: $(srcdir)/default.theme
        $(srcdir)/file2header.sh $(srcdir)/default.theme default_theme > default-theme.h
 
-SUBDIRS = src docs
+if BUILD_PLUGINS
+PLUGINS=plugins
+endif
+if BUILD_SERVERTEST
+SERVERTEST=servertest
+endif
+
+SUBDIRS = src docs scripts
 
 include $(top_srcdir)/Makefile.defines.in
 
 
 include $(top_srcdir)/Makefile.defines.in
 
-#confdir = $(sysconfdir)/irssi
+#confdir = $(sysconfdir)
 confdir = $(silc_etcdir)
 confdir = $(silc_etcdir)
-conf_DATA = config default.theme
+conf_DATA = silc.conf
+
+themedir = $(datadir)/irssi/themes
+theme_DATA = default.theme
 
 
-noinst_HEADERS = irssi-version.h
+noinst_HEADERS = irssi-version.h.in
 
 EXTRA_DIST = \
        autogen.sh \
 
 EXTRA_DIST = \
        autogen.sh \
+       curses.m4 \
        README \
        README \
+       README.cygwin \
        file2header.sh \
        irssi.spec \
        irssi.spec.in \
        $(conf_DATA) \
        file2header.sh \
        irssi.spec \
        irssi.spec.in \
        $(conf_DATA) \
+       $(theme_DATA) \
        irssi-config.in \
        irssi-config.in \
+       irssi-icon.png \
        Makefile.defines.in \
        Makefile.defines_int.in
 
        Makefile.defines.in \
        Makefile.defines_int.in
 
index 753505964ac99bb768cd9dd566a8db7758278a4e..33f02000a4bb60cae063148eac3a4eca25b364e5 100644 (file)
@@ -4,7 +4,6 @@
 #undef PLUGINSDIR
 
 /* misc.. */
 #undef PLUGINSDIR
 
 /* misc.. */
-#undef MEM_DEBUG
 #undef HAVE_IPV6
 #undef HAVE_POPT_H
 #undef HAVE_SOCKS_H
 #undef HAVE_IPV6
 #undef HAVE_POPT_H
 #undef HAVE_SOCKS_H
 #undef SCO_FLAVOR
 
 /* our own curses checks */
 #undef SCO_FLAVOR
 
 /* our own curses checks */
-#undef USE_CURSES_WINDOWS
 #undef HAVE_NCURSES_USE_DEFAULT_COLORS
 #undef HAVE_CURSES_IDCOK
 #undef HAVE_CURSES_RESIZETERM
 #undef HAVE_CURSES_WRESIZE
 #undef HAVE_NCURSES_USE_DEFAULT_COLORS
 #undef HAVE_CURSES_IDCOK
 #undef HAVE_CURSES_RESIZETERM
 #undef HAVE_CURSES_WRESIZE
-#undef USE_CURSES_WINDOWS
+
+/* terminfo/termcap */
+#undef HAVE_TERMINFO
 
 /* nls */
 #undef ENABLE_NLS
 
 /* nls */
 #undef ENABLE_NLS
index d968658d38002ab464b0a03f81c36004d9aaa722..5ad69a2724375d696038fbb29bc27b8967ea7a45 100755 (executable)
@@ -15,10 +15,6 @@ fi
 # get versions
 version_date=`date +%Y%m%d`
 
 # get versions
 version_date=`date +%Y%m%d`
 
-echo "/* automatically created by autogen.sh */" > irssi-version.h.in
-echo "#define IRSSI_VERSION \"@VERSION@\"" >> irssi-version.h.in
-echo "#define IRSSI_VERSION_DATE \"$version_date\"" >> irssi-version.h.in
-
 # create help files
 echo "Creating help files..."
 perl syntax.pl
 # create help files
 echo "Creating help files..."
 perl syntax.pl
@@ -80,6 +76,13 @@ if test "$DIE" -eq 1; then
   exit 1
 fi
 
   exit 1
 fi
 
+#if test -z "$*"; then
+#  echo "**Warning**: I am going to run \`configure' with no arguments."
+#  echo "If you wish to pass any to it, please specify them on the"
+#  echo \`$0\'" command line."
+#  echo
+#fi
+
 case $CC in
 xlc )
   am_opt=--include-deps;;
 case $CC in
 xlc )
   am_opt=--include-deps;;
@@ -88,9 +91,9 @@ esac
 rm -f aclocal.m4
 if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
   echo "Running libtoolize..."
 rm -f aclocal.m4
 if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
   echo "Running libtoolize..."
-  libtoolize --copy --force
+  libtoolize --force --copy
 fi
 fi
-aclocalinclude="$ACLOCAL_FLAGS"
+aclocalinclude="$ACLOCAL_FLAGS -I ."
 echo "Running aclocal $aclocalinclude ..."
 aclocal $aclocalinclude
 if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
 echo "Running aclocal $aclocalinclude ..."
 aclocal $aclocalinclude
 if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
@@ -99,5 +102,15 @@ if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
 fi
 echo "Running autoconf ..."
 autoconf
 fi
 echo "Running autoconf ..."
 autoconf
-echo "Running automake $am_opt ..."
-automake --add-missing --foreign $am_opt
+echo "Running automake --gnu $am_opt ..."
+automake --add-missing --gnu $am_opt
+
+#conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
+
+#if test x$NOCONFIGURE = x; then
+#  echo Running $srcdir/configure $conf_flags "$@" ...
+#  $srcdir/configure $conf_flags "$@" \
+#  && echo Now type \`make\' to compile $PKG_NAME || exit 1
+#else
+#  echo Skipping configure process.
+#fi
diff --git a/apps/irssi/config b/apps/irssi/config
deleted file mode 100644 (file)
index 8bec49b..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-servers = (
-  { address = "silc.silcnet.org"; chatnet = SILCNet; port = 706; }
-  { address = "silc.ytti.fi"; chatnet = SILCNet; port = 706; }
-  { address = "silc.peelo.com"; chatnet = SILCNet; port = 706; }
-  { address = "silc.silcnet.org"; chatnet = SILCNet; port = 707; }
-);
-
-chatnets = {
-  SILCNet = { type = "SILC"; };
-};
-
-channels = (
-  { name = "#silc"; chatnet = silcnet; autojoin = No; }
-);
-
-aliases = {
-  JOIN = "join -window";
-  QUERY = "query -window";
-  LEAVE = "part";
-  BYE = "quit";
-  EXIT = "quit";
-  SIGNOFF = "quit";
-  DESCRIBE = "action";
-  DATE = "time";
-  HOST = "userhost";
-  LAST = "lastlog";
-  SAY = "msg *";
-  WHO = "users *";
-  WI = "whois";
-  WII = "whois $0 $0";
-  WW = "whowas";
-  W = "who";
-  N = "names";
-  M = "msg";
-  T = "topic";
-  C = "clear";
-  CL = "clear";
-  K = "kick";
-  KB = "kickban";
-  KN = "knockout";
-  BANS = "ban";
-  B = "ban";
-  MUB = "unban *";
-  UB = "unban";
-  IG = "ignore";
-  UNIG = "unignore";
-  SB = "scrollback";
-  WC = "window close";
-  WN = "window new hide";
-  GOTO = "sb goto";
-  CHAT = "dcc chat";
-  ADMIN = "info";
-};
-
-settings = {
-  "fe-common/core" = {
-    autocreate_own_query = "no";
-    use_status_window = "no";
-    autoclose_windows = "no";
-    use_msgs_window = "no";
-    autocreate_windows = "no";
-    autocreate_query_level = "none";
-  };
-  "fe-text" = { topicbar = "no"; mail_counter = "yes"; indent = "8"; };
-};
index dfc21b47d04a45fa86f34b928f9fd501ef7aa5f6..dfa63c847734d9d8880b89d98417c3937375c0b2 100644 (file)
@@ -1,7 +1,13 @@
 AC_INIT(src)
 
 AC_INIT(src)
 
+# we don't want VERSION in our config.h
+if test -n "`grep '^#undef VERSION' config.h.in`"; then
+  grep -v '^#undef VERSION' config.h.in > config.h.in.temp
+  mv -f config.h.in.temp config.h.in
+fi
+
 AM_CONFIG_HEADER(config.h)
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(Irssi-SILC, 0.7.98.3)
+AM_INIT_AUTOMAKE(Irssi-SILC, 0.7.99)
 
 AM_MAINTAINER_MODE
 
 
 AM_MAINTAINER_MODE
 
@@ -9,25 +15,17 @@ AC_ISC_POSIX
 AC_PROG_CC
 AC_PROG_CPP
 AC_STDC_HEADERS
 AC_PROG_CC
 AC_PROG_CPP
 AC_STDC_HEADERS
-AC_ARG_PROGRAM
-AC_PROG_RANLIB
+AM_PROG_LIBTOOL
+
+AC_PATH_PROG(sedpath, sed)
+AC_PATH_PROG(perlpath, perl)
 
 
-dnl * ahem.. :) we don't want static libraries for modules
-if test "x$lt_target" = "x"; then
-        if test "$target" = "NONE"; then
-               lt_target="$host"
-       else
-               lt_target="$target"
-       fi
-fi
 dnl * --disable-static isn't a good idea, complain if it's used
 if test "x$enable_static" = "xno"; then
        AC_ERROR([Don't give --disable-static option to configure])
 fi
 
 dnl * --disable-static isn't a good idea, complain if it's used
 if test "x$enable_static" = "xno"; then
        AC_ERROR([Don't give --disable-static option to configure])
 fi
 
-AC_CONFIG_AUX_DIR(.)
-
-AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h libintl.h)
+AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h sys/resource.h)
 
 # check posix headers..
 AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h)
 
 # check posix headers..
 AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h)
@@ -97,6 +95,18 @@ AC_ARG_WITH(proxy,
        fi,
        want_irssiproxy=no)
 
        fi,
        want_irssiproxy=no)
 
+AC_ARG_WITH(terminfo,
+[  --with-terminfo         Use terminfo directly instead of curses],
+       if test x$withval = xyes; then
+               want_terminfo=yes
+       else
+               if test "x$withval" = xno; then
+                       want_terminfo=no
+               else
+                       want_terminfo=yes
+               fi
+       fi,
+       want_terminfo=yes)
 
 AC_ARG_WITH(modules,
 [  --with-modules          Specify what modules to build in binary],
 
 AC_ARG_WITH(modules,
 [  --with-modules          Specify what modules to build in binary],
@@ -104,39 +114,73 @@ AC_ARG_WITH(modules,
                build_modules="$withval"
        fi)
 
                build_modules="$withval"
        fi)
 
-if test "x$prefix" = "xNONE"; then
-       PERL_LIB_DIR=""
-else
-       PERL_LIB_DIR="$prefix"
+if test "x$prefix" != "xNONE"; then
+        prefix=`eval echo $prefix`
+       PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=$prefix"
+       perl_library_dir="PERL_USE_LIB"
+       perl_set_use_lib=yes
+
+       perl_prefix_note=yes
 fi
 
 fi
 
-AC_ARG_ENABLE(perl-path,
-[  --enable-perl-path=dir  Specify where to install the Perl libraries for irssi],
-       if test x$enableval = xyes; then
-               want_perl=yes
+AC_ARG_WITH(perl-staticlib,
+[  --with-perl-staticlib   Specify that we want to link perl libraries
+                        statically in irssi, default is no],
+       if test x$withval = xyes; then
+               want_staticperllib=yes
        else
        else
-               if test "x$enableval" = xno; then
-                       want_perl=no
+               if test "x$withval" = xno; then
+                       want_staticperllib=no
                else
                else
-                       want_perl=yes
-                       PERL_LIB_DIR="$enableval"
-                       perl_lib_dir_given=yes
+                       want_staticperllib=yes
                fi
        fi,
                fi
        fi,
-       want_perl=no)
+       want_staticperllib=no)
 
 
-AC_ARG_ENABLE(perl,
-[  --enable-perl[=yes|no|static]  Build with Perl support - also specifies
-                          if it should be built into main irssi binary
-                          (static) or as module (default)],
-       if test x$enableval = xyes; then
+
+AC_ARG_WITH(perl-lib,
+[  --with-perl-lib=[site|vendor|DIR]  Specify where to install the
+                        Perl libraries for irssi, default is site],
+       if test "x$withval" = xyes; then
+               want_perl=yes
+       elif test "x$withval" = xno; then
+               want_perl=no
+       elif test "x$withval" = xsite; then
                want_perl=yes
                want_perl=yes
-       elif test x$enableval = xstatic; then
+               perl_prefix_note=no
+               PERL_MM_PARAMS=""
+       elif test "x$withval" = xvendor; then
+               want_perl=yes
+               perl_prefix_note=no
+               if test -z "`$perlpath -v|grep '5\.0'`"; then
+                       PERL_MM_PARAMS="INSTALLDIRS=vendor"
+               else
+                       PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=`perl -e 'use Config; print $Config{prefix}'`"
+               fi
+               perl_library_dir="(vendor default - `$perlpath -e 'use Config; print $Config{archlib}'`)"
+       else
+               want_perl=yes
+               perl_prefix_note=no
+               PERL_MM_PARAMS="INSTALLDIRS=perl LIB=$withval"
+               perl_library_dir="PERL_USE_LIB"
+               perl_set_use_lib=yes
+       fi,
+       want_perl=yes)
+
+AC_ARG_WITH(perl,
+[  --with-perl[=yes|no|module]  Build with Perl support - also specifies
+                        if it should be built into main irssi binary
+                        (static, default) or as module],
+       if test x$withval = xyes; then
                want_perl=static
                want_perl=static
+       elif test x$withval = xstatic; then
+               want_perl=static
+       elif test x$withval = xmodule; then
+               want_perl=module
        else
                want_perl=no
        fi,
        else
                want_perl=no
        fi,
-       want_perl=no)
+       want_perl=static)
 
 AC_ARG_WITH(tests,
 [  --with-tests           Run all the tests],
 
 AC_ARG_WITH(tests,
 [  --with-tests           Run all the tests],
@@ -146,26 +190,6 @@ AC_ARG_WITH(tests,
        TEST_DIR=)
 AC_SUBST(TEST_DIR)
 
        TEST_DIR=)
 AC_SUBST(TEST_DIR)
 
-AC_ARG_ENABLE(curses-windows,
-[  --enable-curses-windows Use curses windows],
-       if test x$enableval != xno; then
-               AC_DEFINE(USE_CURSES_WINDOWS)
-       fi,
-       AC_DEFINE(USE_CURSES_WINDOWS))
-
-AC_ARG_ENABLE(memdebug,
-[  --enable-memdebug       Enable memory debugging],
-       if test x$enableval = xyes; then
-               want_memdebug=yes
-       else
-               if test "x$enableval" = xno; then
-                       want_memdebug=no
-               else
-                       want_memdebug=yes
-               fi
-       fi,
-       want_memdebug=no)
-
 AC_ARG_ENABLE(ipv6,
 [  --enable-ipv6           Enable IPv6 support],
        if test x$enableval = xyes; then
 AC_ARG_ENABLE(ipv6,
 [  --enable-ipv6           Enable IPv6 support],
        if test x$enableval = xyes; then
@@ -183,30 +207,35 @@ dnl **
 dnl ** just some generic stuff...
 dnl **
 
 dnl ** just some generic stuff...
 dnl **
 
+dnl * OS specific options
+case "$host_os" in
+  hpux*)
+    CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED"
+    ;;
+  *)
+    ;;
+esac
+
+
 AC_CHECK_FUNCS(mkfifo fcntl)
 
 AC_CHECK_FUNCS(mkfifo fcntl)
 
-AC_CHECK_LIB(socket, socket, [
-       PROG_LIBS="$PROG_LIBS -lsocket"
+AC_CHECK_FUNC(socket, [], [
+       AC_CHECK_LIB(socket, socket, [
+               LIBS="$LIBS -lsocket"
+       ])
 ])
 
 ])
 
-AC_CHECK_LIB(nsl, inet_addr, [
-       PROG_LIBS="$PROG_LIBS -lnsl"
-], -lsocket)
+AC_CHECK_FUNC(inet_addr, [], [
+       AC_CHECK_LIB(nsl, inet_addr, [
+               LIBS="$LIBS -lnsl"
+       ], -lsocket)
+])
 
 dnl * gcc specific options
 if test "x$ac_cv_prog_gcc" = "xyes"; then
   CFLAGS="$CFLAGS -Wall"
 fi
 
 
 dnl * gcc specific options
 if test "x$ac_cv_prog_gcc" = "xyes"; then
   CFLAGS="$CFLAGS -Wall"
 fi
 
-dnl * OS specific options
-case "$host_os" in
-  hpux*)
-    CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED"
-    ;;
-  *)
-    ;;
-esac
-
 dnl * socklen_t - AC_CHECK_TYPE() would be _really_ useful if it only would
 dnl * accept header files where to find the typedef..
 AC_MSG_CHECKING([for socklen_t])
 dnl * socklen_t - AC_CHECK_TYPE() would be _really_ useful if it only would
 dnl * accept header files where to find the typedef..
 AC_MSG_CHECKING([for socklen_t])
@@ -229,7 +258,7 @@ dnl **
 
 if test "x$want_socks" = "xyes"; then
        AC_CHECK_LIB(socks, connect, [
 
 if test "x$want_socks" = "xyes"; then
        AC_CHECK_LIB(socks, connect, [
-               PROG_LIBS="$PROG_LIBS -lsocks"
+               LIBS="$LIBS -lsocks"
                AC_CHECK_HEADER(socks.h, [
                        AC_DEFINE(HAVE_SOCKS_H)
                        CFLAGS="$CFLAGS -DSOCKS"
                AC_CHECK_HEADER(socks.h, [
                        AC_DEFINE(HAVE_SOCKS_H)
                        CFLAGS="$CFLAGS -DSOCKS"
@@ -245,13 +274,11 @@ dnl **
 dnl ** fe-text checks
 dnl **
 
 dnl ** fe-text checks
 dnl **
 
-AC_PATH_PROG(sedpath, sed)
-
 AC_DEFUN(AC_CHECK_GLIBDIR,[
   AC_MSG_CHECKING([whether GLib is unpacked to irssi dir])
 
   GLIB_DIR=`for d in *; do test -f $d/glib.h && echo $d; done`
 AC_DEFUN(AC_CHECK_GLIBDIR,[
   AC_MSG_CHECKING([whether GLib is unpacked to irssi dir])
 
   GLIB_DIR=`for d in *; do test -f $d/glib.h && echo $d; done`
-  if test "x$GLIB_DIR" != "x"; then
+  if test -n "$GLIB_DIR"; then
     dnl * glib in irssi directory, use it
     AC_MSG_RESULT([yes, using it])
 
     dnl * glib in irssi directory, use it
     AC_MSG_RESULT([yes, using it])
 
@@ -294,9 +321,9 @@ AC_DEFUN(AC_CHECK_GLIBDIR,[
 
 AC_CHECK_GLIBDIR
 
 
 AC_CHECK_GLIBDIR
 
-if test "x$GLIB_DIR" = "x"; then
+if test -z "$GLIB_DIR"; then
   AM_PATH_GLIB(1.2.0,,, gmodule)
   AM_PATH_GLIB(1.2.0,,, gmodule)
-  if test "x$GLIB_LIBS" = "x"; then
+  if test -z "$GLIB_LIBS"; then
     echo "*** trying without -lgmodule"
     glib_config_args=
     AM_PATH_GLIB(1.2.0)
     echo "*** trying without -lgmodule"
     glib_config_args=
     AM_PATH_GLIB(1.2.0)
@@ -304,7 +331,7 @@ if test "x$GLIB_DIR" = "x"; then
     AC_DEFINE(HAVE_GMODULE)
   fi
 
     AC_DEFINE(HAVE_GMODULE)
   fi
 
-  if test "x$GLIB_LIBS" = "x"; then
+  if test -z "$GLIB_LIBS"; then
     echo
     echo "*** If you don't have GLIB, you can get it from ftp://ftp.gtk.org"
     echo "*** If you can't install GLIB anywhere or if you don't want to,"
     echo
     echo "*** If you don't have GLIB, you can get it from ftp://ftp.gtk.org"
     echo "*** If you can't install GLIB anywhere or if you don't want to,"
@@ -319,14 +346,16 @@ if test "x$GLIB_DIR" = "x"; then
     glib_file=glib-1.2.10.tar.gz
 
     dlcmd=
     glib_file=glib-1.2.10.tar.gz
 
     dlcmd=
-    if test "x`ncftpget 2>/dev/null|grep -i ncftp`" != "x"; then
+    if test -n "`ncftpget 2>/dev/null|grep -i ncftp`"; then
       dlcmd="ncftpget ftp://ftp.gtk.org/pub/gtk/v1.2/$glib_file"
     fi
       dlcmd="ncftpget ftp://ftp.gtk.org/pub/gtk/v1.2/$glib_file"
     fi
-    if test "x`wget 2>/dev/null|grep -i wget`" != "x"; then
+    if test -n "`wget 2>/dev/null|grep -i wget`"; then
       dlcmd="wget http://irssi.org/files/$glib_file"
     fi
       dlcmd="wget http://irssi.org/files/$glib_file"
     fi
-    if test "x$dlcmd" != "x"; then
+    if test -n "$dlcmd"; then
       echo "*** I can download GLib for you now. If you don't want to, press CTRL-C now."
       echo "*** I can download GLib for you now. If you don't want to, press CTRL-C now."
+      echo
+      echo "*** Press ENTER to continue"
       read answer
       eval $dlcmd
       if `gunzip $glib_file`; then
       read answer
       eval $dlcmd
       if `gunzip $glib_file`; then
@@ -338,344 +367,109 @@ if test "x$GLIB_DIR" = "x"; then
       fi
     fi
 
       fi
     fi
 
-    if test "x$GLIB_LIBS" = "x"; then
+    if test -z "$GLIB_LIBS"; then
       AC_ERROR([GLIB is required to build irssi.])
     fi
   fi
 fi
 
       AC_ERROR([GLIB is required to build irssi.])
     fi
   fi
 fi
 
-PROG_LIBS="$PROG_LIBS $GLIB_LIBS"
+LIBS="$LIBS $GLIB_LIBS"
 
 dnl **
 
 dnl **
-dnl ** curses checks
+dnl ** check if we can link dynamic libraries to modules
+dnl ** also checks if libraries are built to .libs dir
 dnl **
 
 dnl **
 
-dnl Curses detection: Munged from Midnight Commander's configure.in
-dnl
-dnl What it does:
-dnl =============
-dnl
-dnl - Determine which version of curses is installed on your system
-dnl   and set the -I/-L/-l compiler entries and add a few preprocessor
-dnl   symbols 
-dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that
-dnl   @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in
-dnl   Makefile.in's
-dnl - Modify the following configure variables (these are the only
-dnl   curses.m4 variables you can access from within configure.in)
-dnl   CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if
-dnl                       an ncurses.h that's been renamed to curses.h
-dnl                       is found.
-dnl   CURSES_LIBS       - sets -L and -l's appropriately
-dnl   CFLAGS            - if --with-sco, add -D_SVID3 
-dnl   has_curses        - exports result of tests to rest of configure
-dnl
-dnl Usage:
-dnl ======
-dnl 1) Add lines indicated below to acconfig.h
-dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in
-dnl 3) Instead of #include <curses.h> you should use the following to
-dnl    properly locate ncurses or curses header file
-dnl
-dnl    #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
-dnl    #include <ncurses.h>
-dnl    #else
-dnl    #include <curses.h>
-dnl    #endif
-dnl
-dnl 4) Make sure to add @CURSES_INCLUDEDIR@ to your preprocessor flags
-dnl 5) Make sure to add @CURSES_LIBS@ to your linker flags or LIBS
-dnl
-dnl Notes with automake:
-dnl - call AM_CONDITIONAL(HAS_CURSES, test "$has_curses" = true) from
-dnl   configure.in
-dnl - your Makefile.am can look something like this
-dnl   -----------------------------------------------
-dnl   INCLUDES= blah blah blah $(CURSES_INCLUDEDIR) 
-dnl   if HAS_CURSES
-dnl   CURSES_TARGETS=name_of_curses_prog
-dnl   endif
-dnl   bin_PROGRAMS = other_programs $(CURSES_TARGETS)
-dnl   other_programs_SOURCES = blah blah blah
-dnl   name_of_curses_prog_SOURCES = blah blah blah
-dnl   other_programs_LDADD = blah
-dnl   name_of_curses_prog_LDADD = blah $(CURSES_LIBS)
-dnl   -----------------------------------------------
-dnl
-dnl
-dnl The following lines should be added to acconfig.h:
-dnl ==================================================
-dnl
-dnl /*=== Curses version detection defines ===*/
-dnl /* Found some version of curses that we're going to use */
-dnl #undef HAS_CURSES
-dnl    
-dnl /* Use SunOS SysV curses? */
-dnl #undef USE_SUNOS_CURSES
-dnl 
-dnl /* Use old BSD curses - not used right now */
-dnl #undef USE_BSD_CURSES
-dnl 
-dnl /* Use SystemV curses? */
-dnl #undef USE_SYSV_CURSES
-dnl 
-dnl /* Use Ncurses? */
-dnl #undef USE_NCURSES
-dnl 
-dnl /* If you Curses does not have color define this one */
-dnl #undef NO_COLOR_CURSES
-dnl 
-dnl /* Define if you want to turn on SCO-specific code */
-dnl #undef SCO_FLAVOR
-dnl 
-dnl /* Set to reflect version of ncurses *
-dnl  *   0 = version 1.*
-dnl  *   1 = version 1.9.9g
-dnl  *   2 = version 4.0/4.1 */
-dnl #undef NCURSES_970530
-dnl
-dnl /*=== End new stuff for acconfig.h ===*/
-dnl 
-
-
-AC_DEFUN(AC_CHECK_CURSES,[
-       search_ncurses=true
-       screen_manager=""
-       has_curses=false
-
-       CFLAGS=${CFLAGS--O}
-
-       AC_SUBST(CURSES_LIBS)
-       AC_SUBST(CURSES_INCLUDEDIR)
-
-       AC_ARG_WITH(sco,
-         [  --with-sco              Use this to turn on SCO-specific code],[
-         if test x$withval = xyes; then
-               AC_DEFINE(SCO_FLAVOR)
-               CFLAGS="$CFLAGS -D_SVID3"
-         fi
-       ])
-
-       AC_ARG_WITH(sunos-curses,
-         [  --with-sunos-curses     Used to force SunOS 4.x curses],[
-         if test x$withval = xyes; then
-               AC_USE_SUNOS_CURSES
-         fi
-       ])
-
-       AC_ARG_WITH(osf1-curses,
-         [  --with-osf1-curses      Used to force OSF/1 curses],[
-         if test x$withval = xyes; then
-               AC_USE_OSF1_CURSES
-         fi
-       ])
-
-       AC_ARG_WITH(vcurses,
-         [  --with-vcurses[=incdir] Used to force SysV curses],
-         if test x$withval != xyes; then
-               CURSES_INCLUDEDIR="-I$withval"
-         fi
-         AC_USE_SYSV_CURSES
-       )
-
-       AC_ARG_WITH(ncurses,
-         [  --with-ncurses[=dir]    Compile with ncurses/locate base dir],
-         if test x$withval = xno ; then
-               search_ncurses=false
-         elif test x$withval != xyes ; then
-               AC_NCURSES($withval/include, ncurses.h, -L$withval/lib -lncurses, -I$withval/include, "ncurses on $withval/include")
-         fi
-       )
-
-       if $search_ncurses
-       then
-               AC_SEARCH_NCURSES()
-       fi
-])
+AC_MSG_CHECKING([if we can link dynamic libraries with modules])
+DYNLIB_MODULES=no
 
 
+dnl ** compile object file
+cat > conftest.c <<EOF
+#include <math.h>
+int modfunc(){return (int)floor(1.2);}
+EOF
 
 
-AC_DEFUN(AC_USE_SUNOS_CURSES, [
-       search_ncurses=false
-       screen_manager="SunOS 4.x /usr/5include curses"
-       AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses)
-       AC_DEFINE(USE_SUNOS_CURSES)
-       AC_DEFINE(HAS_CURSES)
-       has_curses=true
-       AC_DEFINE(NO_COLOR_CURSES)
-       AC_DEFINE(USE_SYSV_CURSES)
-       CURSES_INCLUDEDIR="-I/usr/5include"
-       CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a"
-       AC_MSG_RESULT(Please note that some screen refreshs may fail)
-])
-
-AC_DEFUN(AC_USE_OSF1_CURSES, [
-       AC_MSG_RESULT(Using OSF1 curses)
-       search_ncurses=false
-       screen_manager="OSF1 curses"
-       AC_DEFINE(HAS_CURSES)
-       has_curses=true
-       AC_DEFINE(NO_COLOR_CURSES)
-       AC_DEFINE(USE_SYSV_CURSES)
-       CURSES_LIBS="-lcurses"
-])
-
-AC_DEFUN(AC_USE_SYSV_CURSES, [
-       AC_MSG_RESULT(Using SysV curses)
-       AC_DEFINE(HAS_CURSES)
-       has_curses=true
-       AC_DEFINE(USE_SYSV_CURSES)
-       search_ncurses=false
-       screen_manager="SysV/curses"
-       CURSES_LIBS="-lcurses"
-])
-
-dnl AC_ARG_WITH(bsd-curses,
-dnl [--with-bsd-curses         Used to compile with bsd curses, not very fancy],
-dnl    search_ncurses=false
-dnl    screen_manager="Ultrix/cursesX"
-dnl    if test $system = ULTRIX
-dnl    then
-dnl        THIS_CURSES=cursesX
-dnl        else
-dnl        THIS_CURSES=curses
-dnl    fi
-dnl
-dnl    CURSES_LIBS="-l$THIS_CURSES -ltermcap"
-dnl    AC_DEFINE(HAS_CURSES)
-dnl    has_curses=true
-dnl    AC_DEFINE(USE_BSD_CURSES)
-dnl    AC_MSG_RESULT(Please note that some screen refreshs may fail)
-dnl    AC_WARN(Use of the bsdcurses extension has some)
-dnl    AC_WARN(display/input problems.)
-dnl    AC_WARN(Reconsider using xcurses)
-dnl)
-
-       
-dnl
-dnl Parameters: directory filename cureses_LIBS curses_INCLUDEDIR nicename
-dnl
-AC_DEFUN(AC_NCURSES, [
-    if $search_ncurses
-    then
-        if test -f $1/$2
-       then
-           AC_MSG_RESULT(Found ncurses on $1/$2)
-
-           CURSES_LIBS="$3"
-           AC_CHECK_LIB(ncurses, initscr, , [
-                CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'`
-               AC_CHECK_LIB(curses, initscr, [
-                       CURSES_LIBS="$CHECKLIBS"
-               ],, $CHECKLIBS)
-           ], $CURSES_LIBS)
-           CURSES_INCLUDEDIR="$4"
-           search_ncurses=false
-           screen_manager=$5
-            AC_DEFINE(HAS_CURSES)
-            has_curses=true
-           has_ncurses=true
-           AC_DEFINE(USE_NCURSES)
-       fi
-    fi
-])
+./libtool --mode=compile $CC $CFLAGS -c conftest.c 2> /dev/null > /dev/null
+if test ! -s conftest.lo; then
+  AC_ERROR([error compiling test module])
+fi
 
 
-AC_DEFUN(AC_SEARCH_NCURSES, [
-    AC_CHECKING("location of ncurses.h file")
-
-    AC_NCURSES(/usr/include, ncurses.h, -lncurses,, "ncurses on /usr/include")
-    AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses, "ncurses on /usr/include/ncurses")
-    AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include, "ncurses on /usr/local")
-    AC_NCURSES(/usr/pkg/include, ncurses.h, -L/usr/pkg/lib -lncurses, -I/usr/pkg/include, "ncurses on /usr/pkg")
-    AC_NCURSES(/usr/contrib/include, ncurses.h, -L/usr/contrib/lib -lncurses, -I/usr/contrib/include, "ncurses on /usr/contrib")
-    AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -L/usr/local/lib/ncurses -lncurses, -I/usr/local/include/ncurses, "ncurses on /usr/local/include/ncurses")
-
-    AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/local/.../ncurses")
-
-    AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/include/ncurses")
-
-    dnl
-    dnl We couldn't find ncurses, try SysV curses
-    dnl
-    if $search_ncurses 
-    then
-        AC_EGREP_HEADER(init_color, /usr/include/curses.h,
-           AC_USE_SYSV_CURSES)
-       AC_EGREP_CPP(USE_NCURSES,[
-#include <curses.h>
-#ifdef __NCURSES_H
-#undef USE_NCURSES
-USE_NCURSES
-#endif
-],[
-       CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES"
-        AC_DEFINE(HAS_CURSES)
-       has_curses=true
-       has_ncurses=true
-        AC_DEFINE(USE_NCURSES)
-        search_ncurses=false
-        screen_manager="ncurses installed as curses"
-])
-    fi
+dnl ** link to library
+./libtool --mode=link $CC $CFLAGS $LDFLAGS -rpath /usr/lib conftest.lo -lm -o libconftest.la > /dev/null
+if test ! -s .libs/libconftest.a; then
+  AC_ERROR([error, can't even find .a library])
+fi
 
 
-    dnl
-    dnl Try SunOS 4.x /usr/5{lib,include} ncurses
-    dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES
-    dnl should be replaced by a more fine grained selection routine
-    dnl
-    if $search_ncurses
-    then
-       if test -f /usr/5include/curses.h
-       then
-           AC_USE_SUNOS_CURSES
-        fi
+dnl ** check if dynamic linking worked
+libfile=`grep '^library_names' libconftest.la|$sedpath "s/library_names='\(.*\)'.*/\1/"|$sedpath 's/.* \([[^ ]]*\)$/\1/'`
+if test ! -s .libs/$libfile; then
+  AC_MSG_RESULT([no, error linking test module])
+else
+  cat > conftest.c <<EOF
+#include <gmodule.h>
+main() {
+GModule *m; int (*modfunc)(void);
+m = g_module_open(".libs/$libfile", 0);
+if (!m) g_print("error loading: %s", g_module_error());
+else if (!g_module_symbol(m, "modfunc", (gpointer *) &modfunc))
+  g_print("modfunc() symbol not found from module");
+else if (modfunc() == 1) g_print("ok"); else g_print("wrong result?! 1 vs %d", modfunc());
+return 0; }
+EOF
+  $CC $CFLAGS $LDFLAGS conftest.c -o conftest $GLIB_CFLAGS $GLIB_LIBS 2> /dev/null > /dev/null
+  if test ! -s conftest; then
+    AC_MSG_RESULT([no, error compiling test program])
+  else
+    status="`./conftest`"
+    if test "x$status" = "xok"; then
+      DYNLIB_MODULES=yes
+      AC_MSG_RESULT([yes])
+    else
+      AC_MSG_RESULT([no, error running: $status])
     fi
     fi
+  fi
+fi
+rm -rf conftest conftest.* libconftest.* .libs
 
 
-    dnl use whatever curses there happens to be
-    if $search_ncurses
-    then
-       if test -f /usr/include/curses.h
-       then
-         CURSES_LIBS="-lcurses"
-         AC_DEFINE(HAS_CURSES)
-         has_curses=true
-         search_ncurses=false
-         screen_manager="curses"
-       fi
-    fi
-])
+dnl **
+dnl ** curses checks
+dnl **
 
 if test "x$want_textui" = "xyes"; then
        AC_CHECK_CURSES
 
 
 if test "x$want_textui" = "xyes"; then
        AC_CHECK_CURSES
 
-       if test "x$has_ncurses" != "x"; then
-               AC_CHECK_LIB(ncurses, use_default_colors, [
-                       AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(ncurses, idcok, [
-                       AC_DEFINE(HAVE_CURSES_IDCOK)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(ncurses, resizeterm, [
-                       AC_DEFINE(HAVE_CURSES_RESIZETERM)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(ncurses, wresize, [
-                       AC_DEFINE(HAVE_CURSES_WRESIZE)
-               ],, $CURSES_LIBS)
-       elif test "x$has_curses" = "xtrue"; then
-               AC_CHECK_LIB(curses, idcok, [
-                       AC_DEFINE(HAVE_CURSES_IDCOK)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(curses, resizeterm, [
-                       AC_DEFINE(HAVE_CURSES_RESIZETERM)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(curses, wresize, [
-                       AC_DEFINE(HAVE_CURSES_WRESIZE)
-               ],, $CURSES_LIBS)
+       LIBS="$LIBS $CURSES_LIBS"
+       if test "x$has_curses" = "xtrue"; then
+                       AC_CHECK_FUNC(use_default_colors, AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS))
+                       AC_CHECK_FUNC(idcok, AC_DEFINE(HAVE_CURSES_IDCOK))
+                       AC_CHECK_FUNC(resizeterm, AC_DEFINE(HAVE_CURSES_RESIZETERM))
+                       AC_CHECK_FUNC(wresize, AC_DEFINE(HAVE_CURSES_WRESIZE))
+               if test "x$want_terminfo" = "xyes"; then
+                       AC_CHECK_LIB(curses, setupterm,, [
+                               want_termcap=yes
+                       ])
+               fi
        else
        else
-               want_textui=no
-               curses_error=yes
+               AC_CHECK_LIB(tinfo, setupterm, [
+                 LIBS="$LIBS -ltinfo"
+                 want_terminfo=yes
+               ], AC_CHECK_LIB(termlib, tgetent, [
+                 LIBS="$LIBS -ltermlib"
+                 want_termcap=yes
+               ], AC_CHECK_LIB(termcap, tgetent, [
+                 LIBS="$LIBS -ltermcap"
+                 want_termcap=yes
+               ], [
+                 AC_MSG_WARN(Terminfo/termcap not found)
+                 want_textui=no
+                 curses_error=yes
+               ])))
         fi
         fi
-else
-       has_curses=false
+               if test "x$want_termcap" = "xyes"; then
+                       AC_CHECK_FUNC(tparm,, need_tparm=yes)
+       else
+               AC_DEFINE(HAVE_TERMINFO)
+               fi
 fi
 
 dnl **
 fi
 
 dnl **
@@ -683,17 +477,18 @@ dnl ** perl checks
 dnl **
 
 if test "$want_perl" != "no"; then
 dnl **
 
 if test "$want_perl" != "no"; then
-       AC_PATH_PROG(perlpath, perl)
        AC_MSG_CHECKING(for working Perl support)
 
        AC_MSG_CHECKING(for working Perl support)
 
-       if test "x$perlpath" = "x"; then
+       if test -z "$perlpath"; then
                        perl_check_error="perl binary not found"
        else
                PERL_CFLAGS=`$perlpath -MExtUtils::Embed -e ccopts 2>/dev/null`
        fi
 
                        perl_check_error="perl binary not found"
        else
                PERL_CFLAGS=`$perlpath -MExtUtils::Embed -e ccopts 2>/dev/null`
        fi
 
-       if test "x$PERL_CFLAGS" = "x"; then
-               perl_check_error="Error getting perl CFLAGS"
+       if test -z "$PERL_CFLAGS"; then
+               if test -n "$perl_check_error"; then
+                       perl_check_error="Error getting perl CFLAGS"
+               fi
                AC_MSG_RESULT([not found, building without Perl])
                want_perl=no
        else
                AC_MSG_RESULT([not found, building without Perl])
                want_perl=no
        else
@@ -718,10 +513,10 @@ if test "$want_perl" != "no"; then
                fi
 
                dnl * don't check libperl.a if dynaloader.a wasn't found..
                fi
 
                dnl * don't check libperl.a if dynaloader.a wasn't found..
-               if test "x$DYNALOADER_A" != "x"; then
+               if test -n "$DYNALOADER_A"; then
                        dnl * find either libperl.a or libperl.so
                        LIBPERL_A=`echo "$PERL_LDFLAGS -L/usr/lib"|$perlpath -e 'foreach (split(/ /, <STDIN>)) { if (/^-L(.*)/) { my $dir=$1; if (\`ls $dir/libperl.so* 2>/dev/null\`) { print "-lperl"; last; }; if (-e "$dir/libperl.a") { print "$dir/libperl.a"; last } } };'`
                        dnl * find either libperl.a or libperl.so
                        LIBPERL_A=`echo "$PERL_LDFLAGS -L/usr/lib"|$perlpath -e 'foreach (split(/ /, <STDIN>)) { if (/^-L(.*)/) { my $dir=$1; if (\`ls $dir/libperl.so* 2>/dev/null\`) { print "-lperl"; last; }; if (-e "$dir/libperl.a") { print "$dir/libperl.a"; last } } };'`
-                       if test "x$LIBPERL_A" = "x"; then
+                       if test -z "$LIBPERL_A"; then
                                perl_mod_error="Didn't find location of -lperl"
                                DYNALOADER_A=
                        elif test "$LIBPERL_A" = "-lperl"; then
                                perl_mod_error="Didn't find location of -lperl"
                                DYNALOADER_A=
                        elif test "$LIBPERL_A" = "-lperl"; then
@@ -754,7 +549,7 @@ if test "$want_perl" != "no"; then
                dnl * check that perl's ldflags actually work
                AC_CACHE_VAL(irssi_cv_lib_perl_works, [
                        echo "main(){perl_alloc(); return 0;}" > conftest.c
                dnl * check that perl's ldflags actually work
                AC_CACHE_VAL(irssi_cv_lib_perl_works, [
                        echo "main(){perl_alloc(); return 0;}" > conftest.c
-                       $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> /dev/null > /dev/null
+                       $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> perl.error.tmp > /dev/null
                        if test -s conftest; then
                                irssi_cv_lib_perl_works=yes
                        else
                        if test -s conftest; then
                                irssi_cv_lib_perl_works=yes
                        else
@@ -763,22 +558,23 @@ if test "$want_perl" != "no"; then
                ])
 
                if test "x$irssi_cv_lib_perl_works" = "xno"; then
                ])
 
                if test "x$irssi_cv_lib_perl_works" = "xno"; then
-                       perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS"
+                       perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS: `cat perl.error.tmp`"
                        AC_MSG_RESULT([error linking with perl libraries, building without Perl])
                        want_perl=no
                fi
                        AC_MSG_RESULT([error linking with perl libraries, building without Perl])
                        want_perl=no
                fi
+               rm -f perl.error.tmp
        fi
 
        if test "x$want_perl" != "xno"; then
                if test "x$want_perl" = "xstatic"; then
                        AC_MSG_RESULT(ok)
        fi
 
        if test "x$want_perl" != "xno"; then
                if test "x$want_perl" = "xstatic"; then
                        AC_MSG_RESULT(ok)
-               elif test "x$DYNALOADER_A" = "x"; then
+               elif test -z "$DYNALOADER_A"; then
                        AC_MSG_RESULT([error parsing ldopts, building Perl into irssi binary instead of as module])
                        want_perl=static
                else
                        AC_MSG_RESULT(ok)
                        PERL_LDFLAGS=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*[[^ ]]*DynaLoader\.a/\1libperl_dynaloader.la/'`
                        AC_MSG_RESULT([error parsing ldopts, building Perl into irssi binary instead of as module])
                        want_perl=static
                else
                        AC_MSG_RESULT(ok)
                        PERL_LDFLAGS=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*[[^ ]]*DynaLoader\.a/\1libperl_dynaloader.la/'`
-                       if test "x$LIBPERL_A" != "x"; then
+                       if test -n "$LIBPERL_A"; then
                                PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lperl /libperl_orig.la /' -e 's/-lperl$/libperl_orig.la$/'`
                        fi
                        AC_SUBST(LIBPERL_A)
                                PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lperl /libperl_orig.la /' -e 's/-lperl$/libperl_orig.la$/'`
                        fi
                        AC_SUBST(LIBPERL_A)
@@ -788,8 +584,8 @@ if test "$want_perl" != "no"; then
                if test "x$want_perl" = "xstatic"; then
                        dnl * building with static perl support
                        dnl * all PERL_LDFLAGS linking is done in fe-text
                if test "x$want_perl" = "xstatic"; then
                        dnl * building with static perl support
                        dnl * all PERL_LDFLAGS linking is done in fe-text
-                       PERL_LDFLAGS="../perl/libperl_static.la $PERL_LDFLAGS"
-                       PERL_LINK_LIBS="$PERL_LDFLAGS"
+                       PERL_LINK_FLAGS="$PERL_LDFLAGS"
+                       PERL_LINK_LIBS="../perl/libperl_core_static.la"
                        PERL_FE_LINK_LIBS="../perl/libfe_perl_static.la"
                        PERL_LDFLAGS=
                        AC_DEFINE(HAVE_STATIC_PERL)
                        PERL_FE_LINK_LIBS="../perl/libfe_perl_static.la"
                        PERL_LDFLAGS=
                        AC_DEFINE(HAVE_STATIC_PERL)
@@ -797,31 +593,51 @@ if test "$want_perl" != "no"; then
                        dnl * build only static library of perl module
                        perl_module_lib=
                        perl_module_fe_lib=
                        dnl * build only static library of perl module
                        perl_module_lib=
                        perl_module_fe_lib=
-                       perl_static_lib=libperl_static.la
+                       perl_static_lib=libperl_core_static.la
                        perl_static_fe_lib=libfe_perl_static.la
                        PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool'
                else
                        perl_static_fe_lib=libfe_perl_static.la
                        PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool'
                else
-                       dnl * build dynamic library of perl module,
-                       dnl * use libtool-shared to prevent creating of
-                       dnl * libperl.a
+                       dnl * build dynamic library of perl module
                        perl_module_lib=libperl_core.la
                        perl_module_fe_lib=libfe_perl.la
                        perl_static_lib=
                        perl_static_fe_lib=
                        perl_module_lib=libperl_core.la
                        perl_module_fe_lib=libfe_perl.la
                        perl_static_lib=
                        perl_static_fe_lib=
-                       PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool-shared'
+                       PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+               fi
+
+               if test "x$want_staticperllib" = "xyes"; then
+                       PERL_MM_PARAMS="$PERL_MM_PARAMS LINKTYPE=static"
+                       PERL_LINK_LIBS="$PERL_LINK_LIBS ../perl/common/blib/arch/auto/Irssi/Irssi.a ../perl/irc/blib/arch/auto/Irssi/Irc/Irc.a ../perl/ui/blib/arch/auto/Irssi/UI/UI.a ../perl/textui/blib/arch/auto/Irssi/TextUI/TextUI.a"
+                       PERL_STATIC_LIBS=1
+               else
+                       PERL_STATIC_LIBS=0
                fi
                fi
+
+               # figure out the correct @INC path - we'll need to do this
+               # through MakeMaker since it's difficult to get it right
+               # otherwise.
+               if test "x$perl_set_use_lib" = "xyes"; then
+                       perl -e 'use ExtUtils::MakeMaker; WriteMakefile("NAME" => "test", "MAKEFILE" => "Makefile.test");' $PERL_MM_PARAMS >/dev/null
+                       PERL_USE_LIB=`perl -e 'open(F, "Makefile.test"); while (<F>) { chomp; if (/^(\w+) = (.*$)/) { $keys{$1} = $2; } }; $key = $keys{INSTALLARCHLIB}; while ($key =~ /\\$\((\w+)\)/) { $value = $keys{$1}; $key =~ s/\\$\($1\)/$value/; }; print $key;'`
+                       rm -f Makefile.test
+               fi
+
                AC_SUBST(perl_module_lib)
                AC_SUBST(perl_static_lib)
                AC_SUBST(perl_module_fe_lib)
                AC_SUBST(perl_static_fe_lib)
                AC_SUBST(PERL_LIBTOOL)
 
                AC_SUBST(perl_module_lib)
                AC_SUBST(perl_static_lib)
                AC_SUBST(perl_module_fe_lib)
                AC_SUBST(perl_static_fe_lib)
                AC_SUBST(PERL_LIBTOOL)
 
+               AC_SUBST(PERL_LINK_FLAGS)
                AC_SUBST(PERL_LINK_LIBS)
                AC_SUBST(PERL_LINK_LIBS)
-               AC_SUBST(PERL_FE_LINK_LIBS)
+                AC_SUBST(PERL_FE_LINK_LIBS)
 
                AC_SUBST(PERL_LDFLAGS)
                AC_SUBST(PERL_CFLAGS)
 
                AC_SUBST(PERL_LDFLAGS)
                AC_SUBST(PERL_CFLAGS)
-               AC_SUBST(PERL_LIB_DIR)
+
+               AC_SUBST(PERL_USE_LIB)
+               AC_SUBST(PERL_MM_PARAMS)
+               AC_SUBST(PERL_STATIC_LIBS)
        fi
 fi
 
        fi
 fi
 
@@ -830,10 +646,15 @@ AM_CONDITIONAL(BUILD_TEXTUI, test "$want_textui" = "yes")
 AM_CONDITIONAL(BUILD_IRSSIBOT, test "$want_irssibot" = "yes")
 AM_CONDITIONAL(BUILD_IRSSIPROXY, test "$want_irssiproxy" = "yes")
 AM_CONDITIONAL(BUILD_PLUGINS, test "$want_plugins" = "yes")
 AM_CONDITIONAL(BUILD_IRSSIBOT, test "$want_irssibot" = "yes")
 AM_CONDITIONAL(BUILD_IRSSIPROXY, test "$want_irssiproxy" = "yes")
 AM_CONDITIONAL(BUILD_PLUGINS, test "$want_plugins" = "yes")
-AM_CONDITIONAL(BUILD_SERVERTEST, test "x$TEST_DIR" != "x")
+AM_CONDITIONAL(BUILD_SERVERTEST, test -n "$TEST_DIR")
 AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no")
 AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static")
 AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no")
 AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static")
+AM_CONDITIONAL(NEED_TPARM, test "$need_tparm" = "yes")
+AM_CONDITIONAL(USE_CURSES, test "$want_terminfo" != "yes" -a "$want_termcap" != "yes")
 
 
+# move LIBS to PROG_LIBS so they're not tried to be used when linking eg. perl libraries
+PROG_LIBS=$LIBS
+LIBS=
 AC_SUBST(PROG_LIBS)
 
 dnl **
 AC_SUBST(PROG_LIBS)
 
 dnl **
@@ -844,10 +665,9 @@ dnl ** (these could be made configurable)
 
 CHAT_MODULES="silc"
 silc_MODULES=""
 
 CHAT_MODULES="silc"
 silc_MODULES=""
-if test "x$build_modules" != "x"; then
+if test -n "$build_modules"; then
        silc_MODULES="$silc_MODULES $build_modules"
 fi
        silc_MODULES="$silc_MODULES $build_modules"
 fi
-#PROG_LIBS="$PROG_LIBS -lsilc"
 
 dnl ****************************************
 
 
 dnl ****************************************
 
@@ -868,11 +688,11 @@ for c in $CHAT_MODULES; do
                FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/libfe_common_$c.a "
        fi
        for s in `eval echo \\$${c}_MODULES`; do
                FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/libfe_common_$c.a "
        fi
        for s in `eval echo \\$${c}_MODULES`; do
-               CHAT_LIBS="$CHAT_LIBS ../$c/$s/.libs/lib${c}_$s.a"
+               CHAT_LIBS="$CHAT_LIBS ../$c/$s/lib${c}_$s.a"
                module_inits="$module_inits ${c}_${s}_init();"
                module_deinits="${c}_${s}_deinit(); $module_deinits"
                if test -f $srcdir/src/fe-common/$c/$s/module.h; then
                module_inits="$module_inits ${c}_${s}_init();"
                module_deinits="${c}_${s}_deinit(); $module_deinits"
                if test -f $srcdir/src/fe-common/$c/$s/module.h; then
-                       FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/$s/.libs/libfe_${c}_$s.a "
+                       FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/$s/libfe_${c}_$s.a "
                        fe_module_inits="$fe_module_inits fe_${c}_${s}_init();"
                        fe_module_deinits="fe_${c}_${s}_deinit(); $fe_module_deinits"
                fi
                        fe_module_inits="$fe_module_inits fe_${c}_${s}_init();"
                        fe_module_deinits="fe_${c}_${s}_deinit(); $fe_module_deinits"
                fi
@@ -880,19 +700,18 @@ for c in $CHAT_MODULES; do
 
        file="$srcdir/src/$c/$c.c"
         echo "/* this file is automatically generated by configure - don't change */" > $file
 
        file="$srcdir/src/$c/$c.c"
         echo "/* this file is automatically generated by configure - don't change */" > $file
-       echo "void ${c}_core_init(void); void ${c}_core_init_finish(void); void ${c}_core_deinit(void);" >> $file
-       if test "x$module_inits" != "x"; then
+       echo "void ${c}_core_init(void); void ${c}_core_deinit(void);" >> $file
+       if test -n "$module_inits"; then
                echo "$module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
                echo "$module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
        fi
         echo "void ${c}_init(void) { ${c}_core_init(); $module_inits }" >> $file
                echo "$module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
                echo "$module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
        fi
         echo "void ${c}_init(void) { ${c}_core_init(); $module_inits }" >> $file
-        echo "void ${c}_init_finish(void) { ${c}_core_init_finish(); $module_inits }" >> $file
         echo "void ${c}_deinit(void) { $module_deinits ${c}_core_deinit(); }" >> $file
 
        if test -f $srcdir/src/fe-common/$c/module.h; then
                file="$srcdir/src/fe-common/$c/${c}-modules.c"
                echo "/* this file is automatically generated by configure - don't change */" > $file
         echo "void ${c}_deinit(void) { $module_deinits ${c}_core_deinit(); }" >> $file
 
        if test -f $srcdir/src/fe-common/$c/module.h; then
                file="$srcdir/src/fe-common/$c/${c}-modules.c"
                echo "/* this file is automatically generated by configure - don't change */" > $file
-                if test "x$fe_module_inits" != "x"; then
+                if test -n "$fe_module_inits"; then
                        echo "$fe_module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
                        echo "$fe_module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
                fi
                        echo "$fe_module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
                        echo "$fe_module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
                fi
@@ -909,15 +728,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS"
 AC_SUBST(COMMON_NOUI_LIBS)
 AC_SUBST(COMMON_LIBS)
 
 AC_SUBST(COMMON_NOUI_LIBS)
 AC_SUBST(COMMON_LIBS)
 
-dnl **
-dnl ** memory debugging
-dnl **
-
-if test "x$want_memdebug" = "xyes"; then
-       AC_DEFINE(MEM_DEBUG)
-fi
-AM_CONDITIONAL(BUILD_MEMDEBUG, test "x$want_memdebug" = "xyes")
-
 dnl **
 dnl ** tr-Chinese Big5 support
 dnl **
 dnl **
 dnl ** tr-Chinese Big5 support
 dnl **
@@ -931,9 +741,35 @@ dnl ** IPv6 support
 dnl **
 
 if test "x$want_ipv6" = "xyes"; then
 dnl **
 
 if test "x$want_ipv6" = "xyes"; then
-       AC_DEFINE(HAVE_IPV6)
+       AC_MSG_CHECKING([for IPv6])
+       AC_CACHE_VAL(irssi_cv_type_in6_addr,
+       [AC_TRY_COMPILE([
+       #include <sys/types.h>
+       #include <sys/socket.h>
+       #include <netinet/in.h>
+       #include <netdb.h>
+       #include <arpa/inet.h>],
+       [struct in6_addr i;],
+       irssi_cv_type_in6_addr=yes,
+       irssi_cv_type_in6_addr=no,
+       )])
+       if test $irssi_cv_type_in6_addr = yes; then
+               AC_DEFINE(HAVE_IPV6)
+       fi
+       AC_MSG_RESULT($irssi_cv_type_in6_addr)
 fi
 
 fi
 
+dnl **
+dnl ** IRSSI_VERSION_DATE and IRSSI_VERSION_TIME
+dnl **
+#VERSION_DATE=`head -1 $srcdir/ChangeLog|sed 's/^\(....\)-\(..\)-\(..\).*/\1\2\3/'`
+#VERSION_TIME=`head -1 $srcdir/ChangeLog|sed -e 's/^[[^ ]]* \(..\):\(..\).*/\1\2/' -e 's/^0*//'`
+#AC_SUBST(VERSION_DATE)
+#AC_SUBST(VERSION_TIME)
+
+#
+# Glue into SILC build system
+#
 INCLUDE_DEFINES_INT="include \$(top_srcdir)/Makefile.defines_int"
 AC_SUBST(INCLUDE_DEFINES_INT)
 
 INCLUDE_DEFINES_INT="include \$(top_srcdir)/Makefile.defines_int"
 AC_SUBST(INCLUDE_DEFINES_INT)
 
@@ -949,20 +785,30 @@ src/lib-config/Makefile
 src/lib-popt/Makefile
 src/silc/Makefile
 src/silc/core/Makefile
 src/lib-popt/Makefile
 src/silc/Makefile
 src/silc/core/Makefile
+src/perl/Makefile
+src/perl/common/Makefile.PL
+src/perl/ui/Makefile.PL
+src/perl/textui/Makefile.PL
+scripts/Makefile
 docs/Makefile
 docs/help/Makefile
 docs/help/in/Makefile
 docs/Makefile
 docs/help/Makefile
 docs/help/in/Makefile
+irssi-version.h
 stamp.h
 irssi.spec
 stamp.h
 irssi.spec
-irssi-version.h
 irssi-config)
 
 dnl ** for building from objdir
 irssi-config)
 
 dnl ** for building from objdir
-if test "x$want_perl" != "xno"; then
-       old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir
+old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir
+if test "x$old_dir" != "x$whole_dir"; then
+       $LN_S $srcdir/irssi-version.h irssi-version.h
 
 
-       if test "x$old_dir" != "x$whole_dir"; then
-               for file in $whole_dir/src/perl/*.[[ch]] $whole_dir/src/perl/libperl_orig.la $whole_dir/src/perl/libperl_dynaloader.la $whole_dir/src/perl/common/typemap $whole_dir/src/perl/common/module.h $whole_dir/src/perl/common/*.xs $whole_dir/src/perl/irc/typemap $whole_dir/src/perl/irc/module.h $whole_dir/src/perl/irc/*.xs; do
+       if test "x$want_perl" != "xno"; then
+               subdirfiles=""
+               for i in $whole_dir/src/perl/common $whole_dir/src/perl/irc $whole_dir/src/perl/ui $whole_dir/src/perl/textui; do
+                       subdirfiles=`echo $subdirfiles $i/typemap $i/module.h $i/*.pm $i/*.xs`
+               done
+               for file in $whole_dir/src/perl/*.[[ch]] $whole_dir/src/perl/libperl_orig.la $whole_dir/src/perl/libperl_dynaloader.la $subdirfiles; do
                        link=`echo $file|$sedpath "s?$whole_dir/??"`
                        rm -f $link
                        $LN_S $file $link
                        link=`echo $file|$sedpath "s?$whole_dir/??"`
                        rm -f $link
                        $LN_S $file $link
@@ -973,7 +819,16 @@ fi
 echo
 
 if test "x$curses_error" != "xyes"; then
 echo
 
 if test "x$curses_error" != "xyes"; then
-       echo "Building text frontend ..... : $want_textui"
+       if test "x$want_textui" = "xno"; then
+               text=no
+       elif test "x$want_termcap" = "xyes"; then
+               text="yes, using termcap"
+       elif test "x$want_terminfo" = "xyes"; then
+               text="yes, using terminfo"
+       else
+               text="yes, using curses"
+       fi
+       echo "Building text frontend ..... : $text"
 else
        echo "Building text frontend ..... : NO!!"
        echo " - Because curses was not found, specify the path to it with"
 else
        echo "Building text frontend ..... : NO!!"
        echo " - Because curses was not found, specify the path to it with"
@@ -986,10 +841,10 @@ echo "Building with IPv6 support . : $want_ipv6"
 
 if test "x$want_perl" = "xstatic"; then
        echo "Building with Perl support . : static (in irssi binary)"
 
 if test "x$want_perl" = "xstatic"; then
        echo "Building with Perl support . : static (in irssi binary)"
-elif test "x$want_perl" = "xyes"; then
+elif test "x$want_perl" = "xmodule"; then
        echo "Building with Perl support . : module"
 else
        echo "Building with Perl support . : module"
 else
-       if test "x$perl_check_error" = "x"; then
+       if test -z "$perl_check_error"; then
                echo "Building with Perl support . : no"
        else
                echo "Building with Perl support . : NO!"
                echo "Building with Perl support . : no"
        else
                echo "Building with Perl support . : NO!"
@@ -1003,18 +858,20 @@ if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then
        echo "   $perl_mod_error"
 fi
 
        echo "   $perl_mod_error"
 fi
 
-if test "x$want_perl" = "xyes"; then
-       if test "x$PERL_LIB_DIR" = "x"; then
-               echo "Perl library directory ..... : (default - usually /usr/local/lib/perl_site)"
-       else
-               echo "Perl library directory ..... : $PERL_LIB_DIR"
-               if test "x$perl_lib_dir_given" != "xyes"; then
-                       echo " - NOTE: This was automatically set to the same directory you gave with"
-                       echo "   --prefix. If you want the perl libraries to install to their 'correct'"
-                       echo "   path, you'll need to give --enable-perl-path= (nothing after '=') option"
-                       echo "   to configure. Anyway, installing perl to this directory should work"
-                       echo "   just as well.."
-               fi
+if test "x$want_perl" != "xno"; then
+       if test "$perl_library_dir" = "PERL_USE_LIB"; then
+               perl_library_dir=$PERL_USE_LIB
+       fi
+       if test -z "$perl_library_dir"; then
+               perl_library_dir="(site default - `$perlpath -e 'use Config; print $Config{sitearch}'`)"
+       fi
+               echo "Perl library directory ..... : $perl_library_dir"
+       if test "x$perl_prefix_note" = "xyes"; then
+               echo "  - NOTE: This was automatically set to the same directory you gave with"
+               echo "  --prefix. If you want the perl libraries to install to their 'correct'"
+               echo "  path, you'll need to give --with-perl-lib=site option to configure."
+               echo "  Anyway, installing perl to this directory should work just as well."
        fi
 fi
 echo "Install prefix ............. : $prefix"
        fi
 fi
 echo "Install prefix ............. : $prefix"
+
diff --git a/apps/irssi/curses.m4 b/apps/irssi/curses.m4
new file mode 100644 (file)
index 0000000..83a0812
--- /dev/null
@@ -0,0 +1,296 @@
+dnl Curses detection: Munged from Midnight Commander's configure.in
+dnl
+dnl What it does:
+dnl =============
+dnl
+dnl - Determine which version of curses is installed on your system
+dnl   and set the -I/-L/-l compiler entries and add a few preprocessor
+dnl   symbols 
+dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that
+dnl   @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in
+dnl   Makefile.in's
+dnl - Modify the following configure variables (these are the only
+dnl   curses.m4 variables you can access from within configure.in)
+dnl   CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if
+dnl                       an ncurses.h that's been renamed to curses.h
+dnl                       is found.
+dnl   CURSES_LIBS       - sets -L and -l's appropriately
+dnl   CFLAGS            - if --with-sco, add -D_SVID3 
+dnl   has_curses        - exports result of tests to rest of configure
+dnl
+dnl Usage:
+dnl ======
+dnl 1) Add lines indicated below to acconfig.h
+dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in
+dnl 3) Instead of #include <curses.h> you should use the following to
+dnl    properly locate ncurses or curses header file
+dnl
+dnl    #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+dnl    #include <ncurses.h>
+dnl    #else
+dnl    #include <curses.h>
+dnl    #endif
+dnl
+dnl 4) Make sure to add @CURSES_INCLUDEDIR@ to your preprocessor flags
+dnl 5) Make sure to add @CURSES_LIBS@ to your linker flags or LIBS
+dnl
+dnl Notes with automake:
+dnl - call AM_CONDITIONAL(HAS_CURSES, test "$has_curses" = true) from
+dnl   configure.in
+dnl - your Makefile.am can look something like this
+dnl   -----------------------------------------------
+dnl   INCLUDES= blah blah blah $(CURSES_INCLUDEDIR) 
+dnl   if HAS_CURSES
+dnl   CURSES_TARGETS=name_of_curses_prog
+dnl   endif
+dnl   bin_PROGRAMS = other_programs $(CURSES_TARGETS)
+dnl   other_programs_SOURCES = blah blah blah
+dnl   name_of_curses_prog_SOURCES = blah blah blah
+dnl   other_programs_LDADD = blah
+dnl   name_of_curses_prog_LDADD = blah $(CURSES_LIBS)
+dnl   -----------------------------------------------
+dnl
+dnl
+dnl The following lines should be added to acconfig.h:
+dnl ==================================================
+dnl
+dnl /*=== Curses version detection defines ===*/
+dnl /* Found some version of curses that we're going to use */
+dnl #undef HAS_CURSES
+dnl    
+dnl /* Use SunOS SysV curses? */
+dnl #undef USE_SUNOS_CURSES
+dnl 
+dnl /* Use old BSD curses - not used right now */
+dnl #undef USE_BSD_CURSES
+dnl 
+dnl /* Use SystemV curses? */
+dnl #undef USE_SYSV_CURSES
+dnl 
+dnl /* Use Ncurses? */
+dnl #undef USE_NCURSES
+dnl 
+dnl /* If you Curses does not have color define this one */
+dnl #undef NO_COLOR_CURSES
+dnl 
+dnl /* Define if you want to turn on SCO-specific code */
+dnl #undef SCO_FLAVOR
+dnl 
+dnl /* Set to reflect version of ncurses *
+dnl  *   0 = version 1.*
+dnl  *   1 = version 1.9.9g
+dnl  *   2 = version 4.0/4.1 */
+dnl #undef NCURSES_970530
+dnl
+dnl /*=== End new stuff for acconfig.h ===*/
+dnl 
+
+
+AC_DEFUN(AC_CHECK_CURSES,[
+       search_ncurses=true
+       screen_manager=""
+       has_curses=false
+
+       CFLAGS=${CFLAGS--O}
+
+       AC_SUBST(CURSES_LIBS)
+       AC_SUBST(CURSES_INCLUDEDIR)
+
+       AC_ARG_WITH(sco,
+         [  --with-sco              Use this to turn on SCO-specific code],[
+         if test x$withval = xyes; then
+               AC_DEFINE(SCO_FLAVOR)
+               CFLAGS="$CFLAGS -D_SVID3"
+         fi
+       ])
+
+       AC_ARG_WITH(sunos-curses,
+         [  --with-sunos-curses     Used to force SunOS 4.x curses],[
+         if test x$withval = xyes; then
+               AC_USE_SUNOS_CURSES
+         fi
+       ])
+
+       AC_ARG_WITH(osf1-curses,
+         [  --with-osf1-curses      Used to force OSF/1 curses],[
+         if test x$withval = xyes; then
+               AC_USE_OSF1_CURSES
+         fi
+       ])
+
+       AC_ARG_WITH(vcurses,
+         [  --with-vcurses[=incdir] Used to force SysV curses],
+         if test x$withval != xyes; then
+               CURSES_INCLUDEDIR="-I$withval"
+         fi
+         AC_USE_SYSV_CURSES
+       )
+
+       AC_ARG_WITH(ncurses,
+         [  --with-ncurses[=dir]    Compile with ncurses/locate base dir],
+         if test x$withval = xno ; then
+               search_ncurses=false
+         elif test x$withval != xyes ; then
+               AC_NCURSES($withval/include, ncurses.h, -L$withval/lib -lncurses, -I$withval/include, "ncurses on $withval/include")
+         fi
+       )
+
+       if $search_ncurses
+       then
+               AC_SEARCH_NCURSES()
+       fi
+])
+
+
+AC_DEFUN(AC_USE_SUNOS_CURSES, [
+       search_ncurses=false
+       screen_manager="SunOS 4.x /usr/5include curses"
+       AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses)
+       AC_DEFINE(USE_SUNOS_CURSES)
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(NO_COLOR_CURSES)
+       AC_DEFINE(USE_SYSV_CURSES)
+       CURSES_INCLUDEDIR="-I/usr/5include"
+       CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a"
+       AC_MSG_RESULT(Please note that some screen refreshs may fail)
+])
+
+AC_DEFUN(AC_USE_OSF1_CURSES, [
+       AC_MSG_RESULT(Using OSF1 curses)
+       search_ncurses=false
+       screen_manager="OSF1 curses"
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(NO_COLOR_CURSES)
+       AC_DEFINE(USE_SYSV_CURSES)
+       CURSES_LIBS="-lcurses"
+])
+
+AC_DEFUN(AC_USE_SYSV_CURSES, [
+       AC_MSG_RESULT(Using SysV curses)
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(USE_SYSV_CURSES)
+       search_ncurses=false
+       screen_manager="SysV/curses"
+       CURSES_LIBS="-lcurses"
+])
+
+dnl AC_ARG_WITH(bsd-curses,
+dnl [--with-bsd-curses         Used to compile with bsd curses, not very fancy],
+dnl    search_ncurses=false
+dnl    screen_manager="Ultrix/cursesX"
+dnl    if test $system = ULTRIX
+dnl    then
+dnl        THIS_CURSES=cursesX
+dnl        else
+dnl        THIS_CURSES=curses
+dnl    fi
+dnl
+dnl    CURSES_LIBS="-l$THIS_CURSES -ltermcap"
+dnl    AC_DEFINE(HAS_CURSES)
+dnl    has_curses=true
+dnl    AC_DEFINE(USE_BSD_CURSES)
+dnl    AC_MSG_RESULT(Please note that some screen refreshs may fail)
+dnl    AC_WARN(Use of the bsdcurses extension has some)
+dnl    AC_WARN(display/input problems.)
+dnl    AC_WARN(Reconsider using xcurses)
+dnl)
+
+       
+dnl
+dnl Parameters: directory filename cureses_LIBS curses_INCLUDEDIR nicename
+dnl
+AC_DEFUN(AC_NCURSES, [
+    if $search_ncurses
+    then
+        if test -f $1/$2
+       then
+           AC_MSG_RESULT(Found ncurses on $1/$2)
+
+           CURSES_LIBS="$3"
+           AC_CHECK_LIB(ncurses, initscr, [
+               true;
+           ], [
+                CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'`
+               AC_CHECK_LIB(curses, initscr, [
+                       CURSES_LIBS="$CHECKLIBS"
+               ],, $CHECKLIBS)
+           ], $CURSES_LIBS)
+           CURSES_INCLUDEDIR="$4"
+           search_ncurses=false
+           screen_manager=$5
+            AC_DEFINE(HAS_CURSES)
+            has_curses=true
+           has_ncurses=true
+           AC_DEFINE(USE_NCURSES)
+       fi
+    fi
+])
+
+AC_DEFUN(AC_SEARCH_NCURSES, [
+    AC_CHECKING("location of ncurses.h file")
+
+    AC_NCURSES(/usr/include, ncurses.h, -lncurses,, "ncurses on /usr/include")
+    AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses, "ncurses on /usr/include/ncurses")
+    AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include, "ncurses on /usr/local")
+    AC_NCURSES(/usr/pkg/include, ncurses.h, -L/usr/pkg/lib -lncurses, -I/usr/pkg/include, "ncurses on /usr/pkg")
+    AC_NCURSES(/usr/contrib/include, ncurses.h, -L/usr/contrib/lib -lncurses, -I/usr/contrib/include, "ncurses on /usr/contrib")
+    AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -L/usr/local/lib/ncurses -lncurses, -I/usr/local/include/ncurses, "ncurses on /usr/local/include/ncurses")
+
+    AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/local/.../ncurses")
+
+    AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/include/ncurses")
+
+    dnl
+    dnl We couldn't find ncurses, try SysV curses
+    dnl
+    if $search_ncurses 
+    then
+        AC_EGREP_HEADER(init_color, /usr/include/curses.h,
+           AC_USE_SYSV_CURSES)
+       AC_EGREP_CPP(USE_NCURSES,[
+#include <curses.h>
+#ifdef __NCURSES_H
+#undef USE_NCURSES
+USE_NCURSES
+#endif
+],[
+       CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES"
+        AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       has_ncurses=true
+        AC_DEFINE(USE_NCURSES)
+        search_ncurses=false
+        screen_manager="ncurses installed as curses"
+])
+    fi
+
+    dnl
+    dnl Try SunOS 4.x /usr/5{lib,include} ncurses
+    dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES
+    dnl should be replaced by a more fine grained selection routine
+    dnl
+    if $search_ncurses
+    then
+       if test -f /usr/5include/curses.h
+       then
+           AC_USE_SUNOS_CURSES
+        fi
+    fi
+
+    dnl use whatever curses there happens to be
+    if $search_ncurses
+    then
+       if test -f /usr/include/curses.h
+       then
+         CURSES_LIBS="-lcurses"
+         AC_DEFINE(HAS_CURSES)
+         has_curses=true
+         search_ncurses=false
+         screen_manager="curses"
+       fi
+    fi
+])
+
index 4e1ec830900776a875a5d5c96189603978f50a88..427f7f595c462e1328f69137466d26edab802214 100644 (file)
@@ -231,24 +231,45 @@ abstracts = {
   ## statusbar
   ##
 
   ## statusbar
   ##
 
-  # background of statusbar
-  sb_background = "%4";
-
-  # default statusbar item style
-  sb = "%c[%n$0-%c]%n";
-
-  sbmode = "(%c+%n$0-)";
+  # default background for all statusbars. You can also give
+  # the default foreground color for statusbar items.
+  sb_background = "%4%w";
+
+  # default backround for "default" statusbar group
+  #sb_default_bg = "%4";
+  # background for prompt / input line
+  sb_prompt_bg = "%0";
+  # background for info statusbar
+  sb_info_bg = "%8";
+  # background for topicbar (same default)
+  #sb_topic_bg = "%4";
+
+  # text at the beginning of statusbars. sb-item already puts
+  # space there,so we don't use anything by default.
+  sbstart = "";
+  # text at the end of statusbars. Use space so that it's never
+  # used for anything.
+  sbend = " ";
+
+  prompt = "[$*] ";
+
+  sb = " %c[%n$*%c]%n";
+  sbmode = "(%c+%n$*)";
   sbaway = " (%GzZzZ%n)";
   sbaway = " (%GzZzZ%n)";
-  sbservertag = "%c:%n$0 (change with ^X)";
-  sbmore = "%_-- more --%_";
-  sblag = "{sb Lag: $0-}";
-  sbmail = "{sb Mail: $0-}";
-
-  # activity. Det is used for hilights when display doesn't support colors
-  sbact = "{sb {sbact_act $0}{sbact_det $1}}";
-  sbact_act = "Act: $0-";
-  sbact_det = " Det: $0-";
-
+  sbservertag = ":$0 (change with ^X)";
+
+  # activity in statusbar
+
+  # ',' separator
+  sb_act_sep = "%c$*";
+  # normal text
+  sb_act_text = "%c$*";
+  # public message
+  sb_act_msg = "%W$*";
+  # hilight
+  sb_act_hilight = "%M$*";
+  # hilight with specified color, $0 = color, $1 = text
+  sb_act_hilight_color = "$0$1-%n";
 };
 
 #
 };
 
 #
diff --git a/apps/irssi/docs/.cvsignore b/apps/irssi/docs/.cvsignore
new file mode 100644 (file)
index 0000000..c5b9e55
--- /dev/null
@@ -0,0 +1,3 @@
+Makefile
+Makefile.in
+startup-HOWTO.txt
diff --git a/apps/irssi/docs/botnet.txt b/apps/irssi/docs/botnet.txt
new file mode 100644 (file)
index 0000000..8b5f9ba
--- /dev/null
@@ -0,0 +1,316 @@
+
+ Irssi's botnet description
+
+ Copyright (c) 1999-2000 Timo Sirainen
+
+
+ 0. History
+
+ draft v0.1 : 21.8.1999
+
+       Just a first draft of my botnet design I did on a boring friday
+       work afternoon :) I'll try to implement this to irssi some day, it
+       feels pretty interesting now so it might be pretty soon even. Any
+       comments are welcome :)
+
+ draft v0.2 : 21.11.1999
+
+       Exactly three months since the first draft :) Now I actually have
+       some code done, just committed pretty simple botnet to irssi CVS.
+       Made several changes to this document.. Still missing much details
+       but the basic idea should be clear.
+
+ draft v0.3 : 21.05.2000
+
+       Strange, again the same day. I really didn't plan this :)
+       Reformatted the text, added lots of text, implemented more of the
+       stuff.
+
+
+ 1. General
+
+ 1.1 Description
+
+       A small description of what botnet would do: A group of bots
+       efficiently working together to perform their tasks. Like when
+       someone's trying to take over your channel, bots will quickly
+       decide who deops/kicks whom instead of multiple bots deopping or
+       trying to kick the same people.
+
+       Irssi's botnet is pretty much based on trust. Some malicious bot
+       can quite well mess up the whole botnet. Connecting the bots to
+       each other via ssh would be a good idea.
+
+ 1.2 Configuration
+
+       example config file:
+
+       mybotnet =
+       {
+         priority=5;
+         nick=mybot;
+         uplinks = (
+           { host = "main.botnet.org"; password = "mypass"; },
+           { host = "alter.botnet.org"; password = "pass2"; }
+         );
+         downlinks = (
+           { password = "thepass"; valid_addrs = ( "192.168.0.*" ); },
+           { password = "blah"; valid_addrs = ( "*.botnet.org" ); },
+           { password = "localpass"; valid_addrs = ( "127.*" ); }
+         );
+       }
+
+       When connecting to botnet, the bot first tries to connect to the
+       first bot in uplinks list, then the second, etc. Setting port to -1
+       will prevent connecting to the bot, 0 uses the default.
+
+ 1.3 Botnet master
+
+       To avoid total chaos inside botnet, the bots shouldn't do (almost)
+       anything without a command from botnet's master. The master should
+       know everything, and give commands to clients that can perform the
+       task best.
+
+       Master is the bot with the highest priority. If there's multiple
+       with the same priority, the one that already was the master will
+       stay master. When joining two botnets to one, the uplink's master
+       stays. If link to master breaks, the closest bot to it will choose
+       a new one.
+
+       The priorities should be given so that the bots that have the
+       fastest connections and are in the middle of the botnet have the
+       highest priorities.
+
+ 1.4 Command format
+
+       Commands that are sent inside botnet are in the following format:
+
+         <from_nick> <to_nick> COMMAND [command specific data..]
+
+        If to_nick is '-', the command should be sent to everyone.
+
+
+ 2. Handshake
+
+       First host checks from bots' valid_addrs who is connecting. If
+       there's no matches it just disconnects the client.
+
+         CLIENT: PASS <password>
+         HOST  : (if error, disconnect)
+
+         CLIENT: NICK <nick>
+         HOST  : NICKERROR | CONNECTED
+
+       If nick is already in use, the host sends NICKERROR and waits for
+       new nick.
+
+       Now we're connected to botnet. The commands from now on use the
+       format specified in section 1.4.
+
+       Both the client and the host sends information to other side of
+       all the clients they serve (if any):
+
+         BOTINFO <nick> <connected_to_nick> <priority>
+
+       BOTINFOs must be sent sorted so that connected_to_nick bot is
+       always known. Like first comes the bots connected to the
+       host/client, then the bots connected to them etc.
+
+       If the client had downlinks, nick collisions might happen. The
+       uplink is responsible for noticing them from BOTINFO commands.
+       It should automatically replace the nicks with new ones and
+       send nick change command to client and all it's downlinks. For
+       example if host received:
+
+         BOTINFO bot highbot 10
+
+       And the bot already exists, the BOTINFO is changed to:
+
+         BOTINFO bot2 highbot 10
+
+       And the client and it's downlinks are notified:
+
+         BOTNICK bot2 bot
+
+       After sending BOTINFOs, the host tells the current master:
+
+         MASTER <nick>
+
+       The client now checks if it's priority is higher than the current
+       master's. If it is, it will send the MASTER command without any
+       parameters.
+
+
+ 3. Bot connections
+
+ 3.1 General
+
+       Everyone's connections should be kept in n-way tree. Example:
+
+
+                              [highuplink]
+                 _____________/    |  |   \
+                /                  | [h5] [h6]
+              [h1]                 |     /  | \
+             /    \                |   [h7] | [h8]
+           [h2]   [h3]             |        |    \
+                   |            [uplink]   [h9] [h10]
+                  [h4]         /   |    \
+                            [up2]  |   [up1]
+                           /   |   |     |
+                       [up3] [up4] |    [up5]
+                                   |
+                                  [we]
+                                 /    \
+                         [client1]     [client2]
+                                        /     \
+                                      [c3]    [c4]
+
+
+       Botnet should try to keep together even if some hub in the middle
+       crashes. Each bot should have at least two uplinks in case one
+       dies. For example if [uplink] dies, [we] and [up1] could connect
+       to [up2], and [up2] could connect to [highuplink].
+
+       When connection is closed to some bot, a notice is sent by the
+       bot's uplink:
+
+         BOTQUIT <nick>
+
+       The quit notice should be sent only about the bot that was
+       disconnected. Bots should figure out themselves the other bots and
+       remove them too from their lists.
+
+ 3.2 Lag
+
+       Each bot should send PING commands to their up/downlinks every
+       now and then (1min?) to check that the connection is still active.
+       If the PONG isn't received in 10 seconds, it's priority should be
+       temporarily lowered to -1. If the PONG isn't received in 3
+       minutes, the whole connection should be closed.
+
+       Master should know lag times of every bots. It could then
+       automatically raise/lower bots' priorities depending on how big
+       their lag is. Master could also lower it's own priority and pass
+       the master status to someone else with lower lag.
+
+       If there's lot of lag (>3sec?) somewhere and something urgent
+       happens, the botnet could split and behave independently.
+
+
+ 4. IRC networks
+
+ 4.1 Server connections
+
+       When bot is connected to some irc server and is ready to take
+       commands, it says:
+
+         IRCJOIN <tag> <ircnet> <server> <nick>
+
+       Tag is the bot specific unique tag of the server, so that the bot
+       can connect multiple times to same IRC network. All IRC related
+       commands should specify the server tag where it should be sent.
+
+       If bot quits an irc network, it says:
+
+         IRCQUIT <tag>
+
+ 4.2 IRC commands
+
+       Master asks a bot to send some command to IRC server by saying:
+
+         CMD <id> <tag> <command>
+
+       <command> can't really be anything, since the bot should also be
+       able to reply to it. The <id> is for identifying the command/reply
+       pair. Master should keep the command in memory until it receives
+       the reply:
+
+         CMDREPLY <id> <last> <reply>
+
+       The command could get a reply of multiple lines, so <last>
+       specifies if the reply is the last line (1 or 0).
+
+       If the command failed for some reason, the bot will reply with
+
+         CMDFAIL <id>
+
+       and master should send the command to some other bot.
+
+ 4.3 Channels
+
+       When joined/left channels, the bot says:
+
+         CHANJOIN <tag> <channel>
+         CHANPART <tag> <channel>
+
+       After BOTJOIN, master tries to op the bot. When bot receives +o,
+       it says:
+
+         CHANOP <tag> <channel>
+
+       If it is the first opped bot in channel, master orders the bot to
+       op the rest of the bots.
+
+       If the bot is kicked, it says:
+
+         CHANKICK <tag> <channel>
+
+       When master notices that bot is kicked, it first checks if there's
+       any other opped bots in channel. If not, it waits for a random
+       pause, 5-10sec before letting the bot join the channel again so
+       that it won't get autorejoin ban.
+
+       If bot can't join channel, it says:
+
+         CHANBANNED <tag> <channel>
+         (or)
+         CHANCANTJOIN <tag> <channel>
+
+       When received BOTBANNED, master tries to unban bot or set a ban
+       exception. BOTCANTJOIN results as invite to channel.
+
+ 4.4 Channel information
+
+       When master notices that bot is the first one joined to channel,
+       it asks the bot for some channel information:
+
+         CMD <id> <tag> NAMES <channel>
+         CMD <id> <tag> WHO <channel>
+         CMD <id> <tag> MODE <channel>
+         CMD <id> <tag> MODE b <channel>
+         CMD <id> <tag> MODE e <channel> (if IRC network supports this)
+         CMD <id> <tag> MODE I <channel> (if IRC network supports this)
+
+       It's also possible that if several bots join immediately after the
+       first bot, the commands are shared between all the bots.
+
+       Bots should cache the information as much as possible, at least
+       NAMES command.
+
+ 4.5 Channel priorities
+
+       Every channel has a priority: LOW, NORMAL, HIGH.
+
+       Normally LOW operates just as NORMAL channels, except when some
+       channel has HIGH priority and bots are really busy, LOW channels
+       just wait until there's time for them.
+
+       In NORMAL channels, the most urgent operations (kicks, ops, deops)
+       are performed quite soon even while bots are busy handling HIGH
+       priority commands.
+
+       Channels shouldn't normally be HIGH priority, but if attack
+       against channel is detected (like someone comes from split, gets
+       ops and gets to op someone else), it's priority is set to HIGH.
+       When channel's priority is HIGH, botnet does everything it can to
+       get rid of unauthorized opped people as fast as possible.
+
+       LOW channel's priority can also be raised to HIGH, but it's
+       priority is dropped back to LOW if some NORMAL channel's priority
+       is raised to HIGH too.
+
+       Master notifies about channel's priority change by saying:
+
+         CHANPRIORITY <ircnet> <channel> <LOW/NORMAL/HIGH>
+
diff --git a/apps/irssi/docs/crash.txt b/apps/irssi/docs/crash.txt
new file mode 100644 (file)
index 0000000..d3100f5
--- /dev/null
@@ -0,0 +1,59 @@
+How to submit a good bug report?
+
+First you should give the following information:
+ - irssi version, if CVS (or devel. tarball) then which day?
+ - operating system / distribution and it's version
+ - when did it crash? did you do something? can you reproduce the crash?
+
+Getting backtrace of the crash also helps a lot, especially if irssi crashes
+randomly. If after crash you see text:
+
+ "segmentation fault (core dumped)"
+
+It writes a file named "core" or "irssi.core" depending on your OS to
+directory where you started irssi. If it doesn't print the "(core dumped)"
+or you can't find the core file, you'll have to raise the limit for max.
+core file size before running irssi. To do this, say:
+
+ ulimit -c unlimited
+
+So, if you have the core file and GNU debugger (gdb), you can get the
+backtrace with:
+
+ gdb irssi core
+ bt
+
+Paste all the lines starting from line having #0 at the beginning.
+
+Here's an example session:
+
+[cras@hurina] ~/cvs/m/irssi/src/fe-text$ gdb ./irssi core
+
+GNU gdb 5.0
+Copyright 2000 Free Software Foundation, Inc.
+GDB is free software, covered by the GNU General Public License, and you are
+welcome to change it and/or distribute copies of it under certain conditions.
+Type "show copying" to see the conditions.
+There is absolutely no warranty for GDB.  Type "show warranty" for details.
+This GDB was configured as "i686-pc-linux-gnu"...
+
+Core was generated by ./irssi'.
+Program terminated with signal 11, Segmentation fault.
+#0  0x805e949 in view_scroll (view=0x816cfb5, lines=0x816cfd9, 
+    subline=0x816cfdd, scrollcount=-11, draw_nonclean=1)
+    at textbuffer-view.c:528
+528                             realcount += view->bottom_subline;
+
+(gdb) bt
+
+#0  0x805e949 in view_scroll (view=0x816cfb5, lines=0x816cfd9, 
+    subline=0x816cfdd, scrollcount=-11, draw_nonclean=1)
+    at textbuffer-view.c:528
+#1  0x805ecb4 in textbuffer_view_scroll (view=0x816cfb5, lines=-11)
+    at textbuffer-view.c:669
+#2  0x8058387 in gui_window_scroll (window=0x816cead, lines=-11)
+    at gui-windows.c:128
+#3  0x8056b64 in window_prev_page () at gui-readline.c:109
+#4  0x8057047 in key_scroll_backward () at gui-readline.c:334
+...
+(gdb) 
diff --git a/apps/irssi/docs/design.txt b/apps/irssi/docs/design.txt
new file mode 100644 (file)
index 0000000..5c8ddcf
--- /dev/null
@@ -0,0 +1,156 @@
+
+ Irssi's hierarchy is something like this:
+
+
+         sub1 sub2
+            \ /
+       xxx  IRC       COMMON ICQ  yyy
+        |____|___________|____|____|
+                   |
+                  GUI (gtk/gnome, qt/kde, text, none)
+                   |
+         sub1 sub2 |
+            \ /    |
+       xxx  IRC    |  COMMON ICQ  yyy
+        |____|_____|_____|____|____|
+                   |
+               COMMON UI
+                   |
+         sub1 sub2 |
+            \ /    |
+       xxx  IRC    |    ICQ  yyy
+        |____|_____|_____|____|
+                   |
+                 CORE
+                 /  \
+        lib-config  lib-popt
+
+
+ (IRC, ICQ, xxx and yyy are chat protocols ..)
+ (sub1 and sub2 are submodules of IRC module, like DCC and flood protect)
+
+
+ Chat protocols and frontends are kept in separate modules. Common UI
+ and GUI modules also have the common parts which don't know anything
+ about the chat protocols. This should allow implementing modules to
+ whatever chat protocols and with whatever frontends easily.
+
+ ** Signals
+
+ Communication between different modules are done with "signals". They are
+ not related to UNIX signals in any way, you could more like think of them
+ as "events" - which might be a better name for them, but I don't really
+ want to change it anymore :)
+
+ So, you send signal with signal_emit() and it's sent to all modules that
+ have grabbed it by calling signal_add() in their init function. For
+ example:
+
+   signal_emit("mysignal", 1, "hello");
+
+ Sends a "mysignal" function with one argument "hello" - before that, you
+ should have grabbed the signal somewhere else with:
+
+   static void sig_mysignal(const char *arg1)
+   {
+     /* arg1 contains "hello" */
+   }
+
+   signal_add("mysignal", (SIGNAL_FUNC) sig_mysignal);
+
+ There are three different signal_add() functions which you can use to
+ specify if you want to grab the signal first, "normally" or last. You can
+ also stop the signal from going any further.
+
+ Emitting signal with it's name creates a small overhead since it has to
+ look up the signal's numeric ID first, after which it looks up the signal
+ structure. This is done because if you call a signal _really_ often,
+ it's faster to find it with it's numeric ID instead of the string. You
+ can use signal_get_uniq_id() macro to convert the signal name into ID -
+ you'll have to do this only once! - and use signal_emit_id() to emit the
+ signal. Don't bother to do this unless your signal is sent (or could be
+ sent) several times in a second.
+
+ See src/core/signals.h for defination of the signal function, and
+ signals.txt for a list of signals.
+
+
+ ** lib-popt
+
+   CORE depends on this for command line parameter handling.
+   (distributed with irssi)
+
+
+ ** lib-config
+
+   Irssi depends on this for reading and saving configuration.
+   (created by me for irssi)
+
+
+ ** CORE module
+
+ Provides some functionality that all other modules can use:
+   - signal handling
+   - keeping list of settings
+   - keeping list of /commands
+   - keeping track of loaded modules
+   - networking functions (with nonblocking connects, IPv6 support)
+   - handles connecting to servers
+   - raw logging of server's input/output data
+   - /EVAL support
+   - fgets() like function line_split() without any maximum line limits
+   - command line parameter handling
+   - miscellaneous useful little functions
+   - handles logging
+
+
+ ** COMMON UI module
+
+   - knows basics about windows and window items (=channels, queries, ..)
+   - printtext() - parsing texts and feeding it for GUI to print.
+   - themes
+   - translation tables
+   - text hilighting
+   - command history
+   - user interface (/commands) for CORE's functionality
+
+
+ ** GUI modules
+
+   - all the rest of the functionality needed for a working client.
+
+
+ ** IRC module
+
+   * CORE
+
+     - IRC specific /commands
+     - flood protecting commands sent to server
+     - creating IRC masks based on nick/address for bans, ignores, etc.
+     - keeps list of channels, nicks, channel modes, bans, etc.
+     - keeps list of servers, server settings, irc networks,
+       server reconnections and irc network splits
+     - redirection of commands' replies
+     - lag detection
+     - ctcp support and flood protection
+     - Handles ignoring people
+
+   * DCC
+
+     - DCC chat, send and get
+
+   * FLOOD
+
+     - detects private or channel flooding and sends "flood" signal
+     - automatic ignoring when flooding
+
+   * NOTIFYLIST
+
+     - handles notifylist
+
+
+ ** IRC UI module
+
+   - placing channels and queries in windows
+   - nick completion
+   - printing infomation of some events
index 6dabc43c381e23a5eae0c472840b62aa78cfe950..e98f2719e7feb0a1a2297939a6195cb8e84fe18d 100644 (file)
 
                foreground (fg)     background (bg)
    -------------------------------------------------------
 
                foreground (fg)     background (bg)
    -------------------------------------------------------
-    0          white               black       with 0 as fg
-                                  light gray   with fg > 0
-    1          ligh gray           black
+    0          white               light gray   + blinking fg
+    1          black               black
     2          blue                blue
     3          green               green
     2          blue                blue
     3          green               green
-    4          light red           red         + blinking fg
-    5          orange              orange
+    4          light red           red          + blinking fg
+    5          red                 red
     6          magenta (purple)    magenta
     6          magenta (purple)    magenta
-    7          red                 red
-    8          yellow              orange      + blinking fg
-    9          light green         light green + blinking fg
-    10         cyan                cyan                
-    11         light cyan          cyan                + blinking fg
-    12         light blue          blue                + blinking fg
-    13         light magenta       magenta     + blinking fg
-    14         gray                black       + blinking fg 
-    15         light gray          black on black
-                                   gray  on light gray (with bold)
+    7          orange              orange
+    8          yellow              orange       + blinking fg
+    9          light green         green        + blinking fg
+    10         cyan                cyan         
+    11         light cyan          cyan         + blinking fg
+    12         light blue          blue         + blinking fg
+    13         light magenta       magenta      + blinking fg
+    14         gray                black        + blinking fg 
+    15         light gray          light gray
 
 
- These colors may differ depending on your terminal.
+ These colors may differ depending on your terminal. In particular
+ the meaning for background may be the same as for the foreground
+ (bright colors, no blinking), and orange often looks like brown or
+ dark yellow.
 
  How to use these colors ('#' means a number as MIRC color code):
  
 
  How to use these colors ('#' means a number as MIRC color code):
  
diff --git a/apps/irssi/docs/help/in/invitelist.in b/apps/irssi/docs/help/in/invitelist.in
new file mode 100644 (file)
index 0000000..ed6bc06
--- /dev/null
@@ -0,0 +1,9 @@
+
+@SYNTAX:invitelist@
+
+Shows the +I modes of the current channel. +I mode
+allows free joins of clients with certain userhost mask
+even if the channel is invite only.
+
+See also: INVITE, MODE
+
diff --git a/apps/irssi/docs/help/in/perlflush.in b/apps/irssi/docs/help/in/perlflush.in
new file mode 100644 (file)
index 0000000..27fc509
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:perlflush@
+
+Stops and removes all Perl scripts which have been run.
+Also undefines all the commands defined by Perl scripts.
+
+See also: RUN
+
index 7c25c7bc54c7b421fa42552bd97c8be0bd5aa904..2954deff23d114adb1ca5ad9510f646c2fcce889 100644 (file)
        --enable-memdebug  Enable memory debugging, great for finding
                           memory leaks
 
        --enable-memdebug  Enable memory debugging, great for finding
                           memory leaks
 
-       --enable-perl=static  Build Perl support statically to irssi binary
+       --with-perl=static Build Perl support statically to irssi binary
                           (default is to build a module)
                           (default is to build a module)
-       --enable-perl-path=dir  Specify installation dir for Perl libraries
-       --disable-perl     Disable Perl support
+       --with-perl-lib=[site|vendor|DIR]  Specify installation dir for
+                          Perl libraries. Site is the default (usually
+                          /usr/local/lib/perl/...), vendor uses the path
+                          where the base of the perl is installed
+                          (/usr/lib/perl/...), or DIR specifies exactly
+                          where you want to install it.
+       --without-perl     Disable Perl support
 
        --with-socks       Build with socks library
        --with-bot         Build irssi-bot
 
        --with-socks       Build with socks library
        --with-bot         Build irssi-bot
diff --git a/apps/irssi/docs/perl.txt b/apps/irssi/docs/perl.txt
new file mode 100644 (file)
index 0000000..b04e0cb
--- /dev/null
@@ -0,0 +1,1143 @@
+ Running Perl scripts
+ --------------------
+
+First you'll need to have Perl support on. By default irssi compiles
+Perl as a module, so /LOAD perl probably helps. If you want to do this
+automatically at startup put the "/LOAD perl" to ~/.irssi/startup file.
+After that you can run scripts with /RUN script (you don't need to give
+the .pl extension). If /RUN complains about "unknown command", you
+don't have Perl module loaded, or maybe Perl support wasn't compiled at
+all.
+
+Place new scripts to ~/.irssi/scripts/ or /usr/local/lib/irssi/scripts/
+directory. Scripts in ~/.irssi/scripts/autorun/ directory are
+automatically run at startup.
+
+Using /PERLFLUSH closes and reopens the perl interpreter removing all
+Perl scripts from memory. There's currently no way to unload a single
+Perl script (/SCRIPT REMOVE will probably work soon). You can however
+run same script multiple times, and irssi will remove the old version
+from memory before running the new version.
+
+
+ Irssi's signals
+ ---------------
+
+Irssi is pretty much based on sending and handling different signals.
+Like when you receive a message from server, say
+
+  :nick!user@there.org PRIVMSG you :blahblah
+
+Irssi will first send a signal:
+
+  "server incoming", SERVER_REC, "nick!user@there PRIVMSG ..."
+
+You probably don't want to use this signal. Default handler for this
+signal interprets the header and sends a signal:
+
+  "server event", SERVER_REC, "PRIVMSG ...", "nick", "user@there.org"
+
+You probably don't want to use this either, since this signal's default
+handler parses the event string and sends a signal:
+
+  "event privmsg", SERVER_REC, "you :blahblah", "nick", "user@there.org"
+
+You can at any point grab the signal, do whatever you want to do with
+it and optionally stop it from going any further by calling
+Irssi::signal_stop();
+
+For example:
+
+  sub event_privmsg {
+    # $data = "nick/#channel :text"
+    my ($server, $data, $nick, $address) = @_;
+    my ($target, $text) = split(/ :/, $data, 2);
+
+    Irssi::signal_stop() if ($text =~ /free.*porn/ || $nick =~ /idiot/);
+  }
+
+Irssi::signal_add("event privmsg", "event_privmsg")
+
+This will hide all public or private messages that match the regexp
+"free.*porn" or the sender's nick contain the word "idiot". Yes, you
+could use /IGNORE instead for both of these :)
+
+You can also use signal_add_last() if you wish to let the Irssi's internal
+functions be run before yours.
+
+A list of signals that irssi sends can be found from signals.txt file.
+
+
+ Creating/replacing /COMMANDS
+ ----------------------------
+
+You can create your own commands, or replace existing ones with
+Irssi::command_bind(). The command handling work internally pretty much
+the same as signal handlers, so if you replace existing command and don't
+wish to let it run, call Irssi::signal_stop().
+
+Here's an example:
+
+  # Usage: /HELLO [<nick>]
+  sub cmd_hello {
+    # data - contains the parameters for /HELLO
+    # server - the active server in window
+    # witem - the active window item (eg. channel, query)
+    #         or undef if the window is empty
+    my ($data, $server, $witem) = @_;
+
+    if (!$server || !$server->{connected}) {
+      Irssi::print("Not connected to server");
+      return;
+    }
+
+    if ($data) {
+      $server->command("/MSG $data Hello!");
+    } elsif ($witem && ($witem->{type} eq "CHANNEL" ||
+                        $witem->{type} eq "QUERY")) {
+      # there's query/channel active in window
+      $witem->command("/MSG ".$witem->{name}." Hello!");
+    } else {
+      Irssi::print("Nick not given, and no active channel/query in window");
+    }
+  }
+
+  Irssi::command_bind('hello', 'cmd_hello');
+
+
+ Message levels
+ --------------
+
+Several functions expect message levels. They're used to roughly
+classify messages. They're used by a lot of things including logging,
+ignoring, highlighting, etc. so you should use as good level as
+possible. It's possible to have several levels in one message, like
+ACTIONS+PUBLIC or ACTIONS+MSGS.
+
+Here's all the levels that irssi supports currently:
+
+  CRAP, MSGS, PUBLIC, NOTICES, SNOTES, CTCPS, ACTIONS, JOINS, PARTS
+  QUITS, KICKS, MODES, TOPICS, WALLOPS, INVITES, NICKS, DCC, DCCMSGS,
+  CLIENTNOTICE, CLIENTCRAP, CLIENTERROR
+
+And a few special ones that could be included with the levels above:
+
+  HILIGHT - text is highlighted
+  NOHILIGHT - don't check highlighting for this message
+  NO_ACT - don't trigger channel activity when printing this message
+  NEVER - never ignore or log this message (not a good idea usually)
+
+You can use them with a MSGLEVEL_ prefix, for example:
+
+  $server->print("#channel", 'Hello, world', MSGLEVEL_CLIENTCRAP);
+
+Writes text to #channel window with CLIENTCRAP level.
+
+
+ Window items
+ ------------
+
+Meaning of "window" should be pretty clear, but "window item" is
+something I couldn't really figure out a better name for :) They're
+simply something that's inside a window, a channel or a query usually.
+Windows can have multiple items inside them. It's possible to create
+non-channel/query window items too, currently the third possible window
+item is created by /EXEC -interactive.
+
+In scripts, I think you can quite safely assume that the window item is
+query or channel if the script is intended to be run in one of them.
+Stupid users won't probably have other window items, and smart users
+know where to run the script, or at least later figure out why it
+didn't work :)
+
+
+ Functions that you can use in Irssi's Perl scripts
+ --------------------------------------------------
+
+If there's a "Xxxx::" text before the command, it means that it belongs to
+that package. Like "Server::command" means that you should either call it as
+  Irssi::Server::command($server, $cmd);
+or more easily:
+  $server->command($cmd);
+
+Commands that don't have the Xxxx prefix are called as Irssi::command();
+
+Information from most objects can be fetched with $object->{data}, for
+example current nick in server could be read with $server->{nick}. List
+of all the information that are in objects are in "Object->{}" sections
+below.
+
+Commands are split in two groups, generic ones that could be used with
+any chat protocol, and IRC specific commands. If you want to use IRC
+specific commands, or use IRC specific ->{data} in your scripts, you'll
+need to add "use Irssi::Irc" to your scripts. IRC specific commands are
+listed after the generic ones.
+
+
+ *** General
+
+Window active_win() - return active window
+Server active_server() - return server in active window
+
+windows() - return list of all windows
+servers() - return list of all servers
+reconnects() - return list of all server reconnections
+channels() - return list of all channels
+queries() - return list of all queries
+commands() - return list of all commands
+logs() - return list of all log files
+ignores() - returns list of all ignores
+
+Server::channels() - return list of channels in server
+Server::queries() - return list of queries in server
+
+print(str[, level])
+Server::print(channel, str[, level])
+Window::print(str[, level])
+Windowitem::print(str[, level])
+  Print `str'. Default level is MSGLEVEL_CLIENTNOTICE.
+
+command(cmd)
+Server::command(cmd)
+Window::command(cmd)
+Windowitem::command(cmd)
+  Send a command `cmd' (in current channel). This will work just as if you
+  had typed `cmd' in command line, so you'll need to use /COMMANDS or the
+  text will be sent to the channel.
+
+  Just like above, except different calling method.
+
+
+ *** Themes
+
+You can have user configurable texts in scripts that work just like
+irssi's internal texts that can be changed in themes.
+
+First you'll have to register the formats:
+
+Irssi::theme_register([
+  'format_name', '{hilight my perl format!}',
+  'format2', 'testing.. nick = $0, channel = $1'
+]);
+
+Printing happens with one of the functions:
+
+printformat(level, format, ...)
+Window::printformat(level, format, ...)
+Server::printformat(target, level, format, ...)
+Windowitem::printformat(level, format, ...)
+
+For example:
+
+  $channel->printformat(MSGLEVEL_CRAP, 'format2',
+                       'nick', $channel->{name});
+
+
+ *** Settings
+
+settings_get_str(key)
+settings_get_int(key)
+settings_get_bool(key)
+  Return value for setting.
+
+settings_add_str(section, key, def)
+settings_add_int(section, key, def)
+settings_add_bool(section, key, def)
+  Create new setting.
+
+settings_remove(key)
+  Remove a setting.
+
+
+ *** Signals
+
+signal_emit(signal, ...)
+  Send signal `signal'. You can give 6 parameters at maximum.
+
+signal_add(signal, func)
+  Bind `signal' to function `func'.
+
+signal_add_first(signal, func)
+  Bind `signal' to function `func'. Call `func' as soon as possible.
+
+signal_add_last(signal, func)
+  Bind `signal' to function `func'. Call `func' as late as possible.
+
+signal_remove(signal, func)
+  Unbind `signal' from function `func'.
+
+signal_stop()
+  Stop the signal that's currently being emitted.
+
+signal_stop_by_name(signal)
+  Stop the signal with name `signal' that's currently being emitted.
+
+
+  *** timeouts / IO listener
+
+timeout_add(msecs, func, data)
+  Call `func' every `msecs' milliseconds (1000 = 1 second) with
+  parameter `data'. Returns tag which can be used to stop the timeout.
+
+timeout_remove(tag)
+  Remove timeout with tag.
+
+input_add(source, condition, func, data)
+  Call `func' with parameter `data' when specified IO happens.
+  `source' is the file handle that is being listened. `condition' can
+  be INPUT_READ, INPUT_WRITE or both. Returns tag which can be used to
+  remove the listener.
+
+input_remove(tag)
+  Remove listener with tag.
+
+
+ *** Message levels
+
+level2bits(level)
+  Level string -> number
+
+bits2level(bits)
+  Level number -> string
+
+combine_level(level, str)
+  Combine level number to level string ("+level -level").
+  Return new level number.
+
+
+ *** Commands
+
+Command->{}
+  cmd - Command name
+  category - Category
+
+command_bind(cmd, func[, category])
+  Bind command `cmd' to call function `func'. `category' is the
+  category where the command is displayed in /HELP.
+
+command_runsub(cms, data, server, item)
+  Run subcommands for `cmd'. First word in `data' is parsed as
+  subcommand. `server' is Irssi::Server rec for current
+  Irssi::Windowitem `item'.
+  
+  Call command_runsub in handler function for `cmd' and bind
+  with command_bind("`cmd' `subcmd'", subcmdfunc[, category]);
+
+command_unbind(cmd, func)
+  Unbind command `cmd' from function 'func.
+
+
+ *** Windows
+
+UI::Window->{}
+  refnum - Reference number
+  name - Name
+
+  width - Width
+  height - Height
+
+  history_name - Name of named historylist for this window
+
+  active - Active window item
+  active_server - Active server
+
+  servertag - active_server must be either undef or have this same tag
+              (unless there's items in this window). This is used by
+             /WINDOW SERVER -sticky
+  level - Current window level
+
+  sticky_refnum - 1 if reference number is sticky
+
+  data_level - Current data level
+  hilight_color - Current activity hilight color
+
+  last_timestamp - Last time timestamp was written in window
+  last_line - Last time text was written in window
+
+  theme_name - Active theme in window, undef = default
+
+UI::TextDest->{}
+  window - Window where the text will be written
+  server - Target server
+  target - Target channel/query/etc name
+  level - Text level
+
+  hilight_priority - Priority for the hilighted text
+  hilight_color - Color for the hilighted text
+
+
+Window::items()
+  Return a list of items in window.
+
+Window
+window_create(automatic)
+Windowitem::window_create(automatic)
+  Create a new window.
+
+Window::destroy()
+  Destroy the window.
+
+Irssi::Window
+Windowitem::window()
+  Returns parent window for window item.
+
+Window
+window_find_name(name)
+  Find window with name.
+
+Window
+window_find_refnum(refnum)
+  Find window with reference number.
+
+Window
+window_find_level(level)
+Server::window_find_level(level)
+  Find window with level.
+
+Window
+window_find_closest(name, level)
+Server::window_find_closest(name, level)
+  Find window that matches best to given arguments. `name' can be either
+  window name or name of one of the window items.
+
+Window
+window_find_item(name)
+Server::window_find_item(name)
+  Find window which contains window item with specified name/server.
+
+Windowitem
+window_item_find(name)
+Server::window_item_find(name)
+Window::item_find(server, name)
+  Find window item that matches best to given arguments.
+
+window_refnum_prev(refnum, wrap)
+window_refnum_next(refnum, wrap)
+  Return refnum for window that's previous/next in windows list.
+
+windows_refnum_last()
+  Return refnum for last window.
+
+Window::item_add(item, automatic)
+Window::item_remove(item)
+Window::item_destroy(item)
+  Add/remove/destroy window item
+
+Window::set_active()
+  Set window active.
+
+Window::change_server(server)
+Window::set_refnum(refnum)
+Window::set_name(name)
+Window::set_history(name)
+Window::set_level(level)
+  Change server/refnum/name/history/level in window.
+
+Windowitem::set_active()
+  Change window item active in parent window.
+
+Window::item_prev()
+Window::item_next()
+  Change to previous/next window item.
+
+Windowitem::change_server(server)
+  Change server in window item.
+
+Windowitem::is_active()
+  Returns 1 if window item is the active item in parent window.
+
+Window::get_active_name()
+  Return active item's name, or if none is active, window's name
+
+
+ *** Server Connects
+
+Connect->{}
+  type - "SERVER CONNECT" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  address - Address where we connected (irc.blah.org)
+  port - Port where we connected
+  chatnet - Chat network
+
+  password - Password we used in connection.
+  wanted_nick - Nick which we would prefer to use
+  username - User name
+  realname - Real name
+
+Connect
+server_create_conn(address[, port=6667[, password=''[, nick=''[, channels='']]]])
+  Create new server connection.
+
+
+ *** Server functions
+
+Server->{}
+  type - "SERVER" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Connect above..)
+
+  connect_time - Time when connect() to server finished
+  real_connect_time - Time when server sent "connected" message
+
+  tag - Unique server tag
+  nick - Current nick
+
+  connected - Is connection finished? 1|0
+  connection_lost - Did we lose the connection (1) or was
+                    the connection just /DISCONNECTed (0)
+
+  rawlog - Rawlog object for the server
+
+  version - Server version
+  last_invite - Last channel we were invited to
+  server_operator - Are we server operator (IRC op) 1|0
+  usermode_away - Are we marked as away? 1|0
+  away_reason - Away reason message
+  banned - Were we banned from this server? 1|0
+  lag - Current lag to server in milliseconds
+
+Server
+Connect::connect()
+  Connect to server.
+
+Server::disconnect()
+  Disconnect from server.
+
+Server
+server_find_tag(tag)
+  Find server with tag
+
+Server
+server_find_chatnet(chatnet)
+  Find first server that is in `chatnet'
+
+Server::isnickflag(flag)
+  Returns 1 if flag is a nick mode flag (@, + or % in IRC)
+
+Server::ischannel(data)
+  Returns 1 if start of `data' seems to mean channel.
+
+Server::get_nick_flags()
+  Returns nick flag characters in order: op, voice, halfop ("@+%" in IRC).
+
+Server::send_message(target, msg)
+  Sends a message to nick/channel.
+
+
+ *** Server reconnections
+
+Reconnect->{}
+  type - "RECONNECT" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Connect above..)
+
+  tag - Unique numeric tag
+  next_connect - Unix time stamp when the next connection occurs
+
+
+ *** Chat networks
+
+Chatnet->{}
+  type - "CHATNET" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  name - name of chat network
+
+  nick - if not empty, nick preferred in this network
+  username - if not empty, username preferred in this network
+  realname - if not empty, realname preferred in this network
+
+  own_host - address to use when connecting this network
+  autosendcmd - command to send after connecting to this network
+
+chatnet_find(name)
+  Find chat network with name.
+
+
+ *** Server redirections
+
+This is a powerful feature of Irssi that I haven't seen in other IRC
+clients. You can EASILY grab the server's reply for a command you send
+to server without any horrible kludges.
+
+redirect_register(command, remote, timeout, start, stop, opt)
+   Register new redirection command. By default irssi has already
+   registered at least: whois, whowas, who, list, ison, userhost, ping,
+   "mode channel" (/MODE #channel), "mode b" (/MODE #channel b), "mode e"
+   and "mode I".
+
+   `command' specifies the name of the command to register, it doesn't
+   have to be a real command name, but something you just specify to
+   redirect_event() when using this redirection.
+
+   `remote' specifies if the command is by default a remote command
+   (eg. sent to another server). redirect_event() may override this.
+
+   `timeout' - If remote is TRUE, specifies how many seconds to wait for
+   reply before aborting.
+
+   `start', `stop', `opt' - hash references with "event" => argpos entries.
+   List of events that start and stop this redirection.
+   Start event list may be empty, but there must be at least one
+   stop event. Optional events are checked only if they are received
+   immediately after one of the stop-events. `argpos' specifies the
+   word number in event string which is compared to wanted argument,
+   -1 = don't compare, TRUE always.
+
+  Example (already done by irssi):
+
+  Irssi::redirect_register('mode channel', 0, 0,
+       undef, # no start events
+       { # stop events
+         "event 324" => 1, # MODE-reply
+         "event 403" => 1, # no such channel
+         "event 442" => 1, # "you're not on that channel"
+         "event 479" => 1  # "Cannot join channel (illegal name)"
+       }, { # optional events
+         "event 329", 1 # Channel create time
+       } );
+
+Server::redirect_event(command, count, arg, remote, failure_signal, signals)
+   Specify that the next command sent to server will be redirected.
+   NOTE: This command MUST be called before sending the command to server.
+
+   `command' - Name of the registered redirection that we're using.
+
+   `count' - How many times to execute the redirection. Some commands may
+   send multiple stop events, like MODE #a,#b.
+
+   `arg' - The argument to be compared in event strings. You can give multiple
+   arguments separated with space.
+
+   `remote' - Specifies if the command is a remote command, -1 = use default.
+
+   `failure_signal' - If irssi can't find the stop signal for the redirection,
+   this signal is called.
+
+   `signals' - hash reference with "event" => "redir signal" entries.
+   If the event is "", all the events belonging to the redirection but not
+   specified here, will be sent there.
+
+  Example:
+
+  # ignore all events generated by whois query, except 311.
+  $server->redirect_event("whois", 1, "cras", 0, undef, {
+                         "event 311" => "redir whois",
+                         "" => "event empty" });
+  $server->send_raw("WHOIS :cras");
+
+
+ *** Window items
+
+Windowitem->{}
+  type - Type of the window item, for example "CHANNEL" or "QUERY"
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  server - Active server for item
+  name - Name of the item
+
+  createtime - Time the window item was created
+  data_level - 0=no new data, 1=text, 2=msg, 3=highlighted text
+  hilight_color - Color of the last highlighted text
+
+
+ *** Channels
+
+Channel->{}
+  type - "CHANNEL" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Windowitem above..)
+
+  topic - Channel topic
+  topic_by - Nick who set the topic
+  topic_time - Timestamp when the topic was set
+
+  no_modes - Channel is modeless
+  mode - Channel mode
+  limit - Max. users in channel (+l mode)
+  key - Channel key (password)
+
+  chanop - You are channel operator
+  names_got - /NAMES list has been received
+  wholist - /WHO list has been received
+  synced - Channel is fully synchronized
+
+  joined - JOIN event for this channel has been received
+  left - You just left the channel (for "channel destroyed" event)
+  kicked - You was just kicked out of the channel (for
+           "channel destroyed" event)
+
+Server::channels_join(channels, automatic)
+  Join to channels in server. `channels' may also contain keys for
+  channels just like with /JOIN command. `automatic' specifies if this
+  channel was joined "automatically" or if it was joined because join
+  was requested by user. If channel join is "automatic", irssi doesn't
+  jump to the window where the channel was joined.
+
+Channel
+Server::channel_create(name, automatic)
+  Create new channel.
+
+Channel
+channel_create(chat_type, name, automatic)
+  Create new channel with specified chat type.
+  FIXME: should this be removed? is this useful for anything?
+
+Channel::destroy()
+  Destroy channel.
+
+Channel
+channel_find(channel)
+  Find channel from any server.
+
+Channel
+Server::channel_find(channel)
+  Find channel from specified server.
+
+
+ *** Nick list
+
+Nick->{}
+  type - "NICK" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  nick - Plain nick
+  host - Host address
+  realname - Real name
+  hops - Hop count to the server the nick is using
+
+  gone, serverop - User status, 1 or 0
+  op, voice, halfop - Channel status, 1 or 0
+
+  last_check - timestamp when last checked gone/ircop status.
+  send_massjoin - Waiting to be sent in a "massjoin" signal, 1 or 0
+
+Nick
+Channel::nick_insert(nick, op, voice, send_massjoin)
+  Add nick to nicklist.
+
+Channel::nick_remove(nick)
+  Remove nick from nicklist.
+
+Nick
+Channel::nick_find(mask)
+  Find nick from nicklist.
+
+Channel::nicks(channel)
+  Return a list of all nicks in channel.
+
+Server::nicks_get_same(nick)
+  Return all nick objects in all channels in server. List is in format:
+  Channel, Nick, Channel, ...
+
+
+ *** Queries
+
+Query->{}
+  type - "QUERY" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Windowitem above..)
+
+  address - Host address of the queries nick
+  server_tag - Server tag used for this nick (doesn't get erased if
+               server gets disconnected)
+  unwanted - 1 if the other side closed or some error occured (DCC chats)
+
+Query
+query_create(chat_type, server_tag, nick, automatic)
+  Create a new query.
+
+Query::destroy()
+  Destroy the query.
+
+Query::query_change_server(server)
+  Change the active server of the query.
+
+Query
+query_find(nick)
+  Find query from any server.
+
+Query
+Server::query_find(nick)
+  Find query from specified server.
+
+
+ *** Masks
+
+You should use the Server version of the function if possible, since
+with different chat protocols the mask matching could be different.
+
+mask_match(mask, nick, user, host)
+Server::mask_match(mask, nick, user, host)
+  Return 1 if `mask' matches nick!user@host.
+
+mask_match_address(mask, nick, address)
+Server::mask_match_address(mask, nick, address)
+  Return 1 if `mask' matches nick!address.
+
+masks_match(masks, nick, address)
+Server::masks_match(masks, nick, address)
+  Return 1 if any mask in the `masks' (string separated with spaces)
+  matches nick!address.
+
+
+ *** Rawlog
+
+Rawlog->{}
+  logging - The rawlog is being written to file currently
+  nlines - Number of lines in rawlog
+
+Rawlog
+rawlog_create()
+  Create a new rawlog.
+
+Rawlog::destroy()
+  Destroy the rawlog.
+
+Rawlog::get_lines()
+  Returns all lines in rawlog.
+
+rawlog_set_size(lines)
+  Set the default rawlog size for new rawlogs.
+
+Rawlog::open(filename)
+  Start logging new messages in rawlog to specified file.
+
+Rawlog::close()
+  Stop logging to file.
+
+Rawlog::save(filename)
+  Save the current rawlog history to specified file.
+
+Rawlog::input(str)
+  Send `str' to raw log as input text.
+
+Rawlog::output(str)
+  Send `str' to raw log as output text.
+
+Rawlog::redirect(str)
+  Send `str' to raw log as redirection text.
+
+
+ *** Logging
+
+Log->{}
+  fname - Log file name
+  real_fname - The actual opened log file (after %d.%m.Y etc. are expanded)
+  opened - Log file is open
+  level - Log only these levels
+  last - Timestamp when last message was written
+  autoopen - Automatically open log at startup
+  failed - Opening log failed last time
+  temp - Log isn't saved to config file
+  items - List of log items
+
+Logitem->{}
+  type - 0=target, 1=window refnum
+  name - Name
+  servertag - Server tag
+
+Log
+log_create_rec(fname, level)
+  Create log file.
+
+Log::update()
+  Add log to list of logs / save changes to config file.
+
+Log
+log_find(fname)
+  Find log with file name.
+
+Log::close()
+  Destroy log file.
+
+Log::start_logging()
+  Open log file and start logging.
+
+Log::stop_logging()
+  Close log file.
+
+Log::item_add(type, name, server)
+  Add log item to log.
+
+Log::item_destroy(item)
+  Remove log item from log.
+
+Logitem
+Log::item_find(type, item, server)
+  Find item from log.
+
+
+ *** Ignores
+
+Ignore->{}
+  mask - Ignore mask
+  servertag - Ignore only in server
+  channels - Ignore only in channels (list of names)
+  pattern - Ignore text pattern
+
+  level - Ignore level
+
+  exception - This is an exception ignore
+  regexp - Regexp pattern matching
+  fullword - Pattern matches only full words
+
+ignore_add_rec(ignore)
+  Add ignore record.
+
+ignore_update_rec(ignore)
+  Update ignore record in configuration
+
+ignore_check(nick, host, channel, text, level)
+Server::ignore_check(nick, host, channel, text, level)
+  Return 1 if ignoring matched.
+
+
+ ***
+ *** IRC specific functions. All objects below this are prefixed with Irc::
+ ***
+
+ *** IRC servers
+
+Irc::Server->{}
+  (..contains all the same data as core Server object..)
+  real_address - Address the IRC server gives
+  usermode - User mode in server
+  userhost - Your user host in server
+
+Irc::Connect->{}
+  (..contains all the same data as core Connect object..)
+  alternate_nick - Alternate nick to use if default nick is taken.
+
+Connect::connect()
+  Connect to IRC server.
+
+Server::get_channels(server)
+  Return a string of all channels (and keys, if any have them) in server,
+  like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g"
+
+Server::send_raw(cmd)
+  Send raw message to server, it will be flood protected so you
+  don't need to worry about it.
+
+Server::send_raw_now(cmd)
+  Send raw message to server immediately without flood protection.
+
+Server::send_raw_split(cmd, nickarg, max_nicks)
+  Split the `cmd' into several commands so `nickarg' argument has only
+  `max_nicks' number of nicks.
+
+  Example:
+    $server->send_raw_split("KICK #channel nick1,nick2,nick3 :byebye", 3, 2);
+
+  Irssi will send commands "KICK #channel nick1,nick2 :byebye" and
+  "KICK #channel nick3 :byebye" to server.
+
+Server::ctcp_send_reply(data)
+  Send CTCP reply. This will be "CTCP flood protected" so if there's too
+  many CTCP requests in buffer, this reply might not get sent. The data
+  is the full raw command to be sent to server, like
+    "NOTICE nick :\001VERSION irssi\001"
+
+
+ *** IRC channels
+
+Ban->{}
+  ban - The ban
+  setby - Nick of who set the ban
+  time - Timestamp when ban was set
+
+Channel
+Server::channel_create(name, automatic)
+  Create new channel.
+
+Channel::bans()
+  Return a list of bans in channel.
+
+Channel::ebans()
+  Return a list of ban exceptions in channel.
+
+Channel::invites()
+  Return invite list (+I) of channel.
+
+Channel::ban_get_mask(nick)
+  Get ban mask for `nick'.
+
+Channel::banlist_add(ban, nick, time)
+   Add a new ban to channel.
+
+Channel::banlist_remove(ban)
+   Remove a ban from channel.
+
+Channel::banlist_exception_add(ban, nick, time)
+   Add a new ban exception to channel.
+
+Channel::banlist_exception_remove(ban)
+   Remove a ban exception from channel.
+
+Channel::invitelist_add(mask)
+   Add a new invite mask to channel.
+
+Channel::invitelist_remove(mask)
+   Remove invite mask from channel.
+
+modes_join(old, mode, channel)
+  Add `mode' to `old' - return newly allocated mode. If `channel' is 1,
+  we're parsing channel mode and we should try to join mode arguments too.
+
+
+ *** DCC
+
+Dcc->{}
+  type - Type of the DCC: chat, send, get
+  orig_type - Original DCC type that was sent to us - same as type except
+              GET and SEND are swapped
+  created - Time stamp when the DCC record was created
+
+  server - Server record where the DCC was initiated.
+  servertag - Tag of the server where the DCC was initiated.
+  mynick - Our nick to use in DCC chat.
+  nick - Other side's nick name.
+
+  chat - Dcc chat record if the request came through DCC chat
+  target - Who the request was sent to - your nick, channel or empty
+           if you sent the request
+  arg - Given argument .. file name usually
+
+  addr - Other side's IP address.
+  port - Port we're connecting in.
+
+  starttime - Unix time stamp when the DCC transfer was started
+  transfd - Bytes transferred
+
+Dcc::Chat->{}
+  id - Unique identifier - usually same as nick
+  mirc_ctcp - Send CTCPs without the CTCP_MESSAGE prefix
+  connection_lost - Other side closed connection
+
+Dcc::Get->{}
+  (..contains all the same data as core Dcc object..)
+  size - File size
+  skipped - Bytes skipped from start (resuming file)
+
+  get_type - What to do if file exists? 0=default, 1=rename, 2=overwrite,
+             3=resume
+  file - The real file name which we use.
+  file_quoted - 1 if file name was received quoted ("file name")
+
+Dcc::Send->{}
+  (..contains all the same data as core Dcc object..)
+  size - File size
+  skipped - Bytes skipped from start (resuming file)
+
+  file_quoted - 1 if file name was received quoted ("file name")
+  waitforend - File is sent, just wait for the replies from the other side
+  gotalldata - Got all acks from the other end
+
+
+dccs() - return list of all dcc connections
+
+Dcc::destroy()
+  Destroy DCC connection.
+
+Dcc
+dcc_find_item(type, nick, arg)
+  Find DCC connection.
+
+Dcc
+dcc_find_by_port(nick, port)
+  Find DCC connection by port.
+
+Dcc
+Windowitem::get_dcc(item)
+  If `item' is a query of a =nick, return DCC chat record of nick.
+
+Dcc::chat_send(data)
+  Send `data' to dcc chat.
+
+Server::dcc_ctcp_message(target, notice, msg)
+Dcc::ctcp_message(target, notice, msg)
+  Send a CTCP message/notify to target.
+
+
+ *** Netsplits
+
+Netsplit->{}
+  nick - Nick
+  address - Nick's host
+  destroy - Timestamp when this record should be destroyed
+  server - Netsplitserver object
+  channels - list of channels (Netsplitchannel objects) the nick was in
+
+Netsplitserver->{}
+  server - The server nick was in
+  destserver - The other server where split occured.
+  count - Number of splits in server
+
+Netsplitchannel->{}
+  name - Channel name
+  nick - Nick object
+
+Netsplit
+Server::netsplit_find(nick, address)
+  Check if nick!address is on the other side of netsplit. Netsplit records
+  are automatically removed after 30 minutes (current default)..
+
+Nick
+Server::netsplit_find_channel(nick, address, channel)
+  Find nick record for nick!address in channel `channel'.
+
+
+ *** Notify list
+
+Notifylist->{}
+  mask - Notify nick mask
+  away_check - Notify away status changes
+  idle_check_time - Notify when idle time is reset and idle was bigger
+                    than this (seconds)
+  ircnets - List of ircnets (strings) the notify is checked
+
+notifies() - Return list of all notifies
+
+Notifylist
+notifylist_add(mask, ircnets, away_check, idle_check_time)
+  Add new item to notify list.
+
+notifylist_remove(mask)
+  Remove item from notify list.
+
+Notifylist
+notifylist_find(mask, ircnet)
+  Find notify.
+
+Server
+notifylist_ison(nick, serverlist)
+  Check if `nick' is in IRC. `serverlist' is a space separated
+  list of server tags. If it's empty string, all servers will be checked.
+
+Server::notifylist_ison_server(nick)
+  Check if `nick' is on IRC server.
+
+Notifylist::ircnets_match(ircnet)
+  Returns 1 if notify is checked in `ircnet'.
+
+
+ *** /EXEC processes
+
+Process->{}
+  id - ID for the process
+  name - Name for the process (if given)
+  args - The command that is being executed
+
+  pid - PID for the executed command
+  target - send text with /msg <target> ...
+  target_win - print text to this window
+
+  shell - start the program via /bin/sh
+  notice - send text with /notice, not /msg if target is set
+  silent - don't print "process exited with level xx"
diff --git a/apps/irssi/docs/proxy.txt b/apps/irssi/docs/proxy.txt
new file mode 100644 (file)
index 0000000..20e5171
--- /dev/null
@@ -0,0 +1,26 @@
+Irssi proxy usage:
+
+First you'll need to have the proxy module installed, either configure
+irssi with --with-proxy and do make install, or manually:
+
+  cd src/irc/proxy
+  make
+  mkdir ~/.irssi/modules
+  cp .libs/libproxy.so ~/.irssi/modules/
+
+In irssi, say:
+
+  /LOAD proxy
+
+You really should set some password for the proxy with:
+
+  /SET irssiproxy_password secret
+
+Then you'll need to configure the ports/ircnets the proxy listens in,
+something like:
+
+  /SET irssiproxy_ports ircnet=2777 efnet=2778 opn=2779
+
+There we have 3 different irc networks answering in 3 ports. Note that
+you'll have to make the correct /IRCNET ADD and /SERVER ADD commands to
+make it work properly.
diff --git a/apps/irssi/docs/signals.txt b/apps/irssi/docs/signals.txt
new file mode 100644 (file)
index 0000000..f32789f
--- /dev/null
@@ -0,0 +1,322 @@
+List of signals irssi emits - see design.txt for more information about
+signals.
+
+core
+----
+
+* Requires to work properly:
+
+ "gui exit"
+ "gui dialog", char *type, char *text
+ "send command", char *command, SERVER_REC, WI_ITEM_REC
+
+* Provides signals:
+
+chat-protocols.c:
+ "chat protocol created", CHAT_PROTOCOL_REC
+ "chat protocol updated", CHAT_PROTOCOL_REC
+ "chat protocol destroyed", CHAT_PROTOCOL_REC
+
+channels.c:
+ "channel created", CHANNEL_REC, int automatic
+ "channel destroyed", CHANNEL_REC
+
+chatnets.c:
+ "chatnet created", CHATNET_REC
+ "chatnet destroyed", CHATNET_REC
+
+commands.c:
+ "commandlist new", COMMAND_REC
+ "commandlist remove", COMMAND_REC
+ "error command", int err, char *cmd
+
+ "send command", char *args, SERVER_REC, WI_ITEM_REC
+ "send text", char *line, SERVER_REC, WI_ITEM_REC
+ "command "<cmd>, char *args, SERVER_REC, WI_ITEM_REC
+ "default command", char *args, SERVER_REC, WI_ITEM_REC
+
+ignore.c:
+ "ignore created", IGNORE_REC
+ "ignore destroyed", IGNORE_REC
+ "ignore changed", IGNORE_REC
+
+log.c:
+ "log new", LOG_REC
+ "log remove", LOG_REC
+ "log create failed", LOG_REC
+ "log locked", LOG_REC
+ "log started", LOG_REC
+ "log stopped", LOG_REC
+ "log rotated", LOG_REC
+ "log written", LOG_REC, char *line
+
+modules.c:
+ "module loaded", MODULE_REC, MODULE_FILE_REC
+ "module unloaded", MODULE_REC, MODULE_FILE_REC
+ "module error", int error, char *text, char *rootmodule, char *submodule
+
+nicklist.c:
+ "nicklist new", CHANNEL_REC, NICK_REC
+ "nicklist remove", CHANNEL_REC, NICK_REC
+ "nicklist changed", CHANNEL_REC, NICK_REC, char *old_nick
+ "nicklist host changed", CHANNEL_REC, NICK_REC
+ "nicklist gone changed", CHANNEL_REC, NICK_REC
+ "nicklist serverop changed", CHANNEL_REC, NICK_REC
+
+pidwait.c:
+ "pidwait", int pid, int status
+
+queries.c:
+ "query created", QUERY_REC, int automatic
+ "query destroyed", QUERY_REC
+ "query nick changed", QUERY_REC, char *orignick
+ "query address changed", QUERY_REC
+ "query server changed", QUERY_REC, SERVER_REC
+
+rawlog.c:
+ "rawlog", RAWLOG_REC, char *data
+
+server.c:
+ "server looking", SERVER_REC
+ "server connected", SERVER_REC
+ "server connecting", SERVER_REC, ulong *ip
+ "server connect failed", SERVER_REC
+ "server disconnected", SERVER_REC
+ "server quit", SERVER_REC, char *msg
+
+settings.c:
+ "setup changed"
+ "setup reread", char *fname
+ "setup saved", char *fname, int autosaved
+
+signal.c:
+
+ "signal", char *name, ...
+ "last signal", char *name, ...
+
+IRC core
+--------
+
+* Provides signals:
+
+bans.c:
+ "ban type changed", char *bantype
+
+channels, nicklist:
+ "channel joined", CHANNEL_REC
+ "channel wholist", CHANNEL_REC
+ "channel sync", CHANNEL_REC
+
+ "channel topic changed", CHANNEL_REC
+
+ctcp.c:
+
+ "ctcp msg", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp msg "<cmd>, SERVER_REC, char *args, char *nick, char *addr, char *target
+ "default ctcp msg", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp reply", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp reply "<cmd>, SERVER_REC, char *args, char *nick, char *addr, char *target
+ "default ctcp reply", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp action", SERVER_REC, char *args, char *nick, char *addr, char *target
+
+irc-log.c:
+ "awaylog show", LOG_REC, int away_msgs, int filepos
+
+irc-nicklist.c:
+ "server nick changed", SERVER_REC
+
+irc-servers.c:
+ "event connected", SERVER_REC
+
+irc.c:
+
+ "server event", SERVER_REC, char *data, char *sender_nick, char *sender_address
+ "event "<cmd>, SERVER_REC, char *args, char *sender_nick, char *sender_address
+ "default event", SERVER_REC, char *data, char *sender_nick, char *sender_address
+
+ "server incoming", SERVER_REC, char *data
+
+(for perl parser..)
+ "redir "<cmd>, SERVER_REC, char *args, char *sender_nick, char *sender_address
+
+lag.c:
+ "server lag", SERVER_REC
+ "server lag disconnect", SERVER_REC
+
+massjoin.c:
+ "massjoin", CHANNEL_REC, GSList of NICK_RECs
+
+mode-lists.c:
+ "ban new", CHANNEL_REC, BAN_REC
+ "ban remove", CHANNEL_REC, BAN_REC
+
+modes.c:
+ "channel mode changed", CHANNEL_REC
+ "nick mode changed", CHANNEL_REC, NICK_REC
+ "user mode changed", SERVER_REC, char *old
+ "away mode changed", SERVER_REC
+
+netsplit.c:
+ "netsplit server new", SERVER_REC, NETSPLIT_SERVER_REC
+ "netsplit server remove", SERVER_REC, NETSPLIT_SERVER_REC
+ "netsplit new", NETSPLIT_REC
+ "netsplit remove", NETSPLIT_REC
+
+IRC modules
+-----------
+
+* Provides signals:
+
+dcc*.c:
+
+ "dcc ctcp "<cmd>, char *args, DCC_REC
+ "default dcc ctcp", char *args, DCC_REC
+ "dcc unknown ctcp", char *args, char *sender, char *sendaddr
+
+ "dcc reply "<cmd>, char *args, DCC_REC
+ "default dcc reply", char *args, DCC_REC
+ "dcc unknown reply", char *args, char *sender, char *sendaddr
+
+ "dcc chat message", DCC_REC, char *msg
+
+ "dcc created", DCC_REC
+ "dcc destroyed", DCC_REC
+ "dcc connected", DCC_REC
+ "dcc rejecting", DCC_REC
+ "dcc closed", DCC_REC
+ "dcc request", DCC_REC, char *sendaddr
+ "dcc request send", DCC_REC
+ "dcc chat message", DCC_REC, char *msg
+ "dcc transfer update", DCC_REC
+ "dcc get receive", DCC_REC
+ "dcc error connect", DCC_REC
+ "dcc error file create", DCC_REC, char *filename
+ "dcc error file open", char *nick, char *filename, int errno
+ "dcc error get not found", char *nick
+ "dcc error send exists", char *nick, char *filename
+ "dcc error unknown type", char *type
+ "dcc error close not found", char *type, char *nick, char *filename
+
+autoignore.c:
+
+ "autoignore new", SERVER_REC, AUTOIGNORE_REC
+ "autoignore remove", SERVER_REC, AUTOIGNORE_REC
+
+flood.c:
+
+ "flood", SERVER_REC, char *nick, char *host, int level, char *target
+
+notifylist.c:
+
+ "notifylist new", NOTIFYLIST_REC
+ "notifylist remove", NOTIFYLIST_REC
+ "notifylist joined", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+ "notifylist away changed", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+ "notifylist unidle", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+ "notifylist left", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+
+proxy/listen.c:
+
+ "proxy client connected", CLIENT_REC
+ "proxy client disconnected", CLIENT_REC
+
+FE common
+---------
+
+* Requires to work properly:
+
+ "gui print text", WINDOW_REC, int fg, int bg, int flags, char *text, int level
+
+(Can be used to determine when all "gui print text"s are sent (not required))
+ "gui print text finished", WINDOW_REC
+
+* Provides signals:
+
+completion.c:
+ "complete word", GList * of char*, WINDOW_REC, char *word, char *linestart, int *want_space
+
+fe-common-core.c:
+ "irssi init read settings"
+
+fe-exec.c:
+ "exec new", PROCESS_REC
+ "exec remove", PROCESS_REC, int status
+ "exec input", PROCESS_REC, char *text
+
+fe-messages.c:
+ "message public", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message private", SERVER_REC, char *msg, char *nick, char *address
+ "message own_public", SERVER_REC, char *msg, char *target
+ "message own_private", SERVER_REC, char *msg, char *target, char *orig_target
+ "message join", SERVER_REC, char *channel, char *nick, char *address
+ "message part", SERVER_REC, char *channel, char *nick, char *address, char *reason
+ "message quit", SERVER_REC, char *nick, char *address, char *reason
+ "message kick", SERVER_REC, char *channel, char *nick, char *kicker, char *address, char *reason
+ "message nick", SERVER_REC, char *newnick, char *oldnick, char *address
+ "message own_nick", SERVER_REC, char *newnick, char *oldnick, char *address
+ "message invite", SERVER_REC, char *channel, char *nick, char *address
+ "message topic", SERVER_REC, char *channel, char *topic, char *nick, char *address
+
+keyboard.c:
+ "keyinfo created", KEYINFO_REC
+ "keyinfo destroyed", KEYINFO_REC
+
+printtext.c:
+ "print text", TEXT_DEST_REC *dest, char *text, char *stripped
+
+themes.c:
+ "theme created", THEME_REC
+ "theme destroyed", THEME_REC
+
+window-activity.c:
+ "window hilight", WINDOW_REC
+ "window activity", WINDOW_REC, int old_level
+ "window item hilight", WI_ITEM_REC
+ "window item activity", WI_ITEM_REC, int old_lvel
+
+window-items.c:
+ "window item new", WINDOW_REC, WI_ITEM_REC
+ "window item remove", WINDOW_REC, WI_ITEM_REC
+ "window item changed", WINDOW_REC, WI_ITEM_REC
+ "window item server changed", WINDOW_REC, WI_ITEM_REC
+
+windows.c:
+ "window created", WINDOW_REC
+ "window destroyed", WINDOW_REC
+ "window changed", WINDOW_REC, WINDOW_REC old
+ "window changed automatic", WINDOW_REC
+ "window server changed", WINDOW_REC, SERVER_REC
+ "window refnum changed", WINDOW_REC, int old
+ "window name changed", WINDOW_REC
+ "window history changed", WINDOW_REC, char *oldname
+ "window level changed", WINDOW_REC
+
+FE IRC
+------
+
+fe-irc-messages.c:
+ "message irc op_public", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message irc own_wall", SERVER_REC, char *msg, char *target
+ "message irc own_action", SERVER_REC, char *msg, char *target
+ "message irc action", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message irc own_notice", SERVER_REC, char *msg, char *target
+ "message irc notice", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message irc own_ctcp", SERVER_REC, char *cmd, char *data, char *target
+ "message irc ctcp", SERVER_REC, char *msg, char *nick, char *address, char *target
+
+dcc/fe-dcc-chat-messages.c:
+ "message dcc own", DCC_REC *dcc, char *msg
+ "message dcc own_action", DCC_REC *dcc, char *msg
+ "message dcc own_ctcp", DCC_REC *dcc, char *cmd, char *data
+ "message dcc", DCC_REC *dcc, char *msg
+ "message dcc action", DCC_REC *dcc, char *msg
+ "message dcc ctcp", DCC_REC *dcc, char *cmd, char *data
+
+Text FE
+-------
+
+gui-printtext.c:
+ "beep"
+
+statusbar-items.c:
+ "mail counter"
diff --git a/apps/irssi/docs/special_vars.txt b/apps/irssi/docs/special_vars.txt
new file mode 100644 (file)
index 0000000..c56c452
--- /dev/null
@@ -0,0 +1,113 @@
+NOTE: This is just a slightly modified file taken from EPIC's help.
+
+Special Variables and Expandos
+
+Irssi supports a number of reserved, dynamic variables, sometimes
+referred to as expandos.  They are special in that the client is
+constantly updating their values automatically.  There are also
+numerous variable modifiers available.
+
+   Modifier          Description
+   $variable         A normal variable, expanding to the first match of:
+                     |  1) an internal SET variable
+                     |  2) an environment variable
+   $[num]variable    Expands to the variables value, with 'num' width.  If
+                     | the number is negative, the value is right-aligned.
+                     | The value is padded to meet the width with the
+                     | character given after number (default is space).
+                     | The value is truncated to specified width unless
+                     | '!' character precedes the number. If '.' character
+                     | precedes the number the value isn't padded, just
+                     | truncated.
+   $#variable        Expands to the number of words in $variable. If $variable
+                     | is omitted, it assumes $*
+   $@variable        Expands to the number of characters in $variable. if
+                     | $variable is omitted, it assumes $*
+   $($subvariable)   This is somewhat similar to a pointer, in that the
+                     | value of $subvar is taken as the name of the
+                     | variable to expand to.  Nesting is allowed.
+   ${expression}     Permits the value to be embedded in another string
+                     | unambiguously.
+   $!history!        Expands to a matching entry in the client's command
+                     | history, wildcards allowed.
+
+Whenever an alias is called, these expandos are set to the arguments passed
+to it.  If none of these expandos are used in the alias, or the $() form
+shown above, any arguments passed will automatically be appended to the last
+command in the alias.
+
+   Expando   Description
+   $*        expands to all arguments passed to an alias
+   $n        expands to argument 'n' passed to an alias (counting from zero)
+   $n-m      expands to arguments 'n' through 'm' passed to an alias
+   $n-       expands to all arguments from 'n' on passed to an alias
+   $-m       expands to all arguments up to 'm' passed to an alias
+   $~        expands to the last argument passed to an alias
+
+These variables are set and updated dynamically by the client.  The case of
+$A .. $Z is important.
+
+   Variable   Description
+   $,         last person who sent you a MSG
+   $.         last person to whom you sent a MSG
+   $:         last person to join a channel you are on
+   $;         last person to send a public message to a channel you are on
+   $A         text of your AWAY message, if any
+   $B         body of last MSG you sent
+   $C         current channel
+   $D         last person that NOTIFY detected a signon for
+   $E         idle time
+   $F         time client was started, $time() format
+   $H         current server numeric being processed
+   $I         channel you were last INVITEd to
+   $J         client version text string
+   $K         current value of CMDCHARS
+   $L         current contents of the input line
+   $M         modes of current channel, if any
+   $N         current nickname
+   $O         value of STATUS_OPER if you are an irc operator
+   $P         if you are a channel operator in $C, expands to a '@'
+   $Q         nickname of whomever you are QUERYing
+   $R         version of current server
+   $S         current server name
+   $T         target of current input (channel or nick of query)
+   $U         value of cutbuffer
+   $V         client release date (format YYYYMMDD)
+   $W         current working directory
+   $X         your /userhost $N address (user@host)
+   $Y         value of REALNAME
+   $Z         time of day (hh:mm, can be changed with /SET timestamp_format)
+   $$         a literal '$'
+
+   $versiontim          prints time of the irssi version in HHMM format
+   $sysname            system name (eg. Linux)
+   $sysrelease         system release (eg. 2.2.18)
+   $sysarch            system architecture (eg. i686)
+   $topic              channel topic
+   $usermode           user mode
+   $cumode             own channel user mode
+   $cumode_space       like $cumode, but gives space if there's no mode.
+   $tag                        server tag
+   $chatnet            chat network of server
+   $winref             window reference number
+   $winname            window name
+
+For example, assume you have the following alias:
+
+   alias blah msg $D Hi there!
+
+If /blah is passed any arguments, they will automatically be appended to the
+MSG text.  For example:
+
+   /blah oops                          /* command as entered */
+   "Hi there! oops"                    /* text sent to $D */
+
+Another useful form is ${}.  In general, variables can be embedded inside
+strings without problems, assuming the surrounding text could not be
+misinterpreted as part of the variable name.  This form guarantees that
+surrounding text will not affect the expression's return value.
+
+   /eval echo foo$Nfoo                 /* breaks, looks for $nfoo */
+   /eval echo foo${N}foo               /* ${N} returns current nickname */
+   fooYourNickfoo                      /* returned by above command */
+
index e0af3492013511476fad83598b33112b5c86f8b1..dd8f214891eca9f02a0a2640f3cb4ebd581bb182 100644 (file)
@@ -34,7 +34,8 @@
     <li>How can I save all texts in a window to file?</li>
     </ul></li>
 <li><a href="#c8">Logging</a></li>
     <li>How can I save all texts in a window to file?</li>
     </ul></li>
 <li><a href="#c8">Logging</a></li>
-<li><a href="#c9">Irssi's settings</a></li>
+<li><a href="#c9">Proxies and IRC bouncers</a></li>
+<li><a href="#c10">Irssi's settings</a></li>
 </ol>
 
 <h3><a id="c1">1. For all the lazy people</a></h3>
 </ol>
 
 <h3><a id="c1">1. For all the lazy people</a></h3>
@@ -83,19 +84,29 @@ to bot after joined to efnet/#irssi:</p>
 
 <pre>
      /CHANNEL ADD -auto #irssi ircnet
 
 <pre>
      /CHANNEL ADD -auto #irssi ircnet
-     /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
                  #irssi efnet
 </pre>
 
                  #irssi efnet
 </pre>
 
+If you want lines containing your nick to hilight:
+
+<pre>
+     /HILIGHT nick
+</pre>
+
 <h3><a id="c2">2. Basic user interface usage</a></h3>
 
 <h3><a id="c2">2. Basic user interface usage</a></h3>
 
+<p>Windows can be scrolled up/down with PgUp and PgDown keys. If they don't
+work for you, use Meta-p and Meta-n keys. For jumping to beginning or end of
+the buffer, use /SB HOME and /SB END commands.</p>
+
 <p>By default, irssi uses "hidden windows" for everything. Hidden
 window is created every time you /JOIN a channel or /QUERY someone.
 There's several ways you can change between these windows:</p>
 
 <pre>
      Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
 <p>By default, irssi uses "hidden windows" for everything. Hidden
 window is created every time you /JOIN a channel or /QUERY someone.
 There's several ways you can change between these windows:</p>
 
 <pre>
      Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
-     Meta-q .. Meta-p          - Jump directly between windows 11-20
+     Meta-q .. Meta-o          - Jump directly between windows 11-19
      /WINDOW &lt;number&gt;          - Jump to any window with specified number
      Ctrl-P, Ctrl-N            - Jump to previous / next window
 </pre>
      /WINDOW &lt;number&gt;          - Jump to any window with specified number
      Ctrl-P, Ctrl-N            - Jump to previous / next window
 </pre>
@@ -117,10 +128,16 @@ want to use ALT instead of Windows key for it, use:</p>
      rxvt*modifier: alt
 </pre>
 
      rxvt*modifier: alt
 </pre>
 
+<p>You could do this by changing the X key mappings:</p>
+
+<pre>
+    xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+</pre>
+
 <p>And how exactly do you set these X resources? For Debian, there's
 /etc/X11/Xresources/xterm file where you can put them and it's read 
 automatically when X starts. ~/.Xresources and ~/.Xdefaults files might also
 <p>And how exactly do you set these X resources? For Debian, there's
 /etc/X11/Xresources/xterm file where you can put them and it's read 
 automatically when X starts. ~/.Xresources and ~/.Xdefaults files might also
-work. If you can't get anything else to work, just copy&paste those lines to
+work. If you can't get anything else to work, just copy&amp;paste those lines to
 ~/.Xresources and directly call "xrdb -merge ~/.Xresources" in some xterm.  
 The resources affect only the new xterms you start, not existing ones.</p>  
 
 ~/.Xresources and directly call "xrdb -merge ~/.Xresources" in some xterm.  
 The resources affect only the new xterms you start, not existing ones.</p>  
 
@@ -216,7 +233,7 @@ same network if the -auto server fails.</p>
 <p>And finally channels:</p>
 
 <pre>
 <p>And finally channels:</p>
 
 <pre>
-     /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
                  #irssi efnet
      /CHANNEL ADD -auto #secret ircnet password
 </pre>
                  #irssi efnet
      /CHANNEL ADD -auto #secret ircnet password
 </pre>
@@ -478,13 +495,99 @@ logs by adding date/time formats to the file name. The formats are in
 "man strftime" format. For example</p>
 
 <pre>
 "man strftime" format. For example</p>
 
 <pre>
-     /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
+     /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
 </pre>
 
 <p>For logging only some specific channels or nicks, see /HELP log</p>
 
 
 </pre>
 
 <p>For logging only some specific channels or nicks, see /HELP log</p>
 
 
-<h3><a id="c9">9. Irssi's settings</a></h3>
+<h3><a id="c9">9. Proxies and IRC bouncers</a></h3>
+
+<p>Irssi supports connecting to IRC servers via a proxy. All proxies have
+these settings in common:</p>
+
+<pre>
+     /SET use_proxy ON
+     /SET proxy_address &lt;Proxy host address&gt;
+     /SET proxy_port &lt;Proxy port&gt;
+</pre>
+
+<p><strong>HTTP proxy</strong></p>
+
+<p>Use these settings with HTTP proxies:</p>
+
+<pre>
+     /SET -clear proxy_password
+     /EVAL SET proxy_string CONNECT %s:%d\n\n
+</pre>
+
+<p><strong>Irssi proxy</strong></p>
+
+<p>Irssi contains it's own proxy which you can build giving
+<strong>--with-proxy</strong> option to configure. You'll still need to run
+irssi in a screen to use it though.</p>
+
+<p>Irssi proxy is a bit different than most proxies, normally proxies create
+a new connection to IRC server when you connect to it, but with irssi proxy
+all the clients use the same IRC server connection (a bit like how screen -x
+works).</p>
+
+<p>Irssi proxy supports sharing multiple server connections in different
+ports, like you can share ircnet in port 2777 and efnet in port 2778.</p>
+
+<p>Usage in proxy side:</p>
+
+<pre>
+     /LOAD irc_proxy (/LOAD proxy in irssi 0.7.98.3 and older)
+     /SET irssiproxy_password &lt;password&gt;
+     /SET irssiproxy_ports &lt;ircnet&gt;=&lt;port&gt; ... (eg. ircnet=2777 efnet=2778)
+</pre>
+
+<p><strong>NOTE</strong>: you <strong>MUST</strong> add all the servers you
+are using to server and ircnet lists with /SERVER ADD and /IRCNET ADD.
+..Except if you really don't want to for some reason, and you only use
+one server connection, you may simply set:</p>
+
+<pre>
+     /SET irssiproxy_ports *=2777 (irssi 0.7.99 and later only)
+</pre>
+
+<p>Usage in client side:</p>
+
+<p>Just connect to the irssi proxy like it is a normal server with password
+specified in /SET irssiproxy_password. For example:</p>
+
+<pre>
+     /SERVER ADD -ircnet ircnet my.irssi-proxy.org 2777 secret
+     /SERVER ADD -ircnet efnet my.irssi-proxy.org 2778 secret
+</pre>
+
+<p>Irssi proxy works fine with other IRC clients as well.</p>
+
+<p><strong>SOCKS</strong></p>
+
+Irssi can be compiled with socks support (<strong>--with-socks</strong>
+option to configure), but I don't really know how it works, if at all. /SET
+proxy settings don't have anything to do with socks however.
+
+<p><strong>Others</strong></p>
+
+<p>IRC bouncers usually work like IRC servers, and want a password. You can
+give it with:</p>
+
+<pre>
+     /SET proxy_password &lt;password&gt;
+</pre>
+
+<p>Irssi's default for connect string is</p>
+
+<pre>
+     /SET proxy_string CONNECT %s %d
+</pre>
+
+<p>which you can modify according to your bouncer's needs.</p>
+
+<h3><a id="c10">10. Irssi's settings</a></h3>
 
 <p>You probably don't like Irssi's default settings. I don't like them.
 But I'm still convinced that they're pretty good defaults. Here's some
 
 <p>You probably don't like Irssi's default settings. I don't like them.
 But I'm still convinced that they're pretty good defaults. Here's some
@@ -603,7 +706,11 @@ of them you might want to change (the default value is shown):</p>
 
 <dt>/SET show_nickmode ON</dt>
   <dd>Show the nick's mode before nick in channels, ie. ops have
 
 <dt>/SET show_nickmode ON</dt>
   <dd>Show the nick's mode before nick in channels, ie. ops have
-  &lt;@nick&gt;, voices &lt;+nick&gt; and others &lt; nick&gt;</dd>
+  &lt;@nick&gt;, voices &lt;+nick&gt; and others &lt;&nbsp;nick&gt;</dd>
+
+<dt>/SET show_nickmode_empty ON</dt>
+  <dd>If the nick doesn't have a mode, use one space. ie. ON:
+  &lt;&nbsp;nick&gt;, OFF: &lt;nick&gt;</dd>
 
 <dt>/SET show_quit_once OFF</dt>
   <dd>Show quit message only once in some of the channel windows the
 
 <dt>/SET show_quit_once OFF</dt>
   <dd>Show quit message only once in some of the channel windows the
@@ -632,7 +739,7 @@ of them you might want to change (the default value is shown):</p>
   <dd>Show the number of mails in your mbox in status
   bar. The mbox file is taken from $MAIL environment setting. Only mbox
   format works for now.</dd>
   <dd>Show the number of mails in your mbox in status
   bar. The mbox file is taken from $MAIL environment setting. Only mbox
   format works for now.</dd>
-
+</dl>
 
 <p><strong>Nick completion</strong></p>
 
 
 <p><strong>Nick completion</strong></p>
 
diff --git a/apps/irssi/docs/startup-HOWTO.txt b/apps/irssi/docs/startup-HOWTO.txt
new file mode 100644 (file)
index 0000000..cf98954
--- /dev/null
@@ -0,0 +1,598 @@
+Startup HOWTO
+
+  To new Irssi users (not to new IRC users ..)
+
+   Copyright (c) 2000-2001 by Timo Sirainen
+
+   Index with some FAQ questions that are answered in the chapter:
+    1. For all the lazy people
+    2. Basic user interface usage
+    3. Server and channel automation
+          + how do I automatically connect to servers at startup?
+          + how do I automatically join to channels at startup?
+    4. Setting up windows and automatically restoring them at startup
+    5. Status and msgs windows & message levels
+          + I want /WHOIS to print reply to current window
+          + I want all messages to go to one window, not create new
+            windows
+    6. How support for multiple servers works in irssi
+          + I connected to some server that doesn't respond and now irssi
+            keeps trying to reconnect to it again and again, how can I
+            stop it??
+          + I want to have own status and/or msgs window for each servers
+    7. /LASTLOG and jumping around in scrollback
+          + How can I save all texts in a window to file?
+    8. Logging
+    9. Proxies and IRC bouncers
+   10. Irssi's settings
+
+  1. For all the lazy people
+
+   These settings should give you pretty good defaults (the ones I use):
+
+   I don't like automatic query windows, I don't like status window, I do
+   like msgs window where all messages go:
+     /SET autocreate_own_query OFF
+     /SET autocreate_query_level DCCMSGS
+     /SET use_status_window OFF
+     /SET use_msgs_window ON
+
+   Disable automatic window closing when /PARTing channel or /UNQUERYing
+   query:
+     /SET autoclose_windows OFF
+     /SET reuse_unused_windows ON
+
+   And example how to add servers:
+
+   (openprojects network, identify with nickserv and wait for 2 seconds
+   before joining channels)
+     /IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait -opn 2000" opn
+
+   Then add some servers to different networks (ircnet is already set up
+   for them), irc.kpnqwest.fi is used by default for IRCNet but if it
+   fails, irc.funet.fi is tried next:
+     /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+     /SERVER ADD -ircnet ircnet irc.funet.fi 6667
+     /SERVER ADD -auto -ircnet efnet efnet.cs.hut.fi 6667
+
+   Automatically join to channels after connected to server, send op
+   request to bot after joined to efnet/#irssi:
+     /CHANNEL ADD -auto #irssi ircnet
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
+                  #irssi efnet
+
+   If you want lines containing your nick to hilight:
+     /HILIGHT nick
+
+  2. Basic user interface usage
+
+   Windows can be scrolled up/down with PgUp and PgDown keys. If they
+   don't work for you, use Meta-p and Meta-n keys. For jumping to
+   beginning or end of the buffer, use /SB HOME and /SB END commands.
+
+   By default, irssi uses "hidden windows" for everything. Hidden window
+   is created every time you /JOIN a channel or /QUERY someone. There's
+   several ways you can change between these windows:
+     Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
+     Meta-q .. Meta-o          - Jump directly between windows 11-19
+     /WINDOW <number>          - Jump to any window with specified number
+     Ctrl-P, Ctrl-N            - Jump to previous / next window
+
+   Clearly the easiest way is to use Meta-number keys. And what is the
+   Meta key? For some terminals, it's the same as ALT. If you have
+   Windows keyboard, it's probably the left Windows key. If they don't
+   work directly, you'll need to set a few X resources (NOTE: these work
+   with both xterm and rxvt):
+     XTerm*eightBitInput:   false
+     XTerm*metaSendsEscape: true
+
+   With rxvt, you can also specify which key acts as Meta key. So if you
+   want to use ALT instead of Windows key for it, use:
+     rxvt*modifier: alt
+
+   You could do this by changing the X key mappings:
+    xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+
+   And how exactly do you set these X resources? For Debian, there's
+   /etc/X11/Xresources/xterm file where you can put them and it's read
+   automatically when X starts. ~/.Xresources and ~/.Xdefaults files
+   might also work. If you can't get anything else to work, just
+   copy&paste those lines to ~/.Xresources and directly call "xrdb -merge
+   ~/.Xresources" in some xterm. The resources affect only the new xterms
+   you start, not existing ones.
+
+   Many windows SSH clients also don't allow usage of ALT. One excellent
+   client that does allow is putty, you can download it from
+   http://www.chiark.greenend.org.uk/~sgtatham/putty/.
+
+   Irssi also supports split windows, they've had some problems in past
+   but I think they should work pretty well now :) Here's some commands
+   related to them:
+     /WINDOW NEW                    - Create new split window
+     /WINDOW NEW HIDE               - Create new hidden window
+     /WINDOW CLOSE                  - Close split or hidden window
+
+     /WINDOW HIDE [<number>|<name>] - Make the split window hidden window
+     /WINDOW SHOW <number>|<name>   - Make the hidden window a split window
+
+     /WINDOW SHRINK [<lines>]       - Shrink the split window
+     /WINDOW GROW [<lines>]         - Grow the split window
+     /WINDOW BALANCE                - Balance the sizes of all split windows
+
+   By default, irssi uses "sticky windowing" for split windows. This
+   means that windows created inside one split window cannot be moved to
+   another split window without some effort. For example you could have
+   following window layout:
+     Split window 1: win#1 - Status window, win#2 - Messages window
+     Split window 2: win#3 - ircnet/#channel1, win#4 - ircnet/#channel2
+     Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
+
+   When you are in win#1 and press ALT-6, irssi jumps to split window #3
+   and moves the efnet/#channel2 the active window.
+
+   With non-sticky windowing the windows don't have any relationship with
+   split windows, pressing ALT-6 in win#1 moves win#6 to split window 1
+   and sets it active, except if win#6 was already visible in some other
+   split window irssi just changes to that split window. This it the way
+   windows work with ircii, if you prefer it you can set it with
+     /SET autostick_split_windows OFF
+
+   Each window can have multiple channels, queries and other "window
+   items" inside them. If you don't like windows at all, you disable
+   automatic creating of them with
+     /SET autocreate_windows OFF
+
+   If you want to group only some channels or queries in one window, use
+     /JOIN -window #channel
+     /QUERY -window nick
+
+  3. Server and channel automation
+
+   Irssi's multiple IRC network support is IMHO very good - at least
+   compared to other clients :) Even if you're only in one IRC network
+   you should group all your servers to be in the same IRC network as
+   this helps with reconnecting if your primary server breaks and is
+   probably useful in some other ways too :) For information how to
+   actually use irssi correctly with multiple servers see the chapter 6.
+
+   First you need to have your IRC network set, use /IRCNET command to
+   see if it's already there. If it isn't, use /IRCNET ADD yourircnet. To
+   make Irssi work properly with different IRC networks, you might need
+   to give some special settings to /IRCNET ADD, see manual.txt for more
+   information about them. Irssi defaults to IRCNet's behaviour.
+
+   After that you need to add your servers. For example:
+     /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+     /SERVER ADD -auto -ircnet worknet irc.mycompany.com 6667 password
+
+   The -auto option specifies that this server is automatically connected
+   at startup. You don't need to make more than one server with -auto
+   option to one IRC network, other servers are automatically connected
+   in same network if the -auto server fails.
+
+   And finally channels:
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
+                  #irssi efnet
+     /CHANNEL ADD -auto #secret ircnet password
+
+   -bots and -botcmd should be the only ones needing a bit of explaining.
+   They're used to send commands automatically to bot when channel is
+   joined, usually to get ops automatically. You can specify multiple bot
+   masks with -bots option separated with spaces (and remember to quote
+   the string then). The $0 in -botcmd specifies the first found bot in
+   the list. If you don't need the bot masks (ie. the bot is always with
+   the same nick, like chanserv) you can give only the -botcmd option and
+   the command is always sent.
+
+  4. Setting up windows and automatically restoring them at startup
+
+   First connect to all the servers, join the channels and create the
+   queries you want. If you want to move the windows or channels around
+   use commands:
+     /WINDOW MOVE LEFT/RIGHT/number    - move window elsewhere
+     /WINDOW ITEM MOVE <number>|<name> - move channel/query to another window
+
+   When everything looks the way you like, use /LAYOUT SAVE command (and
+   /SAVE, if you don't have autosaving enabled) and when you start irssi
+   next time, irssi remembers the positions of the channels, queries and
+   everything. This "remembering" doesn't mean that simply using /LAYOUT
+   SAVE would automatically make irssi reconnect to all servers and join
+   all channels, you'll need the /SERVER ADD -auto and /CHANNEL ADD -auto
+   commands to do that.
+
+   If you want to change the layout, you just rearrange the layout like
+   you want it and use /LAYOUT SAVE again. If you want to remove the
+   layout for some reason, use /LAYOUT RESET.
+
+  5. Status and msgs windows & message levels
+
+   By default, all the "extra messages" go to status window. This means
+   pretty much all messages that don't clearly belong to some channel or
+   query. Some people like it, some don't. If you want to remove it, use
+     /SET use_status_window OFF
+
+   This doesn't have any effect until you restart irssi. If you want to
+   remove it immediately, just /WINDOW CLOSE it.
+
+   Another common window is "messages window", where all private messages
+   go. By default it's disabled and query windows are created instead. To
+   make all private messages go to msgs window, say:
+     /SET use_msgs_window ON
+     /SET autocreate_query_level DCCMSGS  (or if you don't want queries to
+                                           dcc chats either, say NONE)
+
+   use_msgs_window either doesn't have any effect until restarting irssi.
+   To create it immediately say:
+     /WINDOW NEW HIDE     - create the window
+     /WINDOW NAME (msgs)  - name it to "(msgs)"
+     /WINDOW LEVEL MSGS   - make all private messages go to this window
+     /WINDOW MOVE 1       - move it to first window
+
+   Note that neither use_msgs_window nor use_status_window have any
+   effect at all if /LAYOUT SAVE has been used.
+
+   This brings us to message levels.. What are they? All messages that
+   irssi prints have one or more "message levels". Most common are PUBLIC
+   for public messages in channels, MSGS for private messages and CRAP
+   for all sorts of messages with no real classification. You can get a
+   whole list of levels with
+     /HELP levels
+
+   Status window has message level "ALL -MSGS", meaning that all
+   messages, except private messages, without more specific place go to
+   status window. The -MSGS is there so it doesn't conflict with messages
+   window.
+
+  6. How support for multiple servers works in irssi
+
+   ircii and several other clients support multiple servers by placing
+   the connection into some window. IRSSI DOES NOT. There is no required
+   relationship between window and server. You can connect to 10 servers
+   and manage them all in just one window, or join channel in each one of
+   them to one sigle window if you really want to. That being said,
+   here's how you do connect to new server without closing the old
+   connection:
+     /CONNECT irc.server.org
+
+   Instead of the /SERVER which disconnects the existing connection. To
+   see list of all active connections, use /SERVER without any
+   parameters. You should see a list of something like:
+     -!- IRCNet: irc.telia.fi:6667 (IRCNet)
+     -!- OPN: tolkien.openprojects.net:6667 (OPN)
+     -!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
+
+   Here you see that we're connected to IRCNet and OPN networks. The the
+   IRCNet at the beginning is called the "server tag" while the (IRCnet)
+   at the end shows the IRC network. Server tag specifies unique tag to
+   refer to the server, usually it's the same as the IRC network. When
+   the IRC network isn't known it's some part of the server name. When
+   there's multiple connections to same IRC network or server, irssi adds
+   a number after the tag so there could be ircnet, ircnet2, ircnet3 etc.
+
+   Server tags beginning with RECON- mean server reconnections. Above we
+   see that connection to server at 192.168.0.1 wasn't successful and
+   irssi will try to connect it again in 3 minutes.
+
+   To disconnect one of the servers, or to stop irssi from reconnecting,
+   use
+     /DISCONNECT ircnet   - disconnect server with tag "ircnet"
+     /DISCONNECT recon-1  - stop trying to reconnect to RECON-1 server
+     /RMRECONNS           - stop all server reconnections
+
+     /RECONNECT recon-1   - immediately try reconnecting back to RECON-1
+     /RECONNECT ALL       - immediately try reconnecting back to all
+                            servers in reconnection queue
+
+   Now that you're connected to all your servers, you'll have to know how
+   to specify which one of them you want to use. One way is to have an
+   empty window, like status or msgs window. In it, you can specify which
+   server to set active with
+     /WINDOW SERVER tag    - set server "tag" active
+     Ctrl-X                - set the next server in list active
+
+   When the server is active, you can use it normally. When there's
+   multiple connected servers, irssi adds [servertag] prefix to all
+   messages in non-channel/query messages so you'll know where it came
+   from.
+
+   Several commands also accept -servertag option to specify which server
+   it should use:
+     /MSG -tag nick message
+     /JOIN -tag #channel
+     /QUERY -tag nick
+
+   /MSG tab completion also automatically adds the -tag option when nick
+   isn't in active server.
+
+   Window's server can be made sticky. When sticky, it will never
+   automatically change to anything else, and if server gets
+   disconnected, the window won't have any active server. When the server
+   gets connected again, it is automatically set active in the window. To
+   set the window's server sticky use
+     /WINDOW SERVER -sticky tag
+
+   This is useful if you wish to have multiple status or msgs windows,
+   one for each server. Here's how to do them (repeat for each server)
+     /WINDOW NEW HIDE
+     /WINDOW NAME (status)
+     /WINDOW LEVEL ALL -MSGS
+     /WINDOW SERVER -sticky ircnet
+
+     /WINDOW NEW HIDE
+     /WINDOW NAME (msgs)
+     /WINDOW LEVEL MSGS
+     /WINDOW SERVER -sticky ircnet
+
+  7. /LASTLOG and jumping around in scrollback
+
+   /LASTLOG command can be used for searching texts in scrollback buffer.
+   Simplest usages are
+     /LASTLOG word     - print all lines with "word" in them
+     /LASTLOG word 10  - print last 10 occurances of "word"
+     /LASTLOG -topics  - print all topic changes
+
+   If there's more lines to be printed than 1000, irssi doesn't thinks
+   that you probably made some mistake and won't print them without
+   -force option. If you want to save the full lastlog to file, use
+     /LASTLOG -file ~/irc.log
+
+   With -file option you don't need -force even if there's more than 1000
+   lines. /LASTLOG has a lot of other options too, see /HELP lastlog for
+   details.
+
+   Once you've found the lines you were interested in, you might want to
+   check the discussion around them. Irssi has /SCROLLBACK (or alias /SB)
+   command for jumping around in scrollback buffer. Since /LASTLOG prints
+   the timestamp when the message was originally printed, you can use /SB
+   GOTO hh:mm to jump directly there. To get back to the bottom of
+   scrollback, use /SB END command.
+
+  8. Logging
+
+   Irssi can automatically log important messages when you're set away
+   (/AWAY reason). When you set yourself unaway (/AWAY), the new messages
+   in away log are printed to screen. You can configure it with:
+     /SET awaylog_level MSGS HILIGHT     - Specifies what messages to log
+     /SET awaylog_file ~/.irssi/away.log - Specifies the file to use
+
+   Easiest way to start logging with Irssi is to use autologging. With it
+   Irssi logs all channels and private messages to specified directory.
+   You can turn it on with
+     /SET autolog ON
+
+   By default it logs pretty much everything execept CTCPS or CRAP
+   (/WHOIS requests, etc). You can specify the logging level yourself
+   with
+     /SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
+
+   By default irssi logs to ~/irclogs/<servertag>/<target>.log. You can
+   change this with
+     /SET autolog_path ~/irclogs/$tag/$0.log (this is the default)
+
+   The path is automatically created if it doesn't exist. $0 specifies
+   the target (channel/nick). You can make irssi automatically rotate the
+   logs by adding date/time formats to the file name. The formats are in
+   "man strftime" format. For example
+     /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
+
+   For logging only some specific channels or nicks, see /HELP log
+
+  9. Proxies and IRC bouncers
+
+   Irssi supports connecting to IRC servers via a proxy. All proxies have
+   these settings in common:
+     /SET use_proxy ON
+     /SET proxy_address <Proxy host address>
+     /SET proxy_port <Proxy port>
+
+   HTTP proxy
+
+   Use these settings with HTTP proxies:
+     /SET -clear proxy_password
+     /EVAL SET proxy_string CONNECT %s:%d\n\n
+
+   Irssi proxy
+
+   Irssi contains it's own proxy which you can build giving --with-proxy
+   option to configure. You'll still need to run irssi in a screen to use
+   it though.
+
+   Irssi proxy is a bit different than most proxies, normally proxies
+   create a new connection to IRC server when you connect to it, but with
+   irssi proxy all the clients use the same IRC server connection (a bit
+   like how screen -x works).
+
+   Irssi proxy supports sharing multiple server connections in different
+   ports, like you can share ircnet in port 2777 and efnet in port 2778.
+
+   Usage in proxy side:
+     /LOAD irc_proxy (/LOAD proxy in irssi 0.7.98.3 and older)
+     /SET irssiproxy_password <password>
+     /SET irssiproxy_ports <ircnet>=<port> ... (eg. ircnet=2777 efnet=2778)
+
+   NOTE: you MUST add all the servers you are using to server and ircnet
+   lists with /SERVER ADD and /IRCNET ADD. ..Except if you really don't
+   want to for some reason, and you only use one server connection, you
+   may simply set:
+     /SET irssiproxy_ports *=2777 (irssi 0.7.99 and later only)
+
+   Usage in client side:
+
+   Just connect to the irssi proxy like it is a normal server with
+   password specified in /SET irssiproxy_password. For example:
+     /SERVER ADD -ircnet ircnet my.irssi-proxy.org 2777 secret
+     /SERVER ADD -ircnet efnet my.irssi-proxy.org 2778 secret
+
+   Irssi proxy works fine with other IRC clients as well.
+
+   SOCKS
+   Irssi can be compiled with socks support (--with-socks option to
+   configure), but I don't really know how it works, if at all. /SET
+   proxy settings don't have anything to do with socks however.
+
+   Others
+
+   IRC bouncers usually work like IRC servers, and want a password. You
+   can give it with:
+     /SET proxy_password <password>
+
+   Irssi's default for connect string is
+     /SET proxy_string CONNECT %s %d
+
+   which you can modify according to your bouncer's needs.
+
+  10. Irssi's settings
+
+   You probably don't like Irssi's default settings. I don't like them.
+   But I'm still convinced that they're pretty good defaults. Here's some
+   of them you might want to change (the default value is shown):
+
+   Queries
+
+   /SET autocreate_own_query ON
+          Should new query window be created when you send message to
+          someone (with /msg).
+
+   /SET autocreate_query_level MSGS
+          New query window should be created when receiving messages with
+          this level. MSGS, DCCMSGS and NOTICES levels work currently.
+          You can disable this with /SET -clear autocrate_query_level.
+
+   /SET autoclose_query 0
+          Query windows can be automatically closed after certain time of
+          inactivity. Queries with unread messages aren't closed and
+          active window is neither never closed. The value is given in
+          seconds.
+
+   Windows
+
+   /SET use_msgs_window OFF
+          Create messages window at startup. All private messages go to
+          this window. This only makes sense if you've disabled automatic
+          query windows. Message window can also be created manually with
+          /WINDOW LEVEL MSGS, /WINDOW NAME (msgs).
+
+   /SET use_status_window ON
+          Create status window at startup. All messages that don't really
+          have better place go here, like all /WHOIS replies etc. Status
+          window can also be created manually with /WINDOW LEVEL ALL
+          -MSGS, /WINDOW NAME (status).
+
+   /SET autocreate_windows ON
+          Should we create new windows for new window items or just place
+          everything in one window
+
+   /SET autoclose_windows ON
+          Should window be automatically closed when the last item in
+          them is removed (ie. /PART, /UNQUERY).
+
+   /SET reuse_unused_windows OFF
+          When finding where to place new window item (channel, query)
+          Irssi first tries to use already existing empty windows. If
+          this is set ON, new window will always be created for all
+          window items. This setting is ignored if autoclose_windows is
+          set ON.
+
+   /SET window_auto_change OFF
+          Should Irssi automatically change to automatically created
+          windows - usually queries when someone sends you a message. To
+          prevent accidentally sending text meant to some other
+          channel/nick, Irssi clears the input buffer when changing the
+          window. The text is still in scrollback buffer, you can get it
+          back with pressing arrow up key.
+
+   /SET print_active_channel OFF
+          When you keep more than one channel in same window, Irssi
+          prints the messages coming to active channel as "<nick> text"
+          and other channels as "<nick:channel> text". If this setting is
+          set ON, the messages to active channels are also printed in the
+          latter way.
+
+   /SET window_history OFF
+          Should command history be kept separate for each window.
+
+   User information
+
+   /SET nick
+          Your nick name
+
+   /SET alternate_nick
+          Your alternate nick.
+
+   /SET user_name
+          Your username, if you have ident enabled this doesn't affect
+          anything
+
+   /SET real_name
+          Your real name.
+
+   Server information
+
+   /SET skip_motd OFF
+          Should we hide server's MOTD (Message Of The Day).
+
+   /SET server_reconnect_time 300
+          Seconds to wait before connecting to same server again. Don't
+          set this too low since it usually doesn't help at all - if the
+          host is down, the few extra minutes of waiting won't hurt much.
+
+   /SET lag_max_before_disconnect 300
+          Maximum server lag in seconds before disconnecting and trying
+          to reconnect. This happens mostly only when network breaks
+          between you and IRC server.
+
+   Appearance
+
+   /SET timestamps ON
+          Show timestamps before each message.
+
+   /SET hide_text_style OFF
+          Hide all bolds, underlines, MIRC colors, etc.
+
+   /SET show_nickmode ON
+          Show the nick's mode before nick in channels, ie. ops have
+          <@nick>, voices <+nick> and others < nick>
+
+   /SET show_nickmode_empty ON
+          If the nick doesn't have a mode, use one space. ie. ON:
+          < nick>, OFF: <nick>
+
+   /SET show_quit_once OFF
+          Show quit message only once in some of the channel windows the
+          nick was in instead of in all windows.
+
+   /SET topicbar ON
+          Show the channel's topic in top of screen.
+
+   /SET lag_min_show 100
+          Show the server lag in status bar if it's bigger than this, the
+          unit is 1/100 of seconds (ie. the default value of 100 = 1
+          second).
+
+   /SET indent 10
+          When lines are longer than screen width they have to be split
+          to multiple lines. This specifies how much space to put at the
+          beginning of the line before the text begins. This can be
+          overridden in text formats with %| format.
+
+   /SET activity_hide_targets
+          If you don't want to see window activity in some certain
+          channels or queries, list them here. For example
+          "#boringchannel =bot1 =bot2". If any highlighted text or
+          message for you appears in that window, this setting is ignored
+          and the activity is shown.
+
+   /SET mail_counter ON
+          Show the number of mails in your mbox in status bar. The mbox
+          file is taken from $MAIL environment setting. Only mbox format
+          works for now.
+
+   Nick completion
+
+   /SET completion_auto OFF
+          Automatically complete the nick if line begins with start of
+          nick and the completion character. Learn to use the
+          tab-completion instead, it's a lot better ;)
+
+   /SET completion_char :
+          Completion character to use.
index 0a4104252d5dc05d66d5a0191aec6c9ca1891f0b..3076247c7fdd86a2d4ee9bc3d36b52688d1cc65c 100644 (file)
@@ -3,4 +3,4 @@ PERL_LDFLAGS="@PERL_LDFLAGS@"
 COMMON_LIBS="@COMMON_LIBS@"
 
 CHAT_MODULES="@CHAT_MODULES@"
 COMMON_LIBS="@COMMON_LIBS@"
 
 CHAT_MODULES="@CHAT_MODULES@"
-silc_MODULES="@silc_MODULES@"
+irc_MODULES="@irc_MODULES@"
diff --git a/apps/irssi/irssi-icon.png b/apps/irssi/irssi-icon.png
new file mode 100644 (file)
index 0000000..dd5efd2
Binary files /dev/null and b/apps/irssi/irssi-icon.png differ
index 40f1b21e3672eb41410ae5d39465b4c052d446b0..c15069cf39a83eec444c8e60ead794dc3974ee41 100644 (file)
@@ -4,213 +4,70 @@ Version:     @VERSION@
 Release:       1
 Vendor:        Timo Sirainen <tss@iki.fi>
 Summary:       Irssi is a IRC client
 Release:       1
 Vendor:        Timo Sirainen <tss@iki.fi>
 Summary:       Irssi is a IRC client
-Summary(pl):   Irssi - klient IRC
 Copyright:     GPL
 Group:                 Applications/Communications
 Copyright:     GPL
 Group:                 Applications/Communications
-Group(pl):      Aplikacje/Komunikacja
 URL:           http://irssi.org/
 Source0:       http://irssi.org/irssi/files/%{name}-%{version}.tar.gz
 BuildRequires: glib-devel
 BuildRequires: ncurses-devel
 URL:           http://irssi.org/
 Source0:       http://irssi.org/irssi/files/%{name}-%{version}.tar.gz
 BuildRequires: glib-devel
 BuildRequires: ncurses-devel
-BuildRequires: imlib-devel
-BuildRequires: gtk+-devel
-BuildRequires: gnome-libs-devel
-BuildRequires: XFree86-devel
 BuildRoot:     /tmp/%{name}-%{version}-root
 
 %define                _sysconfdir     /etc
 %define                configure { CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ;  CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ;  FFLAGS="${FFLAGS:-%optflags}" ; export FFLAGS ; ./configure %{_target_platform}     --prefix=%{_prefix} --exec-prefix=%{_exec_prefix} --bindir=%{_bindir} --sbindir=%{_sbindir} --sysconfdir=%{_sysconfdir} --datadir=%{_datadir} --includedir=%{_includedir} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --localstatedir=%{_localstatedir} --sharedstatedir=%{_sharedstatedir} --mandir=%{_mandir} --infodir=%{_infodir} }
 
 %description
 BuildRoot:     /tmp/%{name}-%{version}-root
 
 %define                _sysconfdir     /etc
 %define                configure { CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ;  CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ;  FFLAGS="${FFLAGS:-%optflags}" ; export FFLAGS ; ./configure %{_target_platform}     --prefix=%{_prefix} --exec-prefix=%{_exec_prefix} --bindir=%{_bindir} --sbindir=%{_sbindir} --sysconfdir=%{_sysconfdir} --datadir=%{_datadir} --includedir=%{_includedir} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --localstatedir=%{_localstatedir} --sharedstatedir=%{_sharedstatedir} --mandir=%{_mandir} --infodir=%{_infodir} }
 
 %description
-Irssi is a textUI IRC client with IPv6 support 
-by Timo Sirainen <tss@iki.fi>.
-More information can be found at http://irssi.org/.
-
-%description -l pl
-Irssi jest tekstowym klientem IRC ze wsparciem dla IPv6.
-Napisany zosta³ przez Timo Strainen <tss@iki.fi>
-Wiêcej informacji mo¿na znale¶æ pod adresem
-http://irssi.org/
+Irssi is a modular IRC client that currently has only text
+mode user interface, but 80-90% of the code isn't text mode specific
+so other UI could be created pretty easily. Also, Irssi isn't really
+even IRC specific anymore, there's already a working SILC module
+available. Support for other protocols like ICQ could be create some day
+too.
 
 
-%package GNOME
-Summary:       GNOME version of irssi IRC client
-Summary(pl):   Wersja dla Â¶rodowiska GNOME klienta IRC irssi
-Group:         X11/Applications/Communications
-Group(pl):     X11/Aplikacje/Komunikacja
-Requires:      %{name} = %{version}
-
-%description GNOME
-Irssi is a GTK based (with GNOME) GUI IRC client with IPv6 support
-by Timo Sirainen <tss@iki.fi>.
 More information can be found at http://irssi.org/.
 
 More information can be found at http://irssi.org/.
 
-%description GNOME -l pl
-Irssi jest graficznym klientem IRC ze wsparciem dla IPv6 pracuj±cym
-w Â¶rodowisku GNOME. Napisany zosta³ przez Timo Sirainen <tss@iki.fi>.
-Wiêcej informacji mo¿na znale¶æ pod adresem
-http://irssi.org/
-
 %prep
 %setup -q
 
 %build
 %prep
 %setup -q
 
 %build
-CPPFLAGS="-I/usr/X11R6/include"; export CPPFLAGS
-LDFLAGS="-s -L/usr/X11R6/lib"; export LDFLAGS
+export NOCONFIGURE=x
+./autogen.sh       
 %configure \
 %configure \
-       --with-gnome \
-       --with-gnome-panel \
        --with-imlib \
        --enable-ipv6 \
        --with-imlib \
        --enable-ipv6 \
-       --with-textui=ncurses \
-       --without-socks \
-       --with-plugins
+       --with-textui \
+       --with-socks \
+        --with-bot \
+        --with-proxy \
+        --with-perl=yes \
+       --with-ncurses
 make
 
 %install
 rm -rf $RPM_BUILD_ROOT
 make
 
 %install
 rm -rf $RPM_BUILD_ROOT
-make DESTDIR=$RPM_BUILD_ROOT install
-strip --strip-unneeded $RPM_BUILD_ROOT%{_libdir}/irssi/plugins/lib*.so.*.*
-
-gzip -9fn AUTHORS ChangeLog README TODO NEWS
+make DESTDIR=$RPM_BUILD_ROOT PREFIX=$RPM_BUILD_ROOT/usr install
+mv $RPM_BUILD_ROOT/%{_datadir}/doc/irssi $RPM_BUILD_ROOT/%{_datadir}/doc/irssi-%version
+strip $RPM_BUILD_ROOT/%{_bindir}/*
+strip $RPM_BUILD_ROOT/%{_libdir}/irssi/modules/lib*.so*
+rm -f $RPM_BUILD_ROOT/%{_libdir}/perl5/5.6.0/i386-linux/perllocal.pod
 
 %clean
 rm -rf $RPM_BUILD_ROOT
 
 %files 
 %defattr (644,root,root,755)
 
 %clean
 rm -rf $RPM_BUILD_ROOT
 
 %files 
 %defattr (644,root,root,755)
-%doc {AUTHORS,ChangeLog,README,TODO,NEWS}.gz
-
-%attr(755,root,root) %{_bindir}/irssi
-%attr(755,root,root) %{_bindir}/irssi-text
-%attr(755,root,root) %{_bindir}/irssi-bot
+%doc %{_datadir}/doc/irssi-%version/
 
 
-%dir %{_sysconfdir}/irssi
-%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/irssi/*
+%attr(755,root,root) %{_bindir}/*
 
 
-%dir %{_libdir}/irssi
-%dir %{_libdir}/irssi/plugins
-%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.so.*.*
-%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.so
-#%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.la
-#%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.a
+%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/irssi*
 
 
-%files GNOME
-%defattr (644,root,root,755)
-%attr(755,root,root) %{_bindir}/irssi
+%dir %{_libdir}
+%attr(755,root,root) %{_libdir}/irssi
+%attr(755,root,root) %{_libdir}/perl5
 
 
-%{_sysconfdir}/CORBA/servers/irssi.gnorba
-%{_datadir}/gnome/apps/Network/irssi.desktop
-%{_datadir}/gnome/help/irssi
-%{_datadir}/pixmaps/*
+%dir %{_datadir}/irssi
+%attr(755,root,root) %{_datadir}/irssi/*
 
 
-%define date    %(echo `LC_ALL="C" date +"%a %b %d %Y"`)
 %changelog
 %changelog
-* %{date} PLD Team <pld-list@pld.org.pl>
-All below listed persons can be reached on <cvs_login>@pld.org.pl
-
-$Log$
-Revision 1.1  2001/05/24 12:09:28  priikone
-Initial revision
-
-Revision 1.11  2001/03/29 14:38:28  cras
-http://irssi.org -> http://irssi.org/
-
-Revision 1.10  2000/06/02 01:55:01  cras
-Changed irssi's url to http://irssi.org/
-
-Revision 1.9  2000/04/17 09:37:05  kloczek
-- added pixmaps to %files (irssi have now own icon).
-
-Revision 1.8  2000/04/14 11:27:02  cras
-Sorry for a big update - I still don't have internet connection at home
-and this is what I've been doing a few weeks now.. :) You really shouldn't
-upgrade to this version without keeping a backup of the working one, since
-this will break everything and at least notify list is broken - probably
-something else too.
-
-* On the way to 0.8.0 .. Major rewriting/rearranging code. There's
-  some changes in behaviour because I'm trying to make Irssi a bit
-  more compatible with EPIC.
-
-* libPropList isn't needed anymore - I'm using my own configuration
-  library. This is mostly because different proplists worked a bit
-  differently everywhere and several people had problems with it.
-  It's also yet another extra library that you needed to compile
-  Irssi. New configuration library has several advantages:
-
-  You can add comments to configuration file and they also stay
-  there when it's saved.
-
-  It's not nearly as vulnerable as proplist. If some error occurs,
-  instead of just not reading anything it will try to continue if
-  possible. Also the error messages are written to irssi's text
-  window instead of stdout.
-
-  It can be managed more easily than proplist - setting/getting the
-  configuration is a lot more easier.
-
-* Coding style changes - I'm not using gint, gchar etc. anymore,
-  they're just extra pain when moving code to non-glib projects and
-  syntax hilighting doesn't work by default with most editors ;)
-
-  Indentation style was also changed to K&R because of some political
-  reasons ;) And I'm already starting to like it.. :) It forces me
-  to split code to different functions more often and the result is
-  that the code gets more readable.
-
-  And finally I'm also using nst' all over the place.
-
-+ /EVAL <commands> - Expand all the special variables from string and
-  run it. Commands can be split with ; character. See
-  docs/SPECIAL_VARS for more info.
-+ Aliases are parsed just like /EVAL - arguments are in $0..$9.
-+ Text formats are also parsed like /EVAL, arguments used to be in
-  $1..$9, now they're in $0..$8 so it messes up existing themes..
-+ /SET [key [value]] - no more the '=' character. Boolean values
-  also need to be changed with ON/OFF/TOGGLE values (not yes/no).
-  Settings aren't saved to disk until you use /SAVE.
-+ /TOGGLE <key> [ON/OFF] - same as /SET <key> TOGGLE
-
-Revision 1.7  2000/02/25 17:03:15  cras
-Irssi 0.7.27 released.
-
-Revision 1.6  2000/01/27 19:03:28  cras
-fixes by vkoivula@saunalahti.fi
-
-Revision 1.6  2000/01/25 00:00:00 vkoivula@saunalahti.fi
-- requires libProbList-devel changed to libPropList (problist.h is actually in
-  this package)
-- fixed filelist
-
-Revision 1.5  2000/01/13 02:13:20  kloczek
-- irssi.desktop now this is not applet but application description file -
-  place them in $(datadir)/gnome/apps/Network.
-
-Revision 1.4  1999/10/16 13:05:25  wiget
-- polish translation
-
-Revision 1.3  1999/09/13 16:50:25  wiget
-- fixed %%configure macro
-
-Revision 1.2  1999/09/04 11:42:33  wiget
-- new way to update Version: field in spec
-- new target for make 'make rpm'
-
-Revision 1.4  1999/09/03 09:36:24  wiget
-- updated to 0.7.16alpha-1
-
-Revision 1.3  1999/09/02 17:27:36  wiget
-- added BuildRequires rules
-
-Revision 1.2  1999/09/02 17:22:51  wiget
-- rewrite to PLD style coding:
--- correct Group and Group(pl)
--- %%changelog moved to end
--- splited to irssi and irssi-GNOME
--- added patch to allow 'make install DESTDIR=/some/dir'
--- added ./configure parameters
--- striped unneeded symbol from plugins
--- gziped docs
--- corrected %%files section
-
-- based at spec from tarball (by JT Traub <jtraub@dragoncat.net>)
+* Fri Aug 17 2001 - Joose Vettenranta <joose@iki.fi>
+  Created new spec file from spec file founded in irssi-0.7.98.3
diff --git a/apps/irssi/scripts/.cvsignore b/apps/irssi/scripts/.cvsignore
new file mode 100644 (file)
index 0000000..282522d
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/apps/irssi/scripts/Makefile.am b/apps/irssi/scripts/Makefile.am
new file mode 100644 (file)
index 0000000..7a5a7ab
--- /dev/null
@@ -0,0 +1,12 @@
+scriptdir = $(datadir)/irssi/scripts
+
+script_DATA = \
+       autoop.pl \
+       autorejoin.pl \
+       clones.pl \
+       hello.pl \
+       privmsg.pl \
+       realname.pl \
+       quitmsg.pl
+
+EXTRA_DIST = $(script_DATA)
diff --git a/apps/irssi/scripts/autoop.pl b/apps/irssi/scripts/autoop.pl
new file mode 100644 (file)
index 0000000..413f5e1
--- /dev/null
@@ -0,0 +1,80 @@
+# /AUTOOP <*|#channel> [<nickmasks>]
+
+use Irssi;
+use strict;
+
+my (%opnicks, %temp_opped);
+
+sub cmd_autoop {
+       my ($data) = @_;
+       my ($channel, $masks) = split(" ", $data, 2);
+
+       if ($channel eq "") {
+               if (!%opnicks) {
+                       Irssi::print("Usage: /AUTOOP <*|#channel> [<nickmasks>]");
+                       Irssi::print("No-one's being auto-opped currently.");
+                       return;
+               }
+
+               Irssi::print("Currently auto-opping in channels:");
+               foreach $channel (keys %opnicks) {
+                       $masks = $opnicks{$channel};
+
+                       if ($channel eq "*") {
+                               Irssi::print("All channels: $masks");
+                       } else {
+                               Irssi::print("$channel: $masks");
+                       }
+               }
+               return;
+       }
+
+       if ($masks eq "") {
+               $masks = "<no-one>";
+               delete $opnicks{$channel};
+       } else {
+               $opnicks{$channel} = $masks;
+       }
+       if ($channel eq "*") {
+               Irssi::print("Now auto-opping in all channels: $masks");
+       } else {
+               Irssi::print("$channel: Now auto-opping: $masks");
+       }
+}
+
+sub autoop {
+       my ($channel, $masks, @nicks) = @_;
+       my ($server, $nickrec);
+
+       $server = $channel->{server};
+       foreach $nickrec (@nicks) {
+               my $nick = $nickrec->{nick};
+               my $host = $nickrec->{host};
+
+                if (!$temp_opped{$nick} &&
+                   $server->masks_match($masks, $nick, $host)) {
+                       $channel->command("/op $nick");
+                       $temp_opped{$nick} = 1;
+               }
+       }
+}
+
+sub event_massjoin {
+       my ($channel, $nicks_list) = @_;
+       my @nicks = @{$nicks_list};
+
+       return if (!$channel->{chanop});
+
+       undef %temp_opped;
+
+       # channel specific
+       my $masks = $opnicks{$channel->{name}};
+       autoop($channel, $masks, @nicks) if ($masks);
+
+       # for all channels
+       $masks = $opnicks{"*"};
+       autoop($channel, $masks, @nicks) if ($masks);
+}
+
+Irssi::command_bind('autoop', 'cmd_autoop');
+Irssi::signal_add_last('massjoin', 'event_massjoin');
diff --git a/apps/irssi/scripts/autorejoin.pl b/apps/irssi/scripts/autorejoin.pl
new file mode 100644 (file)
index 0000000..bbbb0ed
--- /dev/null
@@ -0,0 +1,26 @@
+# automatically rejoin to channel after kicked
+
+# NOTE: I personally don't like this feature, in most channels I'm in it
+# will just result as ban. You've probably misunderstood the idea of /KICK
+# if you kick/get kicked all the time "just for fun" ...
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+sub event_rejoin_kick {
+       my ($server, $data) = @_;
+       my ($channel, $nick) = split(/ +/, $data);
+
+       return if ($server->{nick} ne $nick);
+
+       # check if channel has password
+       my $chanrec = $server->channel_find($channel);
+       my $password = $chanrec->{key} if ($chanrec);
+
+       # We have to use send_raw() because the channel record still
+       # exists and irssi won't even try to join to it with command()
+       $server->send_raw("JOIN $channel $password");
+}
+
+Irssi::signal_add('event kick', 'event_rejoin_kick');
diff --git a/apps/irssi/scripts/clones.pl b/apps/irssi/scripts/clones.pl
new file mode 100644 (file)
index 0000000..59c22c3
--- /dev/null
@@ -0,0 +1,43 @@
+# /CLONES - Display clones in the active channel
+# Modified by Roi Dayan. dejavo@punkass.com
+
+use strict;
+
+sub cmd_clones {
+  my ($data, $server, $channel) = @_;
+  my $min_show_count = ($data =~ /^[0-9]+$/) ? $data : 2;
+
+  if (!$channel || $channel->{type} ne "CHANNEL") {
+    Irssi::print("No active channel in window");
+    return;
+  }
+
+  my %hostnames = {};
+  my %hostnicks = {};
+  my @hosttmp = {};
+  foreach my $nick ($channel->nicks()) {
+    my @hosttmp = split(/\@/,$nick->{host});
+    $hostnames{$hosttmp[1]}++;
+    $hostnicks{$hosttmp[1]} = $hostnicks{$hosttmp[1]}.$hostnames{$hosttmp[1]}.". ".$nick->{nick}."!".$nick->{host}."\n";
+    $hostnicks{$hosttmp[1]} =~ s/^,//;
+#    $hostnicks{$hosttmp[1]} =~ s/\n$//;
+  }
+  
+  foreach my $nick (keys %hostnicks) {
+    $hostnicks{$nick} =~ s/\n$//;
+  }
+
+  my $count = 0;
+  foreach my $host (keys %hostnames) {
+    my $clones = $hostnames{$host};
+    if ($clones >= $min_show_count) {
+      $channel->print("Clones:") if ($count == 0);
+      $channel->print("$host: $clones $hostnicks{$host}");
+      $count++;
+    }
+  }
+
+  $channel->print("No clones in channel") if ($count == 0);
+}
+
+Irssi::command_bind('clones', 'cmd_clones');
diff --git a/apps/irssi/scripts/hello.pl b/apps/irssi/scripts/hello.pl
new file mode 100644 (file)
index 0000000..82adfe2
--- /dev/null
@@ -0,0 +1,12 @@
+# "Hello, world!" script :) /hello <nick> sends "Hello, world!" to <nick>
+
+use Irssi;
+use strict;
+
+sub cmd_hello {
+       my ($data, $server, $channel) = @_;
+
+       $server->command("/msg $data Hello, world!");
+}
+
+Irssi::command_bind('hello', 'cmd_hello');
diff --git a/apps/irssi/scripts/mlock.pl b/apps/irssi/scripts/mlock.pl
new file mode 100644 (file)
index 0000000..35ad785
--- /dev/null
@@ -0,0 +1,119 @@
+# /MLOCK <channel> <mode>
+#
+# Locks the channel mode to <mode>, if someone else tries to change the mode
+# Irssi will automatically change it back. +k and +l are a bit special since
+# they require the parameter. If you omit the parameter, like setting the
+# mode to "+ntlk", Irssi will allow all +k and +l (or -lk) mode changes.
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+my %keep_channels;
+
+sub cmd_mlock {
+       my ($data, $server) = @_;
+       my ($channel, $mode) = split(/ /, $data, 2);
+
+       $keep_channels{$channel} = $mode;
+       mlock_check_mode($server, $channel);
+}
+
+sub mlock_check_mode {
+        my ($server, $channame) = @_;
+
+       my $channel = $server->channel_find($channame);
+       return if (!$channel || !$channel->{chanop});
+
+        my $keep_mode = $keep_channels{$channame};
+       return if (!$keep_mode);
+
+       # old channel mode
+       my ($oldmode, $oldkey, $oldlimit);
+       $oldmode = $channel->{mode};
+        $oldmode =~ s/^([^ ]*).*/\1/;
+       $oldkey = $channel->{key};
+       $oldlimit = $channel->{limit};
+
+       # get the new channel key/limit
+       my (@newmodes, $newkey, $limit);
+       @newmodes = split(/ /, $keep_mode); $keep_mode = $newmodes[0];
+       if ($keep_mode =~ /k/) {
+               if ($keep_mode =~ /k.*l/) {
+                        $newkey = $newmodes[1];
+                        $limit = $newmodes[2];
+               } elsif ($keep_mode =~ /l.*k/) {
+                       $limit = $newmodes[1];
+                        $newkey = $newmodes[2];
+               } else {
+                        $newkey = $newmodes[1];
+               }
+       } elsif ($keep_mode =~ /l/) {
+               $limit = $newmodes[1];
+       }
+
+       # check the differences
+       my %allmodes;
+       $keep_mode =~ s/^\+//;
+       for (my $n = 0; $n < length($keep_mode); $n++) {
+               my $modechar = substr($keep_mode, $n, 1);
+               $allmodes{$modechar} = '+';
+       }
+
+       for (my $n = 0; $n < length($oldmode); $n++) {
+               my $modechar = substr($oldmode, $n, 1);
+
+               if ($allmodes{$modechar} eq '+') {
+                       next if (($modechar eq "k" && $newkey ne $oldkey) ||
+                                ($modechar eq "l" && $limit != $oldlimit));
+                       delete $allmodes{$modechar};
+               } else {
+                       $allmodes{$modechar} = '-';
+               }
+       }
+
+       # create the mode change string
+       my ($modecmd, $extracmd);
+       foreach my $mode (keys %allmodes) {
+               Irssi::print("key = '$mode':".$allmodes{$mode});
+               if ($mode eq "k") {
+                       if ($allmodes{$mode} eq '+') {
+                               next if ($newkey eq "");
+                               if ($oldkey ne "") {
+                                       # we need to get rid of old key too
+                                       $modecmd .= "-k";
+                                       $extracmd .= " $oldkey";
+                               }
+                               $extracmd .= " $newkey";
+                       } else {
+                               $extracmd .= " $oldkey";
+                       }
+               }
+               if ($mode eq "l" && $allmodes{$mode} eq '+') {
+                       next if ($limit <= 0);
+                        $extracmd .= " $limit";
+               }
+               $modecmd .= $allmodes{$mode}.$mode;
+       }
+
+       if ($modecmd ne "") {
+               $channel->{server}->command("/mode $channame $modecmd$extracmd");
+       }
+}
+
+sub mlock_mode_changed {
+       my ($server, $data) = @_;
+       my ($channel, $mode) = split(/ /, $data, 2);
+
+       mlock_check_mode($server, $channel);
+}
+
+sub mlock_synced {
+       my $channel = $_[0];
+
+       mlock_check_mode($channel->{server}, $channel->{name});
+}
+
+Irssi::command_bind('mlock', 'cmd_mlock');
+Irssi::signal_add_last("event mode", "mlock_mode_changed");
+Irssi::signal_add("channel synced", "mlock_synced");
diff --git a/apps/irssi/scripts/privmsg.pl b/apps/irssi/scripts/privmsg.pl
new file mode 100644 (file)
index 0000000..b1d119c
--- /dev/null
@@ -0,0 +1,18 @@
+# listen PRIVMSGs - send a notice to yourself when your nick is meantioned
+
+use Irssi;
+use strict;
+
+sub event_privmsg {
+       my ($server, $data, $nick, $address) = @_;
+       my ($target, $text) = $data =~ /^(\S*)\s:(.*)/;
+
+       return if (!$server->ischannel($target));
+
+       my $mynick = $server->{nick};
+       return if ($text !~ /\b$mynick\b/);
+
+       $server->command("/notice $mynick In channel $target, $nick!$address said: $text");
+}
+
+Irssi::signal_add("event privmsg", "event_privmsg");
diff --git a/apps/irssi/scripts/quitmsg.pl b/apps/irssi/scripts/quitmsg.pl
new file mode 100644 (file)
index 0000000..981b15a
--- /dev/null
@@ -0,0 +1,35 @@
+# If quit message isn't given, quit with a random message
+# read from ~/.irssi/irssi.quit
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+my $quitfile = glob "~/.irssi/irssi.quit";
+
+sub cmd_quit {
+       my ($data, $server, $channel) = @_;
+       return if ($data ne "");
+
+       open (f, $quitfile) || return;
+       my $lines = 0; while(<f>) { $lines++; };
+
+       my $line = int(rand($lines))+1;
+
+       my $quitmsg;
+       seek(f, 0, 0); $. = 0;
+       while(<f>) {
+               next if ($. != $line);
+
+               chomp;
+               $quitmsg = $_;
+               last;
+       }
+       close(f);
+
+       foreach my $server (Irssi::servers) {
+               $server->command("/disconnect ".$server->{tag}." $quitmsg");
+       }
+}
+
+Irssi::command_bind('quit', 'cmd_quit');
diff --git a/apps/irssi/scripts/realname.pl b/apps/irssi/scripts/realname.pl
new file mode 100644 (file)
index 0000000..ccb1c6b
--- /dev/null
@@ -0,0 +1,34 @@
+# /RN - display real name of nick
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+sub cmd_realname {
+       my ($data, $server, $channel) = @_;
+
+       $server->send_raw("WHOIS :$data");
+
+       # ignore all whois replies except "No such nick" or the 
+       # first line of the WHOIS reply
+       $server->redirect_event($data, 2,
+                         "event 318", "event empty", -1,
+                         "event 402", "event 402", -1,
+                         "event 401", "event 401", 1,
+                         "event 311", "redir whois", 1,
+                         "event 301", "event empty", 1,
+                         "event 312", "event empty", 1,
+                         "event 313", "event empty", 1,
+                         "event 317", "event empty", 1,
+                         "event 319", "event empty", 1);
+}
+
+sub event_rn_whois {
+       my ($num, $nick, $user, $host, $empty, $realname) = split(/ +/, $_[1], 6);
+       $realname =~ s/^://;
+
+       Irssi::print("%_$nick%_ is $realname");
+}
+
+Irssi::command_bind('rn', 'cmd_realname');
+Irssi::signal_add('redir whois', 'event_rn_whois');
diff --git a/apps/irssi/silc.conf b/apps/irssi/silc.conf
new file mode 100644 (file)
index 0000000..e99332a
--- /dev/null
@@ -0,0 +1,173 @@
+servers = (
+  { address = "silc.silcnet.org"; chatnet = SILCNet; port = 706; },
+  { address = "silc.ytti.fi"; chatnet = SILCNet; port = 706; },
+  { address = "silc.peelo.com"; chatnet = SILCNet; port = 706; },
+  { address = "silc.silcnet.org"; chatnet = SILCNet; port = 707; }
+);
+
+chatnets = {
+  SILCNet = { type = "SILC"; };
+};
+
+channels = (
+  { name = "#silc"; chatnet = silcnet; autojoin = No; }
+);
+
+aliases = {
+  JOIN = "join -window";
+  QUERY = "query -window";
+  LEAVE = "part";
+  BYE = "quit";
+  EXIT = "quit";
+  SIGNOFF = "quit";
+  DESCRIBE = "action";
+  DATE = "time";
+  HOST = "userhost";
+  LAST = "lastlog";
+  SAY = "msg *";
+  WHO = "users *";
+  WI = "whois";
+  WII = "whois $0 $0";
+  WW = "whowas";
+  W = "who";
+  N = "names";
+  M = "msg";
+  T = "topic";
+  C = "clear";
+  CL = "clear";
+  K = "kick";
+  KB = "kickban";
+  KN = "knockout";
+  BANS = "ban";
+  B = "ban";
+  MUB = "unban *";
+  UB = "unban";
+  IG = "ignore";
+  UNIG = "unignore";
+  SB = "scrollback";
+  WC = "window close";
+  WN = "window new hide";
+  GOTO = "sb goto";
+  CHAT = "dcc chat";
+  ADMIN = "info";
+  RUN = "SCRIPT LOAD";
+  UPTIME = "eval exec - expr `date +%s` - \\$F | awk '{print \"Irssi uptime: \"int(\\\\\\$1/3600/24)\"d \"int(\\\\\\$1/3600%24)\"h \"int(\\\\\\$1/60%60)\"m \"int(\\\\\\$1%60)\"s\" }'";
+  CALC = "exec - if which bc &>/dev/null\\; then echo '$*' | bc | awk '{print \"$*=\"$$1}'\\; else echo bc was not found\\; fi";
+};
+
+statusbar = {
+  # formats:
+  # when using {templates}, the template is shown only if it's argument isn't
+  # empty unless no argument is given. for example {sb} is printed always,
+  # but {sb $T} is printed only if $T isn't empty.
+
+  items = {
+    # start/end text in statusbars
+    barstart = "{sbstart}";
+    barend = "{sbend}";
+
+    # treated "normally", you could change the time/user name to whatever
+    time = "{sb $Z}";
+    user = "{sb $cumode$N{sbmode $usermode}{sbaway $A}}";
+    topic = " $topic";
+
+    # treated specially .. window is printed with non-empty windows,
+    # window_empty is printed with empty windows
+    window = "{sb $winref:$T{sbmode $M}}";
+    window_empty = "{sb $winref{sbservertag $tag}}";
+    prompt = "{prompt $[.15]T}";
+    prompt_empty = "{prompt $winname}";
+
+    # all of these treated specially, they're only displayed when needed
+    lag = "{sb Lag: $0-}";
+    act = "{sb Act: $0-}";
+    more = "-- more --";
+  };
+
+  # there's two type of statusbars. root statusbars are either at the top
+  # of the screen or at the bottom of the screen. window statusbars are at
+  # the top/bottom of each split window in screen.
+  default = {
+    # the "default statusbar" to be displayed at the bottom of the window.
+    # contains all the normal items.
+    window = {
+      # window, root
+      type = "window";
+      # top, bottom
+      placement = "bottom";
+      # number
+      position = "1";
+      # active, inactive, always, never (disables the statusbar)
+      visible = "active";
+
+      # list of items in statusbar in the display order
+      items = {
+        barstart = { priority = "100"; };
+        time = { };
+        user = { };
+        window = { };
+        window_empty = { };
+        lag = { priority = "-1"; };
+        act = { priority = "10"; };
+        more = { priority = "-1"; alignment = "right"; };
+        barend = { priority = "100"; alignment = "right"; };
+      };
+    };
+
+    # statusbar to use in inactive split windows
+    window_inact = {
+      type = "window";
+      placement = "bottom";
+      position = "1";
+      visible = "inactive";
+      items = {
+        barstart = { priority = "100"; };
+        window = { };
+       window_empty = { };
+        more = { priority = "-1"; alignment = "right"; };
+        barend = { priority = "100"; alignment = "right"; };
+      };
+    };
+
+    # we treat input line as yet another statusbar :) It's possible to
+    # add other items before or after the input line item.
+    prompt = {
+      type = "root";
+      placement = "bottom";
+      # we want to be at the bottom always
+      position = "100";
+      visible = "always";
+      items = {
+        prompt = { priority = "-1"; };
+        prompt_empty = { priority = "-1"; };
+        # treated specially, this is the real input line.
+        input = { priority = "10"; };
+      };
+    };
+
+    # topicbar
+    topic = {
+      type = "root";
+      placement = "top";
+      position = "1";
+      visible = "always";
+      items = {
+        barstart = { priority = "100"; };
+        topic = { };
+        barend = { priority = "100"; alignment = "right"; };
+      };
+    };
+  };
+};
+
+settings = {
+  "fe-common/core" = {
+    autocreate_own_query = "no";
+    use_status_window = "no";
+    autoclose_windows = "no";
+    use_msgs_window = "no";
+    autocreate_windows = "no";
+    autocreate_query_level = "none";
+  };
+  "fe-text" = { indent = "8"; };
+};
index d8fc1899f0ec9a835ab0eba86291d4a7fee5b59b..a0dc6af1d8a363188f5af702176485a3a3776f4d 100644 (file)
@@ -2,7 +2,15 @@ if BUILD_TEXTUI
 TEXTUI=fe-text
 endif
 
 TEXTUI=fe-text
 endif
 
+if BUILD_IRSSIBOT
+BOTUI=#
+endif
+
+if HAVE_PERL
+PERLDIR=perl
+endif
+
 noinst_HEADERS = \
        common.h
 
 noinst_HEADERS = \
        common.h
 
-SUBDIRS = lib-popt lib-config core silc fe-common $(TEXTUI)
+SUBDIRS = lib-popt lib-config core silc fe-common $(PERLDIR) $(TEXTUI) $(BOTUI)
index c79c5d9ba615ebe335a01ddcce5aea2cb2fe98b4..a4240be50b57525c0997f76ca70f3cef4e10584f 100644 (file)
@@ -4,6 +4,9 @@
 #define IRSSI_AUTHOR "Timo Sirainen <tss@iki.fi>"
 #define IRSSI_WEBSITE "http://irssi.org/"
 
 #define IRSSI_AUTHOR "Timo Sirainen <tss@iki.fi>"
 #define IRSSI_WEBSITE "http://irssi.org/"
 
+#define IRSSI_DIR_SHORT "~/.silc"
+#define IRSSI_DIR_FULL "%s/.silc" /* %s == g_get_home_dir() */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #  include <gmodule.h>
 #endif
 
 #  include <gmodule.h>
 #endif
 
-#include "core/memdebug.h"
-
-#define g_free_not_null(a) \
-       G_STMT_START { \
-         if (a) g_free(a); \
-       } G_STMT_END
-
-#define g_free_and_null(a) \
-       G_STMT_START { \
-         if (a) { g_free(a); (a) = NULL; } \
-       } G_STMT_END
-
+/* input functions */
 #define G_INPUT_READ   (1 << 0)
 #define G_INPUT_WRITE  (1 << 1)
 
 #define G_INPUT_READ   (1 << 0)
 #define G_INPUT_WRITE  (1 << 1)
 
@@ -65,8 +57,38 @@ int g_input_add(GIOChannel *source, int condition,
 int g_input_add_full(GIOChannel *source, int priority, int condition,
                     GInputFunction function, void *data);
 
 int g_input_add_full(GIOChannel *source, int priority, int condition,
                     GInputFunction function, void *data);
 
+/* return full path for ~/.irssi */
+const char *get_irssi_dir(void);
+/* return full path for ~/.irssi/config */
+const char *get_irssi_config(void);
+
+/* max. size for %d */
 #define MAX_INT_STRLEN ((sizeof(int) * CHAR_BIT + 2) / 3 + 1)
 
 #define MAX_INT_STRLEN ((sizeof(int) * CHAR_BIT + 2) / 3 + 1)
 
+#define g_free_not_null(a) g_free(a)
+
+#define g_free_and_null(a) \
+       G_STMT_START { \
+         if (a) { g_free(a); (a) = NULL; } \
+       } G_STMT_END
+
+/* ctype.h isn't safe with chars, use our own instead */
+#define i_toupper(x) toupper((int) (unsigned char) (x))
+#define i_tolower(x) tolower((int) (unsigned char) (x))
+#define i_isalnum(x) isalnum((int) (unsigned char) (x))
+#define i_isalpha(x) isalpha((int) (unsigned char) (x))
+#define i_isascii(x) isascii((int) (unsigned char) (x))
+#define i_isblank(x) isblank((int) (unsigned char) (x))
+#define i_iscntrl(x) iscntrl((int) (unsigned char) (x))
+#define i_isdigit(x) isdigit((int) (unsigned char) (x))
+#define i_isgraph(x) isgraph((int) (unsigned char) (x))
+#define i_islower(x) islower((int) (unsigned char) (x))
+#define i_isprint(x) isprint((int) (unsigned char) (x))
+#define i_ispunct(x) ispunct((int) (unsigned char) (x))
+#define i_isspace(x) isspace((int) (unsigned char) (x))
+#define i_isupper(x) isupper((int) (unsigned char) (x))
+#define i_isxdigit(x) isxdigit((int) (unsigned char) (x))
+
 typedef struct _IPADDR IPADDR;
 typedef struct _CONFIG_REC CONFIG_REC;
 typedef struct _CONFIG_NODE CONFIG_NODE;
 typedef struct _IPADDR IPADDR;
 typedef struct _CONFIG_REC CONFIG_REC;
 typedef struct _CONFIG_NODE CONFIG_NODE;
@@ -86,4 +108,6 @@ typedef struct _SERVER_CONNECT_REC SERVER_CONNECT_REC;
 typedef struct _SERVER_SETUP_REC SERVER_SETUP_REC;
 typedef struct _CHANNEL_SETUP_REC CHANNEL_SETUP_REC;
 
 typedef struct _SERVER_SETUP_REC SERVER_SETUP_REC;
 typedef struct _CHANNEL_SETUP_REC CHANNEL_SETUP_REC;
 
+typedef struct _WINDOW_REC WINDOW_REC;
+
 #endif
 #endif
diff --git a/apps/irssi/src/core/.cvsignore b/apps/irssi/src/core/.cvsignore
new file mode 100644 (file)
index 0000000..8553e9e
--- /dev/null
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
index 1443bc91cc5a8186498e3aff04a8b425231c5dfc..655d291a7da6fcbd6a757a64e901bc07236490e8 100644 (file)
@@ -3,17 +3,11 @@ noinst_LIBRARIES = libcore.a
 include $(top_srcdir)/Makefile.defines.in
 
 INCLUDES = \
 include $(top_srcdir)/Makefile.defines.in
 
 INCLUDES = \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/core \
        $(GLIB_CFLAGS) \
        -DSYSCONFDIR=\""$(silc_etcdir)"\" \
        $(GLIB_CFLAGS) \
        -DSYSCONFDIR=\""$(silc_etcdir)"\" \
-       -DMODULEDIR=\""$(silc_modulesdir)"\" \
-       -I$(top_srcdir)/src \
-       -I$(top_srcdir)/src/core
-
-if BUILD_MEMDEBUG
-memdebug_src=memdebug.c
-else
-memdebug_src=
-endif
+       -DMODULEDIR=\""$(silc_modulesdir)"\"
 
 libcore_a_SOURCES = \
        args.c \
 
 libcore_a_SOURCES = \
        args.c \
@@ -29,10 +23,11 @@ libcore_a_SOURCES = \
         levels.c \
        line-split.c \
        log.c \
         levels.c \
        line-split.c \
        log.c \
+       log-away.c \
        masks.c \
        masks.c \
-        $(memdebug_src) \
        misc.c \
        modules.c \
        misc.c \
        modules.c \
+       modules-load.c \
        net-disconnect.c \
        net-nonblock.c \
        net-sendbuffer.c \
        net-disconnect.c \
        net-nonblock.c \
        net-sendbuffer.c \
@@ -44,8 +39,8 @@ libcore_a_SOURCES = \
        rawlog.c \
        servers.c \
        servers-reconnect.c \
        rawlog.c \
        servers.c \
        servers-reconnect.c \
-       servers-redirect.c \
        servers-setup.c \
        servers-setup.c \
+       session.c \
        settings.c \
        signals.c \
        special-vars.c \
        settings.c \
        signals.c \
        special-vars.c \
@@ -75,10 +70,10 @@ noinst_HEADERS = \
        line-split.h \
        log.h \
        masks.h \
        line-split.h \
        log.h \
        masks.h \
-        memdebug.h \
        misc.h \
        module.h \
        modules.h \
        misc.h \
        module.h \
        modules.h \
+       modules-load.h \
        net-disconnect.h \
        net-nonblock.h \
        net-sendbuffer.h \
        net-disconnect.h \
        net-nonblock.h \
        net-sendbuffer.h \
@@ -91,14 +86,11 @@ noinst_HEADERS = \
        rawlog.h \
        servers.h \
        servers-reconnect.h \
        rawlog.h \
        servers.h \
        servers-reconnect.h \
-       servers-redirect.h \
        servers-setup.h \
        servers-setup.h \
+       session.h \
        settings.h \
        signals.h \
        special-vars.h \
        window-item-def.h \
        write-buffer.h \
        $(structure_headers)
        settings.h \
        signals.h \
        special-vars.h \
        window-item-def.h \
        write-buffer.h \
        $(structure_headers)
-
-EXTRA_DIST = \
-       memdebug.c
index 093a8d569e651b550b35468cdef5b55e9d4b2b49..ab26ee14b22b97c5d9f3bbab6eee7013a873257a 100644 (file)
@@ -61,4 +61,6 @@ void args_execute(int argc, char *argv[])
 
        g_array_free(iopt_tables, TRUE);
        iopt_tables = NULL;
 
        g_array_free(iopt_tables, TRUE);
        iopt_tables = NULL;
+
+        poptFreeContext(con);
 }
 }
index 2ff6a536d24d6f3eb883562cad2a4417fdd6ccf7..f3ec5f8d494863e23dfb9448c0c93951eba44f8b 100644 (file)
@@ -22,6 +22,7 @@ unsigned int synced:1; /* Channel synced - all queries done */
 unsigned int joined:1; /* Have we even received JOIN event for this channel? */
 unsigned int left:1; /* You just left the channel */
 unsigned int kicked:1; /* You just got kicked */
 unsigned int joined:1; /* Have we even received JOIN event for this channel? */
 unsigned int left:1; /* You just left the channel */
 unsigned int kicked:1; /* You just got kicked */
+unsigned int session_rejoin:1; /* This channel was joined with /UPGRADE */
 unsigned int destroying:1;
 
 /* Return the information needed to call SERVER_REC->channels_join() for
 unsigned int destroying:1;
 
 /* Return the information needed to call SERVER_REC->channels_join() for
index a53bd3f31798496b83653c9811089b27326fcc48..ac9b1c23eb5c7ccd0b1695631cc126c9ef9f114a 100644 (file)
@@ -118,13 +118,6 @@ static CHANNEL_SETUP_REC *channel_setup_read(CONFIG_NODE *node)
 
        channel = config_node_get_str(node, "name", NULL);
         chatnet = config_node_get_str(node, "chatnet", NULL);
 
        channel = config_node_get_str(node, "name", NULL);
         chatnet = config_node_get_str(node, "chatnet", NULL);
-       if (chatnet == NULL) /* FIXME: remove this after .98... */ {
-               chatnet = g_strdup(config_node_get_str(node, "ircnet", NULL));
-               if (chatnet != NULL) {
-                        iconfig_node_set_str(node, "chatnet", chatnet);
-                        iconfig_node_set_str(node, "ircnet", NULL);
-               }
-       }
 
        chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
        if (channel == NULL || chatnetrec == NULL) {
 
        chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
        if (channel == NULL || chatnetrec == NULL) {
@@ -158,7 +151,8 @@ static void channels_read_config(void)
        /* Read channels */
        node = iconfig_node_traverse("channels", FALSE);
        if (node != NULL) {
        /* Read channels */
        node = iconfig_node_traverse("channels", FALSE);
        if (node != NULL) {
-               for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
                        channel_setup_read(tmp->data);
        }
 }
                        channel_setup_read(tmp->data);
        }
 }
index f3d3a7a45ef0bd313a09268014e2b0a0a3f2c2e4..57288f7e7013383d64ca13c31f6f55d18a0817a3 100644 (file)
@@ -48,6 +48,7 @@ void channel_init(CHANNEL_REC *channel, int automatic)
 
         MODULE_DATA_INIT(channel);
        channel->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
 
         MODULE_DATA_INIT(channel);
        channel->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
+        channel->destroy = (void (*) (WI_ITEM_REC *)) channel_destroy;
         channel->mode = g_strdup("");
        channel->createtime = time(NULL);
         channel->get_join_data = get_join_data;
         channel->mode = g_strdup("");
        channel->createtime = time(NULL);
         channel->get_join_data = get_join_data;
@@ -74,6 +75,8 @@ void channel_destroy(CHANNEL_REC *channel)
        g_free_not_null(channel->key);
        g_free(channel->mode);
        g_free(channel->name);
        g_free_not_null(channel->key);
        g_free(channel->mode);
        g_free(channel->name);
+
+        channel->type = 0;
        g_free(channel);
 }
 
        g_free(channel);
 }
 
@@ -113,17 +116,48 @@ CHANNEL_REC *channel_find(SERVER_REC *server, const char *name)
                                   (void *) name);
 }
 
                                   (void *) name);
 }
 
+static CHANNEL_REC *channel_find_servers(GSList *servers, const char *name)
+{
+       return gslist_foreach_find(servers,
+                                  (FOREACH_FIND_FUNC) channel_find_server,
+                                  (void *) name);
+}
+
+static GSList *servers_find_chatnet_except(SERVER_REC *server)
+{
+       GSList *tmp, *list;
+
+        list = NULL;
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *rec = tmp->data;
+
+               if (server != rec && rec->connrec->chatnet != NULL &&
+                   strcmp(server->connrec->chatnet,
+                          rec->connrec->chatnet) == 0) {
+                       /* chatnets match */
+                       list = g_slist_append(list, rec);
+               }
+       }
+
+        return list;
+}
+
 /* connected to server, autojoin to channels. */
 static void event_connected(SERVER_REC *server)
 {
        GString *chans;
 /* connected to server, autojoin to channels. */
 static void event_connected(SERVER_REC *server)
 {
        GString *chans;
-       GSList *tmp;
+       GSList *tmp, *chatnet_servers;
 
        g_return_if_fail(SERVER(server));
 
 
        g_return_if_fail(SERVER(server));
 
-       if (server->connrec->reconnection)
+       if (server->connrec->reconnection ||
+           server->connrec->no_autojoin_channels)
                return;
 
                return;
 
+       /* get list of servers in same chat network */
+       chatnet_servers = server->connrec->chatnet == NULL ? NULL:
+               servers_find_chatnet_except(server);
+
        /* join to the channels marked with autojoin in setup */
        chans = g_string_new(NULL);
        for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
        /* join to the channels marked with autojoin in setup */
        chans = g_string_new(NULL);
        for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
@@ -134,8 +168,12 @@ static void event_connected(SERVER_REC *server)
                                           server->connrec->chatnet))
                        continue;
 
                                           server->connrec->chatnet))
                        continue;
 
-               g_string_sprintfa(chans, "%s,", rec->name);
+               /* check that we haven't already joined this channel in
+                  same chat network connection.. */
+                if (channel_find_servers(chatnet_servers, rec->name) == NULL)
+                       g_string_sprintfa(chans, "%s,", rec->name);
        }
        }
+        g_slist_free(chatnet_servers);
 
        if (chans->len > 0) {
                g_string_truncate(chans, chans->len-1);
 
        if (chans->len > 0) {
                g_string_truncate(chans, chans->len-1);
@@ -165,6 +203,9 @@ void channel_send_autocommands(CHANNEL_REC *channel)
 
        g_return_if_fail(IS_CHANNEL(channel));
 
 
        g_return_if_fail(IS_CHANNEL(channel));
 
+       if (channel->session_rejoin)
+                return;
+
        rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
        if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
                return;
        rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
        if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
                return;
@@ -180,6 +221,9 @@ void channel_send_autocommands(CHANNEL_REC *channel)
        for (bot = bots; *bot != NULL; bot++) {
                const char *botnick = *bot;
 
        for (bot = bots; *bot != NULL; bot++) {
                const char *botnick = *bot;
 
+               if (*botnick == '\0')
+                        continue;
+
                nick = nicklist_find_mask(channel,
                                          channel->server->isnickflag(*botnick) ?
                                          botnick+1 : botnick);
                nick = nicklist_find_mask(channel,
                                          channel->server->isnickflag(*botnick) ?
                                          botnick+1 : botnick);
index 78a05a70b39a8a7e0840370f171cfbe793713fbe..8b1b37389a0d3fde42589fe48e46d9e51aa95a44 100644 (file)
 #include "channels.h"
 #include "queries.h"
 #include "window-item-def.h"
 #include "channels.h"
 #include "queries.h"
 #include "window-item-def.h"
+#include "rawlog.h"
 
 
-static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr)
+static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
+                                             char **rawlog_file)
 {
         CHAT_PROTOCOL_REC *proto;
        SERVER_CONNECT_REC *conn;
 {
         CHAT_PROTOCOL_REC *proto;
        SERVER_CONNECT_REC *conn;
@@ -73,7 +75,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr)
        if (proto->not_initialized) {
                /* trying to use protocol that isn't yet initialized */
                signal_emit("chat protocol unknown", 1, proto->name);
        if (proto->not_initialized) {
                /* trying to use protocol that isn't yet initialized */
                signal_emit("chat protocol unknown", 1, proto->name);
-               server_connect_free(conn);
+               server_connect_unref(conn);
                 cmd_params_free(free_arg);
                return NULL;
        }
                 cmd_params_free(free_arg);
                return NULL;
        }
@@ -83,6 +85,14 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr)
        else if (g_hash_table_lookup(optlist, "4") != NULL)
                conn->family = AF_INET;
 
        else if (g_hash_table_lookup(optlist, "4") != NULL)
                conn->family = AF_INET;
 
+       if (g_hash_table_lookup(optlist, "!") != NULL)
+               conn->no_autojoin_channels = TRUE;
+
+       if (g_hash_table_lookup(optlist, "noproxy") != NULL)
+                g_free_and_null(conn->proxy);
+
+       *rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog"));
+
         host = g_hash_table_lookup(optlist, "host");
        if (host != NULL && *host != '\0') {
                IPADDR ip4, ip6;
         host = g_hash_table_lookup(optlist, "host");
        if (host != NULL && *host != '\0') {
                IPADDR ip4, ip6;
@@ -100,10 +110,19 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr)
 static void cmd_connect(const char *data)
 {
        SERVER_CONNECT_REC *conn;
 static void cmd_connect(const char *data)
 {
        SERVER_CONNECT_REC *conn;
+       SERVER_REC *server;
+        char *rawlog_file;
+
+       conn = get_server_connect(data, NULL, &rawlog_file);
+       if (conn != NULL) {
+               server = CHAT_PROTOCOL(conn)->server_connect(conn);
+                server_connect_unref(conn);
+
+               if (server != NULL && rawlog_file != NULL)
+                       rawlog_open(server->rawlog, rawlog_file);
 
 
-       conn = get_server_connect(data, NULL);
-        if (conn != NULL)
-               CHAT_PROTOCOL(conn)->server_connect(conn);
+               g_free(rawlog_file);
+       }
 }
 
 static RECONNECT_REC *find_reconnect_server(int chat_type,
 }
 
 static RECONNECT_REC *find_reconnect_server(int chat_type,
@@ -147,6 +166,7 @@ static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server)
 
        if (server != NULL) {
                oldconn = server->connrec;
 
        if (server != NULL) {
                oldconn = server->connrec;
+                server_connect_ref(oldconn);
                 reconnect_save_status(conn, server);
        } else {
                /* maybe we can reconnect some server from
                 reconnect_save_status(conn, server);
        } else {
                /* maybe we can reconnect some server from
@@ -156,7 +176,8 @@ static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server)
                if (recon == NULL) return;
 
                oldconn = recon->conn;
                if (recon == NULL) return;
 
                oldconn = recon->conn;
-               server_reconnect_destroy(recon, FALSE);
+                server_connect_ref(oldconn);
+               server_reconnect_destroy(recon);
 
                conn->away_reason = g_strdup(oldconn->away_reason);
                conn->channels = g_strdup(oldconn->channels);
 
                conn->away_reason = g_strdup(oldconn->away_reason);
                conn->channels = g_strdup(oldconn->channels);
@@ -167,29 +188,46 @@ static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server)
        if (conn->chatnet == NULL && oldconn->chatnet != NULL)
                conn->chatnet = g_strdup(oldconn->chatnet);
 
        if (conn->chatnet == NULL && oldconn->chatnet != NULL)
                conn->chatnet = g_strdup(oldconn->chatnet);
 
+       server_connect_unref(oldconn);
        if (server != NULL) {
                signal_emit("command disconnect", 2,
                            "* Changing server", server);
        if (server != NULL) {
                signal_emit("command disconnect", 2,
                            "* Changing server", server);
-       } else {
-               server_connect_free(oldconn);
        }
 }
 
        }
 }
 
+static void cmd_server(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       command_runsub("server", data, server, item);
+}
+
+static void sig_default_command_server(const char *data, SERVER_REC *server,
+                                      WI_ITEM_REC *item)
+{
+        signal_emit("command server connect", 3, data, server, item);
+}
+
 /* SYNTAX: SERVER [-4 | -6] [-ircnet <ircnet>] [-host <hostname>]
                   [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */
 /* SYNTAX: SERVER [-4 | -6] [-ircnet <ircnet>] [-host <hostname>]
                   [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */
-static void cmd_server(const char *data, SERVER_REC *server)
+static void cmd_server_connect(const char *data, SERVER_REC *server)
 {
        SERVER_CONNECT_REC *conn;
 {
        SERVER_CONNECT_REC *conn;
+        char *rawlog_file;
        int plus_addr;
 
        g_return_if_fail(data != NULL);
 
         /* create connection record */
        int plus_addr;
 
        g_return_if_fail(data != NULL);
 
         /* create connection record */
-       conn = get_server_connect(data, &plus_addr);
+       conn = get_server_connect(data, &plus_addr, &rawlog_file);
        if (conn != NULL) {
                if (!plus_addr)
                        update_reconnection(conn, server);
        if (conn != NULL) {
                if (!plus_addr)
                        update_reconnection(conn, server);
-               CHAT_PROTOCOL(conn)->server_connect(conn);
+               server = CHAT_PROTOCOL(conn)->server_connect(conn);
+               server_connect_unref(conn);
+
+               if (server != NULL && rawlog_file != NULL)
+                       rawlog_open(server->rawlog, rawlog_file);
+
+               g_free(rawlog_file);
        }
 }
 
        }
 }
 
@@ -247,36 +285,36 @@ static void cmd_join(const char *data, SERVER_REC *server)
        void *free_arg;
 
        g_return_if_fail(data != NULL);
        void *free_arg;
 
        g_return_if_fail(data != NULL);
-       if (!IS_SERVER(server) || !server->connected)
-               cmd_return_error(CMDERR_NOT_CONNECTED);
 
        if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
                            PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
                            "join", &optlist, &channels))
                return;
 
 
        if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
                            PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
                            "join", &optlist, &channels))
                return;
 
+       /* -<server tag> */
+       server = cmd_options_get_server("join", optlist, server);
+       if (server == NULL || !server->connected)
+                cmd_param_error(CMDERR_NOT_CONNECTED);
+
        if (g_hash_table_lookup(optlist, "invite"))
                channels = server->last_invite;
        else {
                if (*channels == '\0')
                        cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
        if (g_hash_table_lookup(optlist, "invite"))
                channels = server->last_invite;
        else {
                if (*channels == '\0')
                        cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
-
-               /* -<server tag> */
-               server = cmd_options_get_server("join", optlist, server);
        }
 
        }
 
-       if (server != NULL && channels != NULL)
+       if (channels != NULL)
                server->channels_join(server, channels, FALSE);
        cmd_params_free(free_arg);
 }
 
                server->channels_join(server, channels, FALSE);
        cmd_params_free(free_arg);
 }
 
-/* SYNTAX: MSG [-<server tag>] <targets> <message> */
+/* SYNTAX: MSG [-<server tag>] [-channel | -nick] <targets> <message> */
 static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        GHashTable *optlist;
        char *target, *origtarget, *msg;
        void *free_arg;
 static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        GHashTable *optlist;
        char *target, *origtarget, *msg;
        void *free_arg;
-       int free_ret;
+       int free_ret, target_type;
 
        g_return_if_fail(data != NULL);
 
 
        g_return_if_fail(data != NULL);
 
@@ -297,13 +335,34 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
                                       NULL, &free_ret, NULL, 0);
                if (target != NULL && *target == '\0')
                        target = NULL;
                                       NULL, &free_ret, NULL, 0);
                if (target != NULL && *target == '\0')
                        target = NULL;
-       } else if (strcmp(target, "*") == 0 && item != NULL)
+       }
+
+       if (strcmp(target, "*") == 0) {
+                /* send to active channel/query */
+               if (item == NULL)
+                       cmd_param_error(CMDERR_NOT_JOINED);
+
+               target_type = IS_CHANNEL(item) ?
+                       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
                target = item->name;
                target = item->name;
+       }
+       else if (g_hash_table_lookup(optlist, "channel") != NULL)
+                target_type = SEND_TARGET_CHANNEL;
+       else if (g_hash_table_lookup(optlist, "nick") != NULL)
+               target_type = SEND_TARGET_NICK;
+       else {
+               /* Need to rely on server_ischannel(). If the protocol
+                  doesn't really know if it's channel or nick based on the
+                  name, it should just assume it's nick, because when typing
+                  text to channels it's always sent with /MSG -channel. */
+               target_type = server_ischannel(server, target) ?
+                       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+       }
 
        if (target != NULL)
 
        if (target != NULL)
-               server->send_message(server, target, msg);
+               server->send_message(server, target, msg, target_type);
 
 
-       signal_emit(target != NULL && server->ischannel(target) ?
+       signal_emit(target != NULL && target_type == SEND_TARGET_CHANNEL ?
                    "message own_public" : "message own_private", 4,
                    server, msg, target, origtarget);
 
                    "message own_public" : "message own_private", 4,
                    server, msg, target, origtarget);
 
@@ -320,33 +379,40 @@ static void cmd_foreach(const char *data, SERVER_REC *server,
 /* SYNTAX: FOREACH SERVER <command> */
 static void cmd_foreach_server(const char *data, SERVER_REC *server)
 {
 /* SYNTAX: FOREACH SERVER <command> */
 static void cmd_foreach_server(const char *data, SERVER_REC *server)
 {
-       GSList *tmp;
+        GSList *list;
 
 
-       for (tmp = servers; tmp != NULL; tmp = tmp->next)
-               signal_emit("send command", 3, data, tmp->data, NULL);
+       list = g_slist_copy(servers);
+       while (list != NULL) {
+               signal_emit("send command", 3, data, list->data, NULL);
+                list = g_slist_remove(list, list->data);
+       }
 }
 
 /* SYNTAX: FOREACH CHANNEL <command> */
 static void cmd_foreach_channel(const char *data)
 {
 }
 
 /* SYNTAX: FOREACH CHANNEL <command> */
 static void cmd_foreach_channel(const char *data)
 {
-       GSList *tmp;
+        GSList *list;
 
 
-       for (tmp = channels; tmp != NULL; tmp = tmp->next) {
-               CHANNEL_REC *rec = tmp->data;
+       list = g_slist_copy(channels);
+       while (list != NULL) {
+               CHANNEL_REC *rec = list->data;
 
                signal_emit("send command", 3, data, rec->server, rec);
 
                signal_emit("send command", 3, data, rec->server, rec);
+                list = g_slist_remove(list, list->data);
        }
 }
 
 /* SYNTAX: FOREACH QUERY <command> */
 static void cmd_foreach_query(const char *data)
 {
        }
 }
 
 /* SYNTAX: FOREACH QUERY <command> */
 static void cmd_foreach_query(const char *data)
 {
-       GSList *tmp;
+        GSList *list;
 
 
-       for (tmp = queries; tmp != NULL; tmp = tmp->next) {
-               QUERY_REC *rec = tmp->data;
+       list = g_slist_copy(queries);
+       while (list != NULL) {
+               QUERY_REC *rec = list->data;
 
                signal_emit("send command", 3, data, rec->server, rec);
 
                signal_emit("send command", 3, data, rec->server, rec);
+                list = g_slist_remove(list, list->data);
        }
 }
 
        }
 }
 
@@ -355,6 +421,7 @@ void chat_commands_init(void)
        settings_add_str("misc", "quit_message", "leaving");
 
        command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
        settings_add_str("misc", "quit_message", "leaving");
 
        command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
+       command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
        command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect);
        command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
        command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit);
        command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect);
        command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
        command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit);
@@ -365,13 +432,17 @@ void chat_commands_init(void)
        command_bind("foreach channel", NULL, (SIGNAL_FUNC) cmd_foreach_channel);
        command_bind("foreach query", NULL, (SIGNAL_FUNC) cmd_foreach_query);
 
        command_bind("foreach channel", NULL, (SIGNAL_FUNC) cmd_foreach_channel);
        command_bind("foreach query", NULL, (SIGNAL_FUNC) cmd_foreach_query);
 
-       command_set_options("connect", "4 6 +host");
+        signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
+
+       command_set_options("connect", "4 6 !! +host noproxy -rawlog");
        command_set_options("join", "invite");
        command_set_options("join", "invite");
+       command_set_options("msg", "channel nick");
 }
 
 void chat_commands_deinit(void)
 {
        command_unbind("server", (SIGNAL_FUNC) cmd_server);
 }
 
 void chat_commands_deinit(void)
 {
        command_unbind("server", (SIGNAL_FUNC) cmd_server);
+       command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
        command_unbind("connect", (SIGNAL_FUNC) cmd_connect);
        command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
        command_unbind("quit", (SIGNAL_FUNC) cmd_quit);
        command_unbind("connect", (SIGNAL_FUNC) cmd_connect);
        command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
        command_unbind("quit", (SIGNAL_FUNC) cmd_quit);
@@ -381,4 +452,6 @@ void chat_commands_deinit(void)
        command_unbind("foreach server", (SIGNAL_FUNC) cmd_foreach_server);
        command_unbind("foreach channel", (SIGNAL_FUNC) cmd_foreach_channel);
        command_unbind("foreach query", (SIGNAL_FUNC) cmd_foreach_query);
        command_unbind("foreach server", (SIGNAL_FUNC) cmd_foreach_server);
        command_unbind("foreach channel", (SIGNAL_FUNC) cmd_foreach_channel);
        command_unbind("foreach query", (SIGNAL_FUNC) cmd_foreach_query);
+
+        signal_remove("default command server", (SIGNAL_FUNC) sig_default_command_server);
 }
 }
index ab7c09d4bdac0221a294d19196ac625a909f978b..2989598cbd2c5184c1f6e9bb898e051274ae8a09 100644 (file)
@@ -156,7 +156,13 @@ void chat_protocol_unregister(const char *name)
        g_return_if_fail(name != NULL);
 
        rec = chat_protocol_find(name);
        g_return_if_fail(name != NULL);
 
        rec = chat_protocol_find(name);
-       if (rec != NULL) chat_protocol_destroy(rec);
+       if (rec != NULL) {
+               chat_protocol_destroy(rec);
+
+               /* there might still be references to this chat protocol -
+                  recreate it as a dummy protocol */
+               chat_protocol_get_unknown(name);
+       }
 }
 
 /* Default chat protocol to use */
 }
 
 /* Default chat protocol to use */
@@ -190,6 +196,10 @@ static SERVER_CONNECT_REC *create_server_connect(void)
         return g_new0(SERVER_CONNECT_REC, 1);
 }
 
         return g_new0(SERVER_CONNECT_REC, 1);
 }
 
+static void destroy_server_connect(SERVER_CONNECT_REC *conn)
+{
+}
+
 /* Return "unknown chat protocol" record. Used when protocol name is
    specified but it isn't registered yet. */
 CHAT_PROTOCOL_REC *chat_protocol_get_unknown(const char *name)
 /* Return "unknown chat protocol" record. Used when protocol name is
    specified but it isn't registered yet. */
 CHAT_PROTOCOL_REC *chat_protocol_get_unknown(const char *name)
@@ -209,6 +219,7 @@ CHAT_PROTOCOL_REC *chat_protocol_get_unknown(const char *name)
         rec->create_server_setup = create_server_setup;
         rec->create_channel_setup = create_channel_setup;
        rec->create_server_connect = create_server_connect;
         rec->create_server_setup = create_server_setup;
         rec->create_channel_setup = create_channel_setup;
        rec->create_server_connect = create_server_connect;
+       rec->destroy_server_connect = destroy_server_connect;
 
        newrec = chat_protocol_register(rec);
        g_free(rec);
 
        newrec = chat_protocol_register(rec);
        g_free(rec);
index cc7eaadc5da85869e7b065d1e0ecfe67687157fe..eeb0075f33f9bf1ff816364da53713b595f119c6 100644 (file)
@@ -5,6 +5,7 @@ typedef struct {
        int id;
 
        unsigned int not_initialized:1;
        int id;
 
        unsigned int not_initialized:1;
+       unsigned int case_insensitive:1;
 
        char *name;
        char *fullname;
 
        char *name;
        char *fullname;
@@ -14,6 +15,7 @@ typedef struct {
        SERVER_SETUP_REC *(*create_server_setup) (void);
         CHANNEL_SETUP_REC *(*create_channel_setup) (void);
        SERVER_CONNECT_REC *(*create_server_connect) (void);
        SERVER_SETUP_REC *(*create_server_setup) (void);
         CHANNEL_SETUP_REC *(*create_channel_setup) (void);
        SERVER_CONNECT_REC *(*create_server_connect) (void);
+        void (*destroy_server_connect) (SERVER_CONNECT_REC *);
 
         SERVER_REC *(*server_connect) (SERVER_CONNECT_REC *);
         CHANNEL_REC *(*channel_create) (SERVER_REC *, const char *, int);
 
         SERVER_REC *(*server_connect) (SERVER_CONNECT_REC *);
         CHANNEL_REC *(*channel_create) (SERVER_REC *, const char *, int);
index a2cff529c9a167e20691e5294e3f0b695251428c..8739ff7d4cbf7f6f2b154f6d6641ef246fdb7a57 100644 (file)
@@ -162,39 +162,30 @@ static void chatnet_read(CONFIG_NODE *node)
 static void read_chatnets(void)
 {
        CONFIG_NODE *node;
 static void read_chatnets(void)
 {
        CONFIG_NODE *node;
+        GSList *tmp;
 
        while (chatnets != NULL)
                 chatnet_destroy(chatnets->data);
 
        node = iconfig_node_traverse("chatnets", FALSE);
 
        while (chatnets != NULL)
                 chatnet_destroy(chatnets->data);
 
        node = iconfig_node_traverse("chatnets", FALSE);
-       if (node == NULL) {
-               /* FIXME: remove after .98 */
-               node = iconfig_node_traverse("ircnets", FALSE);
-               if (node != NULL) {
-                       /* very dirty method - doesn't update hashtables
-                          but this will do temporarily.. */
-                       g_free(node->key);
-                        node->key = g_strdup("chatnets");
-               }
+       if (node != NULL) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                        chatnet_read(tmp->data);
        }
        }
-
-       if (node != NULL)
-                g_slist_foreach(node->value, (GFunc) chatnet_read, NULL);
 }
 
 void chatnets_init(void)
 {
        chatnets = NULL;
 
 }
 
 void chatnets_init(void)
 {
        chatnets = NULL;
 
-       signal_add("event connected", (SIGNAL_FUNC) sig_connected);
+       signal_add_first("event connected", (SIGNAL_FUNC) sig_connected);
        signal_add("setup reread", (SIGNAL_FUNC) read_chatnets);
         signal_add_first("irssi init read settings", (SIGNAL_FUNC) read_chatnets);
 }
 
 void chatnets_deinit(void)
 {
        signal_add("setup reread", (SIGNAL_FUNC) read_chatnets);
         signal_add_first("irssi init read settings", (SIGNAL_FUNC) read_chatnets);
 }
 
 void chatnets_deinit(void)
 {
-       while (chatnets != NULL)
-               chatnet_destroy(chatnets->data);
        module_uniq_destroy("CHATNET");
 
        signal_remove("event connected", (SIGNAL_FUNC) sig_connected);
        module_uniq_destroy("CHATNET");
 
        signal_remove("event connected", (SIGNAL_FUNC) sig_connected);
index c4c889085d70d216053786eab9a4c70a79a43a49..a96b5fa009d48ee1f749490b1819b39835130c68 100644 (file)
@@ -26,7 +26,6 @@
 #include "window-item-def.h"
 
 #include "servers.h"
 #include "window-item-def.h"
 
 #include "servers.h"
-#include "servers-redirect.h"
 #include "channels.h"
 
 #include "lib-config/iconfig.h"
 #include "channels.h"
 
 #include "lib-config/iconfig.h"
@@ -84,7 +83,7 @@ static COMMAND_MODULE_REC *command_module_find_func(COMMAND_REC *rec,
        for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
                COMMAND_MODULE_REC *rec = tmp->data;
 
        for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
                COMMAND_MODULE_REC *rec = tmp->data;
 
-                if (g_slist_find(rec->signals, func) != NULL)
+                if (g_slist_find(rec->signals, (void *) func) != NULL)
                        return rec;
        }
 
                        return rec;
        }
 
@@ -111,8 +110,8 @@ int command_have_sub(const char *command)
        return FALSE;
 }
 
        return FALSE;
 }
 
-static COMMAND_MODULE_REC *command_module_get(COMMAND_REC *rec,
-                                             const char *module)
+static COMMAND_MODULE_REC *
+command_module_get(COMMAND_REC *rec, const char *module, int protocol)
 {
         COMMAND_MODULE_REC *modrec;
 
 {
         COMMAND_MODULE_REC *modrec;
 
@@ -122,14 +121,18 @@ static COMMAND_MODULE_REC *command_module_get(COMMAND_REC *rec,
        if (modrec == NULL) {
                modrec = g_new0(COMMAND_MODULE_REC, 1);
                modrec->name = g_strdup(module);
        if (modrec == NULL) {
                modrec = g_new0(COMMAND_MODULE_REC, 1);
                modrec->name = g_strdup(module);
+                modrec->protocol = -1;
                rec->modules = g_slist_append(rec->modules, modrec);
        }
 
                rec->modules = g_slist_append(rec->modules, modrec);
        }
 
+        if (protocol != -1)
+               modrec->protocol = protocol;
+
         return modrec;
 }
 
 void command_bind_to(const char *module, int pos, const char *cmd,
         return modrec;
 }
 
 void command_bind_to(const char *module, int pos, const char *cmd,
-                    const char *category, SIGNAL_FUNC func)
+                    int protocol, const char *category, SIGNAL_FUNC func)
 {
        COMMAND_REC *rec;
         COMMAND_MODULE_REC *modrec;
 {
        COMMAND_REC *rec;
         COMMAND_MODULE_REC *modrec;
@@ -145,9 +148,9 @@ void command_bind_to(const char *module, int pos, const char *cmd,
                rec->category = category == NULL ? NULL : g_strdup(category);
                commands = g_slist_append(commands, rec);
        }
                rec->category = category == NULL ? NULL : g_strdup(category);
                commands = g_slist_append(commands, rec);
        }
-        modrec = command_module_get(rec, module);
+        modrec = command_module_get(rec, module, protocol);
 
 
-        modrec->signals = g_slist_append(modrec->signals, func);
+        modrec->signals = g_slist_append(modrec->signals, (void *) func);
 
        if (func != NULL) {
                str = g_strconcat("command ", cmd, NULL);
 
        if (func != NULL) {
                str = g_strconcat("command ", cmd, NULL);
@@ -221,7 +224,10 @@ void command_unbind(const char *cmd, SIGNAL_FUNC func)
        rec = command_find(cmd);
        if (rec != NULL) {
                modrec = command_module_find_func(rec, func);
        rec = command_find(cmd);
        if (rec != NULL) {
                modrec = command_module_find_func(rec, func);
-               modrec->signals = g_slist_remove(modrec->signals, func);
+               g_return_if_fail(modrec != NULL);
+
+               modrec->signals =
+                       g_slist_remove(modrec->signals, (void *) func);
                if (modrec->signals == NULL)
                        command_module_destroy(rec, modrec);
        }
                if (modrec->signals == NULL)
                        command_module_destroy(rec, modrec);
        }
@@ -283,6 +289,8 @@ void command_runsub(const char *cmd, const char *data,
 
        g_return_if_fail(data != NULL);
 
 
        g_return_if_fail(data != NULL);
 
+        while (*data == ' ') data++;
+
        if (*data == '\0') {
                 /* no subcommand given - list the subcommands */
                signal_emit("list subcommands", 2, cmd);
        if (*data == '\0') {
                 /* no subcommand given - list the subcommands */
                signal_emit("list subcommands", 2, cmd);
@@ -431,7 +439,7 @@ void command_set_options_module(const char *module,
 
         rec = command_find(cmd);
        g_return_if_fail(rec != NULL);
 
         rec = command_find(cmd);
        g_return_if_fail(rec != NULL);
-        modrec = command_module_get(rec, module);
+        modrec = command_module_get(rec, module, -1);
 
        reload = modrec->options != NULL;
         if (reload) {
 
        reload = modrec->options != NULL;
         if (reload) {
@@ -550,15 +558,17 @@ static int get_cmd_options(char **data, int ignore_unknown,
                        }
 
                        (*data)++;
                        }
 
                        (*data)++;
-                       if (**data == '-' && isspace((*data)[1])) {
+                       if (**data == '-' && i_isspace((*data)[1])) {
                                /* -- option means end of options even
                                   if next word starts with - */
                                (*data)++;
                                /* -- option means end of options even
                                   if next word starts with - */
                                (*data)++;
-                               while (isspace(**data)) (*data)++;
+                               while (i_isspace(**data)) (*data)++;
                                break;
                        }
 
                                break;
                        }
 
-                       if (!isspace(**data))
+                       if (**data == '\0')
+                               option = "-";
+                       else if (!i_isspace(**data))
                                option = cmd_get_param(data);
                        else {
                                option = "-";
                                option = cmd_get_param(data);
                        else {
                                option = "-";
@@ -568,6 +578,18 @@ static int get_cmd_options(char **data, int ignore_unknown,
                        /* check if this option can have argument */
                        pos = optlist == NULL ? -1 :
                                option_find(optlist, option);
                        /* check if this option can have argument */
                        pos = optlist == NULL ? -1 :
                                option_find(optlist, option);
+
+                       if (pos == -1 && optlist != NULL &&
+                           is_numeric(option, '\0')) {
+                               /* check if we want -<number> option */
+                               pos = option_find(optlist, "#");
+                               if (pos != -1) {
+                                       g_hash_table_insert(options, "#",
+                                                           option);
+                                        pos = -3;
+                               }
+                       }
+
                        if (pos == -1 && !ignore_unknown) {
                                /* unknown option! */
                                 *data = option;
                        if (pos == -1 && !ignore_unknown) {
                                /* unknown option! */
                                 *data = option;
@@ -584,21 +606,21 @@ static int get_cmd_options(char **data, int ignore_unknown,
                                option = optlist[pos] +
                                        iscmdtype(*optlist[pos]);
                        }
                                option = optlist[pos] +
                                        iscmdtype(*optlist[pos]);
                        }
-                       if (options != NULL)
+                       if (options != NULL && pos != -3)
                                g_hash_table_insert(options, option, "");
 
                        if (pos < 0 || !iscmdtype(*optlist[pos]) ||
                            *optlist[pos] == '!')
                                option = NULL;
 
                                g_hash_table_insert(options, option, "");
 
                        if (pos < 0 || !iscmdtype(*optlist[pos]) ||
                            *optlist[pos] == '!')
                                option = NULL;
 
-                       while (isspace(**data)) (*data)++;
+                       while (i_isspace(**data)) (*data)++;
                        continue;
                }
 
                if (option == NULL)
                        break;
 
                        continue;
                }
 
                if (option == NULL)
                        break;
 
-               if (*optlist[pos] == '@' && !isdigit(**data))
+               if (*optlist[pos] == '@' && !i_isdigit(**data))
                        break; /* expected a numeric argument */
 
                /* save the argument */
                        break; /* expected a numeric argument */
 
                /* save the argument */
@@ -609,7 +631,7 @@ static int get_cmd_options(char **data, int ignore_unknown,
                }
                option = NULL;
 
                }
                option = NULL;
 
-               while (isspace(**data)) (*data)++;
+               while (i_isspace(**data)) (*data)++;
        }
 
        return 0;
        }
 
        return 0;
@@ -620,7 +642,8 @@ typedef struct {
         GHashTable *options;
 } CMD_TEMP_REC;
 
         GHashTable *options;
 } CMD_TEMP_REC;
 
-static char *get_optional_channel(WI_ITEM_REC *active_item, char **data)
+static char *get_optional_channel(WI_ITEM_REC *active_item, char **data,
+                                 int require_name)
 {
         CHANNEL_REC *chanrec;
        char *tmp, *origtmp, *channel, *ret;
 {
         CHANNEL_REC *chanrec;
        char *tmp, *origtmp, *channel, *ret;
@@ -633,15 +656,19 @@ static char *get_optional_channel(WI_ITEM_REC *active_item, char **data)
        origtmp = tmp = g_strdup(*data);
        channel = cmd_get_param(&tmp);
 
        origtmp = tmp = g_strdup(*data);
        channel = cmd_get_param(&tmp);
 
-       if (strcmp(channel, "*") == 0 ||
-            !active_item->server->ischannel(channel))
-                ret = active_item->name;
-       else {
+       if (strcmp(channel, "*") == 0 && !require_name) {
+                /* "*" means active channel */
+               cmd_get_param(data);
+               ret = active_item->name;
+       } else if (!server_ischannel(active_item->server, channel)) {
+                /* we don't have channel parameter - use active channel */
+               ret = active_item->name;
+       } else {
                /* Find the channel first and use it's name if found.
                   This allows automatic !channel -> !XXXXXchannel replaces. */
                 channel = cmd_get_param(data);
 
                /* Find the channel first and use it's name if found.
                   This allows automatic !channel -> !XXXXXchannel replaces. */
                 channel = cmd_get_param(data);
 
-                chanrec = channel_find(active_item->server, channel);
+               chanrec = channel_find(active_item->server, channel);
                ret = chanrec == NULL ? channel : chanrec->name;
        }
 
                ret = chanrec == NULL ? channel : chanrec->name;
        }
 
@@ -656,7 +683,7 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...)
        GHashTable **opthash;
        char **str, *arg, *datad;
        va_list args;
        GHashTable **opthash;
        char **str, *arg, *datad;
        va_list args;
-       int cnt, error, ignore_unknown;
+       int cnt, error, ignore_unknown, require_name;
 
        g_return_val_if_fail(data != NULL, FALSE);
 
 
        g_return_val_if_fail(data != NULL, FALSE);
 
@@ -669,8 +696,8 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...)
         datad = rec->data;
        error = FALSE;
 
         datad = rec->data;
        error = FALSE;
 
-        item = (count & PARAM_FLAG_OPTCHAN) == 0 ? NULL:
-                (WI_ITEM_REC *) va_arg(args, WI_ITEM_REC *);
+       item = (count & PARAM_FLAG_OPTCHAN) == 0 ? NULL:
+               (WI_ITEM_REC *) va_arg(args, WI_ITEM_REC *);
 
        if (count & PARAM_FLAG_OPTIONS) {
                arg = (char *) va_arg(args, char *);
 
        if (count & PARAM_FLAG_OPTIONS) {
                arg = (char *) va_arg(args, char *);
@@ -690,7 +717,8 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...)
                cnt = PARAM_WITHOUT_FLAGS(count);
                if (count & PARAM_FLAG_OPTCHAN) {
                        /* optional channel as first parameter */
                cnt = PARAM_WITHOUT_FLAGS(count);
                if (count & PARAM_FLAG_OPTCHAN) {
                        /* optional channel as first parameter */
-                        arg = get_optional_channel(item, &datad);
+                        require_name = (count & PARAM_FLAG_OPTCHAN_NAME);
+                       arg = get_optional_channel(item, &datad, require_name);
 
                        str = (char **) va_arg(args, char **);
                        if (str != NULL) *str = arg;
 
                        str = (char **) va_arg(args, char **);
                        if (str != NULL) *str = arg;
@@ -741,7 +769,7 @@ static void command_module_unbind_all(COMMAND_REC *rec,
        for (tmp = modrec->signals; tmp != NULL; tmp = next) {
                next = tmp->next;
 
        for (tmp = modrec->signals; tmp != NULL; tmp = next) {
                next = tmp->next;
 
-               command_unbind(rec->cmd, tmp->data);
+               command_unbind(rec->cmd, (SIGNAL_FUNC) tmp->data);
        }
 
        if (g_slist_find(commands, rec) != NULL) {
        }
 
        if (g_slist_find(commands, rec) != NULL) {
@@ -767,6 +795,28 @@ void commands_remove_module(const char *module)
        }
 }
 
        }
 }
 
+static int cmd_protocol_match(COMMAND_REC *cmd, SERVER_REC *server)
+{
+       GSList *tmp;
+
+       for (tmp = cmd->modules; tmp != NULL; tmp = tmp->next) {
+               COMMAND_MODULE_REC *rec = tmp->data;
+
+               if (rec->protocol == -1) {
+                       /* at least one module accepts the command
+                          without specific protocol */
+                       return 1;
+               }
+
+               if (server != NULL && rec->protocol == server->chat_type) {
+                        /* matching protocol found */
+                        return 1;
+               }
+       }
+
+        return 0;
+}
+
 #define alias_runstack_push(alias) \
        alias_runstack = g_slist_append(alias_runstack, alias)
 
 #define alias_runstack_push(alias) \
        alias_runstack = g_slist_append(alias_runstack, alias)
 
@@ -779,6 +829,7 @@ void commands_remove_module(const char *module)
 static void parse_command(const char *command, int expand_aliases,
                          SERVER_REC *server, void *item)
 {
 static void parse_command(const char *command, int expand_aliases,
                          SERVER_REC *server, void *item)
 {
+        COMMAND_REC *rec;
        const char *alias, *newcmd;
        char *cmd, *orig, *args, *oldcmd;
 
        const char *alias, *newcmd;
        char *cmd, *orig, *args, *oldcmd;
 
@@ -808,17 +859,32 @@ static void parse_command(const char *command, int expand_aliases,
                return;
        }
 
                return;
        }
 
-       cmd = g_strconcat("command ", newcmd, NULL);
-       if (server != NULL)
-               server_redirect_default(SERVER(server), cmd);
+       rec = command_find(newcmd);
+       if (rec != NULL && !cmd_protocol_match(rec, server)) {
+               g_free(orig);
 
 
+               signal_emit("error command", 2,
+                           GINT_TO_POINTER(server == NULL ?
+                                           CMDERR_NOT_CONNECTED :
+                                           CMDERR_ILLEGAL_PROTO));
+               return;
+       }
+
+       cmd = g_strconcat("command ", newcmd, NULL);
        g_strdown(cmd);
        g_strdown(cmd);
+
        oldcmd = current_command;
        current_command = cmd+8;
        oldcmd = current_command;
        current_command = cmd+8;
-       if (!signal_emit(cmd, 3, args, server, item)) {
+        if (server != NULL) server_ref(server);
+        if (!signal_emit(cmd, 3, args, server, item)) {
                signal_emit_id(signal_default_command, 3,
                               command, server, item);
        }
                signal_emit_id(signal_default_command, 3,
                               command, server, item);
        }
+       if (server != NULL) {
+               if (server->connection_lost)
+                       server_disconnect(server);
+               server_unref(server);
+       }
        current_command = oldcmd;
 
        g_free(cmd);
        current_command = oldcmd;
 
        g_free(cmd);
index 9dee2e8035f8cfee86f01abc0ca9875d8e62edd0..3e55a2adc04f0e7d9ab40504e5251b402786309b 100644 (file)
@@ -6,6 +6,7 @@
 typedef struct {
        char *name;
        char *options;
 typedef struct {
        char *name;
        char *options;
+        int protocol; /* chat protocol required for this command */
         GSList *signals;
 } COMMAND_MODULE_REC;
 
         GSList *signals;
 } COMMAND_MODULE_REC;
 
@@ -26,10 +27,11 @@ enum {
 
         CMDERR_ERRNO, /* get the error from errno */
        CMDERR_NOT_ENOUGH_PARAMS, /* not enough parameters given */
 
         CMDERR_ERRNO, /* get the error from errno */
        CMDERR_NOT_ENOUGH_PARAMS, /* not enough parameters given */
-       CMDERR_NOT_CONNECTED, /* not connected to IRC server */
+       CMDERR_NOT_CONNECTED, /* not connected to server */
        CMDERR_NOT_JOINED, /* not joined to any channels in this window */
        CMDERR_CHAN_NOT_FOUND, /* channel not found */
        CMDERR_CHAN_NOT_SYNCED, /* channel not fully synchronized yet */
        CMDERR_NOT_JOINED, /* not joined to any channels in this window */
        CMDERR_CHAN_NOT_FOUND, /* channel not found */
        CMDERR_CHAN_NOT_SYNCED, /* channel not fully synchronized yet */
+       CMDERR_ILLEGAL_PROTO, /* requires different chat protocol than the active server */
        CMDERR_NOT_GOOD_IDEA /* not good idea to do, -yes overrides this */
 };
 
        CMDERR_NOT_GOOD_IDEA /* not good idea to do, -yes overrides this */
 };
 
@@ -56,10 +58,14 @@ extern char *current_command; /* the command we're right now. */
 
 /* Bind command to specified function. */
 void command_bind_to(const char *module, int pos, const char *cmd,
 
 /* Bind command to specified function. */
 void command_bind_to(const char *module, int pos, const char *cmd,
-                    const char *category, SIGNAL_FUNC func);
-#define command_bind(a, b, c) command_bind_to(MODULE_NAME, 1, a, b, c)
-#define command_bind_first(a, b, c) command_bind_to(MODULE_NAME, 0, a, b, c)
-#define command_bind_last(a, b, c) command_bind_to(MODULE_NAME, 2, a, b, c)
+                     int protocol, const char *category, SIGNAL_FUNC func);
+#define command_bind(a, b, c) command_bind_to(MODULE_NAME, 1, a, -1, b, c)
+#define command_bind_first(a, b, c) command_bind_to(MODULE_NAME, 0, a, -1, b, c)
+#define command_bind_last(a, b, c) command_bind_to(MODULE_NAME, 2, a, -1, b, c)
+
+#define command_bind_proto(a, b, c, d) command_bind_to(MODULE_NAME, 1, a, b, c, d)
+#define command_bind_proto_first(a, b, c, d) command_bind_to(MODULE_NAME, 0, a, b, c, d)
+#define command_bind_proto_last(a, b, c, d) command_bind_to(MODULE_NAME, 2, a, b, c, d)
 
 void command_unbind(const char *cmd, SIGNAL_FUNC func);
 
 
 void command_unbind(const char *cmd, SIGNAL_FUNC func);
 
@@ -129,6 +135,8 @@ int command_have_option(const char *cmd, const char *option);
 #define PARAM_FLAG_UNKNOWN_OPTIONS 0x00008000
 /* optional channel in first argument */
 #define PARAM_FLAG_OPTCHAN 0x00010000
 #define PARAM_FLAG_UNKNOWN_OPTIONS 0x00008000
 /* optional channel in first argument */
 #define PARAM_FLAG_OPTCHAN 0x00010000
+/* optional channel in first argument, but don't treat "*" as current channel */
+#define PARAM_FLAG_OPTCHAN_NAME 0x00030000
 
 char *cmd_get_param(char **data);
 /* get parameters from command - you should point free_me somewhere and
 
 char *cmd_get_param(char **data);
 /* get parameters from command - you should point free_me somewhere and
index fd7459db8d0b1997bc376f0a6a18b0c295783a8e..2321208eb086c999c7dd21e00d44ef88671eccf0 100644 (file)
 */
 
 #include "module.h"
 */
 
 #include "module.h"
+#include <signal.h>
 
 
+#include "args.h"
 #include "pidwait.h"
 #include "pidwait.h"
+#include "misc.h"
 
 #include "net-disconnect.h"
 #include "net-sendbuffer.h"
 #include "signals.h"
 #include "settings.h"
 
 #include "net-disconnect.h"
 #include "net-sendbuffer.h"
 #include "signals.h"
 #include "settings.h"
+#include "session.h"
 
 #include "chat-protocols.h"
 #include "servers.h"
 
 #include "chat-protocols.h"
 #include "servers.h"
 #include "nicklist.h"
 #include "nickmatch-cache.h"
 
 #include "nicklist.h"
 #include "nickmatch-cache.h"
 
+#ifdef HAVE_SYS_RESOURCE_H
+#  include <sys/resource.h>
+   struct rlimit orig_core_rlimit;
+#endif
+
 void chat_commands_init(void);
 void chat_commands_deinit(void);
 
 void chat_commands_init(void);
 void chat_commands_deinit(void);
 
+void log_away_init(void);
+void log_away_deinit(void);
+
 int irssi_gui;
 int irssi_gui;
+int irssi_init_finished;
+
+static char *irssi_dir, *irssi_config_file;
+static GSList *dialog_type_queue, *dialog_text_queue;
+
+const char *get_irssi_dir(void)
+{
+        return irssi_dir;
+}
+
+/* return full path for ~/.irssi/config */
+const char *get_irssi_config(void)
+{
+        return irssi_config_file;
+}
+
+static void read_settings(void)
+{
+#ifndef WIN32
+       static int signals[] = {
+               SIGHUP, SIGINT, SIGQUIT, SIGTERM,
+               SIGALRM, SIGUSR1, SIGUSR2
+       };
+       static char *signames[] = {
+               "hup", "int", "quit", "term",
+               "alrm", "usr1", "usr2"
+       };
+
+       const char *ignores;
+       struct sigaction act;
+        int n;
+
+       ignores = settings_get_str("ignore_signals");
+
+       sigemptyset (&act.sa_mask);
+       act.sa_flags = 0;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               act.sa_handler = find_substr(ignores, signames[n]) ?
+                       SIG_IGN : SIG_DFL;
+               sigaction(signals[n], &act, NULL);
+       }
+
+#ifdef HAVE_SYS_RESOURCE_H
+       if (!settings_get_bool("override_coredump_limit"))
+               setrlimit(RLIMIT_CORE, &orig_core_rlimit);
+       else {
+               struct rlimit rlimit;
+
+                rlimit.rlim_cur = RLIM_INFINITY;
+                rlimit.rlim_max = RLIM_INFINITY;
+               if (setrlimit(RLIMIT_CORE, &rlimit) == -1)
+                        settings_set_bool("override_coredump_limit", FALSE);
+       }
+#endif
+#endif
+}
+
+static void sig_gui_dialog(const char *type, const char *text)
+{
+       dialog_type_queue = g_slist_append(dialog_type_queue, g_strdup(type));
+       dialog_text_queue = g_slist_append(dialog_text_queue, g_strdup(text));
+}
+
+static void sig_init_finished(void)
+{
+       GSList *type, *text;
+
+        signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+
+       /* send the dialog texts that were in queue before irssi
+          was initialized */
+       type = dialog_type_queue;
+        text = dialog_text_queue;
+       for (; text != NULL; text = text->next, type = type->next) {
+               signal_emit("gui dialog", 2, type->data, text->data);
+               g_free(type->data);
+                g_free(text->data);
+       }
+        g_slist_free(dialog_type_queue);
+        g_slist_free(dialog_text_queue);
+}
+
+void core_init_paths(int argc, char *argv[])
+{
+       static struct poptOption options[] = {
+               { "config", 0, POPT_ARG_STRING, NULL, 0, "Configuration file location (~/.irssi/config)", "PATH" },
+               { "home", 0, POPT_ARG_STRING, NULL, 0, "Irssi home dir location (~/.irssi)", "PATH" },
+               { NULL, '\0', 0, NULL }
+       };
+       char *str;
+       int n, len;
+
+       for (n = 1; n < argc; n++) {
+               if (strncmp(argv[n], "--home=", 7) == 0) {
+                        g_free_not_null(irssi_dir);
+                        irssi_dir = convert_home(argv[n]+7);
+                        len = strlen(irssi_dir);
+                       if (irssi_dir[len-1] == G_DIR_SEPARATOR)
+                               irssi_dir[len-1] = '\0';
+               } else if (strncmp(argv[n], "--config=", 9) == 0) {
+                        g_free_not_null(irssi_config_file);
+                       irssi_config_file = convert_home(argv[n]+9);
+               }
+       }
+
+       if (irssi_dir != NULL && !g_path_is_absolute(irssi_dir)) {
+               str = irssi_dir;
+               irssi_dir = g_strdup_printf("%s/%s", g_get_current_dir(), str);
+               g_free(str);
+       }
+
+       if (irssi_config_file != NULL &&
+           !g_path_is_absolute(irssi_config_file)) {
+               str = irssi_config_file;
+               irssi_config_file =
+                       g_strdup_printf("%s/%s", g_get_current_dir(), str);
+               g_free(str);
+       }
+
+       args_register(options);
+
+        if (irssi_dir == NULL)
+               irssi_dir = g_strdup_printf(IRSSI_DIR_FULL, g_get_home_dir());
+       if (irssi_config_file == NULL)
+               irssi_config_file = g_strdup_printf("%s/config", irssi_dir);
+
+       session_set_binary(argv[0]);
+}
 
 
-void core_init(void)
+static void sig_irssi_init_finished(void)
 {
 {
+        irssi_init_finished = TRUE;
+}
+
+void core_init(int argc, char *argv[])
+{
+       dialog_type_queue = NULL;
+       dialog_text_queue = NULL;
+
        modules_init();
 #ifndef WIN32
        pidwait_init();
        modules_init();
 #ifndef WIN32
        pidwait_init();
@@ -57,9 +207,14 @@ void core_init(void)
        net_disconnect_init();
        net_sendbuffer_init();
        signals_init();
        net_disconnect_init();
        net_sendbuffer_init();
        signals_init();
+
+       signal_add_first("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
+       signal_add_first("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+
        settings_init();
        commands_init();
        settings_init();
        commands_init();
-        nickmatch_cache_init();
+       nickmatch_cache_init();
+        session_init();
 
        chat_protocols_init();
        chatnets_init();
 
        chat_protocols_init();
        chatnets_init();
@@ -68,6 +223,7 @@ void core_init(void)
        servers_init();
         write_buffer_init();
        log_init();
        servers_init();
         write_buffer_init();
        log_init();
+       log_away_init();
        rawlog_init();
 
        channels_init();
        rawlog_init();
 
        channels_init();
@@ -75,11 +231,27 @@ void core_init(void)
        nicklist_init();
 
        chat_commands_init();
        nicklist_init();
 
        chat_commands_init();
-        settings_check();
+
+       settings_add_str("misc", "ignore_signals", "");
+       settings_add_bool("misc", "override_coredump_limit", TRUE);
+
+#ifdef HAVE_SYS_RESOURCE_H
+       getrlimit(RLIMIT_CORE, &orig_core_rlimit);
+#endif
+       read_settings();
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_add("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished);
+
+       settings_check();
+
+        module_register("core", "core");
 }
 
 void core_deinit(void)
 {
 }
 
 void core_deinit(void)
 {
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished);
+
        chat_commands_deinit();
 
        nicklist_deinit();
        chat_commands_deinit();
 
        nicklist_deinit();
@@ -87,6 +259,7 @@ void core_deinit(void)
        channels_deinit();
 
        rawlog_deinit();
        channels_deinit();
 
        rawlog_deinit();
+       log_away_deinit();
        log_deinit();
         write_buffer_deinit();
        servers_deinit();
        log_deinit();
         write_buffer_deinit();
        servers_deinit();
@@ -95,6 +268,7 @@ void core_deinit(void)
        chatnets_deinit();
        chat_protocols_deinit();
 
        chatnets_deinit();
        chat_protocols_deinit();
 
+        session_deinit();
         nickmatch_cache_deinit();
        commands_deinit();
        settings_deinit();
         nickmatch_cache_deinit();
        commands_deinit();
        settings_deinit();
@@ -106,4 +280,7 @@ void core_deinit(void)
        pidwait_deinit();
 #endif
        modules_deinit();
        pidwait_deinit();
 #endif
        modules_deinit();
+
+       g_free(irssi_dir);
+        g_free(irssi_config_file);
 }
 }
index 61d6ef7031b17b50c0d1ac27e5439929655229d3..6b7ec6218f25616d118377e8d5e567b04094b32e 100644 (file)
@@ -10,6 +10,9 @@
 #define IRSSI_GUI_KDE          5
 
 extern int irssi_gui;
 #define IRSSI_GUI_KDE          5
 
 extern int irssi_gui;
+extern int irssi_init_finished; /* TRUE after "irssi init finished" signal is sent */
+
+void core_init_paths(int argc, char *argv[]);
 
 void core_init(void);
 void core_deinit(void);
 
 void core_init(void);
 void core_deinit(void);
index 21ebae6b8b8ab020ebcf798cfd9c7d0a16bf8008..777d50e1e38d61d565c3e0633cdd69f86a3cd341 100644 (file)
@@ -48,16 +48,19 @@ typedef struct {
 
 static int timer_tag;
 
 
 static int timer_tag;
 
-static EXPANDO_REC *char_expandos[127];
+static EXPANDO_REC *char_expandos[255];
 static GHashTable *expandos;
 static time_t client_start_time;
 static char *last_sent_msg, *last_sent_msg_body;
 static char *last_privmsg_from, *last_public_from;
 static char *sysname, *sysrelease, *sysarch;
 static GHashTable *expandos;
 static time_t client_start_time;
 static char *last_sent_msg, *last_sent_msg_body;
 static char *last_privmsg_from, *last_public_from;
 static char *sysname, *sysrelease, *sysarch;
+
 static const char *timestamp_format;
 static const char *timestamp_format;
+static int timestamp_seconds;
+static time_t last_timestamp;
 
 
-#define CHAR_EXPANDOS_COUNT \
-       ((int) (sizeof(char_expandos) / sizeof(char_expandos[0])))
+#define CHAR_EXPANDO(chr) \
+       (char_expandos[(int) (unsigned char) chr])
 
 /* Create expando - overrides any existing ones. */
 void expando_create(const char *key, EXPANDO_FUNC func, ...)
 
 /* Create expando - overrides any existing ones. */
 void expando_create(const char *key, EXPANDO_FUNC func, ...)
@@ -73,7 +76,7 @@ void expando_create(const char *key, EXPANDO_FUNC func, ...)
                rec = g_hash_table_lookup(expandos, key);
        else {
                /* single character expando */
                rec = g_hash_table_lookup(expandos, key);
        else {
                /* single character expando */
-               rec = char_expandos[(int) *key];
+               rec = CHAR_EXPANDO(*key);
        }
 
        if (rec != NULL)
        }
 
        if (rec != NULL)
@@ -83,7 +86,7 @@ void expando_create(const char *key, EXPANDO_FUNC func, ...)
                 if (key[1] != '\0')
                        g_hash_table_insert(expandos, g_strdup(key), rec);
                else
                 if (key[1] != '\0')
                        g_hash_table_insert(expandos, g_strdup(key), rec);
                else
-                       char_expandos[(int) *key] = rec;
+                       char_expandos[(int) (unsigned char) *key] = rec;
        }
 
        rec->func = func;
        }
 
        rec->func = func;
@@ -99,7 +102,7 @@ static EXPANDO_REC *expando_find(const char *key)
        if (key[1] != '\0')
                return g_hash_table_lookup(expandos, key);
         else
        if (key[1] != '\0')
                return g_hash_table_lookup(expandos, key);
         else
-               return char_expandos[(int) *key];
+               return CHAR_EXPANDO(*key);
 }
 
 /* Add new signal to expando */
 }
 
 /* Add new signal to expando */
@@ -136,9 +139,9 @@ void expando_destroy(const char *key, EXPANDO_FUNC func)
 
        if (key[1] == '\0') {
                /* single character expando */
 
        if (key[1] == '\0') {
                /* single character expando */
-               rec = char_expandos[(int) *key];
+               rec = CHAR_EXPANDO(*key);
                if (rec != NULL && rec->func == func) {
                if (rec != NULL && rec->func == func) {
-                       char_expandos[(int) *key] = NULL;
+                       char_expandos[(int) (unsigned char) *key] = NULL;
                        g_free(rec);
                }
        } else if (g_hash_table_lookup_extended(expandos, key, &origkey,
                        g_free(rec);
                }
        } else if (g_hash_table_lookup_extended(expandos, key, &origkey,
@@ -209,12 +212,42 @@ void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs)
        }
 }
 
        }
 }
 
-EXPANDO_FUNC expando_find_char(char chr)
+/* Returns [<signal id>, EXPANDO_ARG_xxx, <signal id>, ..., -1] */
+int *expando_get_signals(const char *key)
 {
 {
-       g_return_val_if_fail(chr < CHAR_EXPANDOS_COUNT, NULL);
+       EXPANDO_REC *rec;
+       int *signals;
+        int n;
+
+       g_return_val_if_fail(key != NULL, NULL);
+
+       rec = expando_find(key);
+       if (rec == NULL || rec->signals < 0)
+                return NULL;
+
+       if (rec->signals == 0) {
+               /* it's unknown when this expando changes..
+                  check it once in a second */
+               signals = g_new(int, 3);
+               signals[0] = signal_get_uniq_id("expando timer");
+               signals[1] = EXPANDO_ARG_NONE;
+               signals[2] = -1;
+                return signals;
+       }
+
+        signals = g_new(int, rec->signals*2+1);
+       for (n = 0; n < rec->signals; n++) {
+                signals[n*2] = rec->signal_ids[n];
+                signals[n*2+1] = rec->signal_args[n];
+       }
+       signals[rec->signals*2] = -1;
+        return signals;
+}
 
 
-       return char_expandos[(int) chr] == NULL ? NULL :
-               char_expandos[(int) chr]->func;
+EXPANDO_FUNC expando_find_char(char chr)
+{
+       return CHAR_EXPANDO(chr) == NULL ? NULL :
+               CHAR_EXPANDO(chr)->func;
 }
 
 EXPANDO_FUNC expando_find_long(const char *key)
 }
 
 EXPANDO_FUNC expando_find_long(const char *key)
@@ -327,10 +360,18 @@ static char *expando_target(SERVER_REC *server, void *item, int *free_ret)
        return item == NULL ? "" : ((WI_ITEM_REC *) item)->name;
 }
 
        return item == NULL ? "" : ((WI_ITEM_REC *) item)->name;
 }
 
-/* client release date (numeric version string) */
+/* client release date (in YYYYMMDD format) */
 static char *expando_releasedate(SERVER_REC *server, void *item, int *free_ret)
 {
 static char *expando_releasedate(SERVER_REC *server, void *item, int *free_ret)
 {
-       return IRSSI_VERSION_DATE;
+        *free_ret = TRUE;
+       return g_strdup_printf("%d", IRSSI_VERSION_DATE);
+}
+
+/* client release time (in HHMM format) */
+static char *expando_releasetime(SERVER_REC *server, void *item, int *free_ret)
+{
+        *free_ret = TRUE;
+       return g_strdup_printf("%04d", IRSSI_VERSION_TIME);
 }
 
 /* current working directory */
 }
 
 /* current working directory */
@@ -439,13 +480,42 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg,
 
 static int sig_timer(void)
 {
 
 static int sig_timer(void)
 {
+       time_t now;
+       struct tm *tm;
+        int last_min;
+
         signal_emit("expando timer", 0);
         signal_emit("expando timer", 0);
+
+        /* check if $Z has changed */
+       now = time(NULL);
+       if (last_timestamp != now) {
+               if (!timestamp_seconds && last_timestamp != 0) {
+                        /* assume it changes every minute */
+                       tm = localtime(&last_timestamp);
+                       last_min = tm->tm_min;
+
+                       tm = localtime(&now);
+                       if (tm->tm_min == last_min)
+                                return 1;
+               }
+
+                signal_emit("time changed", 0);
+               last_timestamp = now;
+       }
+
         return 1;
 }
 
 static void read_settings(void)
 {
         return 1;
 }
 
 static void read_settings(void)
 {
-        timestamp_format = settings_get_str("timestamp_format");
+       timestamp_format = settings_get_str("timestamp_format");
+       timestamp_seconds =
+               strstr(timestamp_format, "%r") != NULL ||
+               strstr(timestamp_format, "%s") != NULL ||
+               strstr(timestamp_format, "%S") != NULL ||
+               strstr(timestamp_format, "%X") != NULL ||
+               strstr(timestamp_format, "%T") != NULL;
+
 }
 
 void expandos_init(void)
 }
 
 void expandos_init(void)
@@ -459,10 +529,11 @@ void expandos_init(void)
        client_start_time = time(NULL);
        last_sent_msg = NULL; last_sent_msg_body = NULL;
        last_privmsg_from = NULL; last_public_from = NULL;
        client_start_time = time(NULL);
        last_sent_msg = NULL; last_sent_msg_body = NULL;
        last_privmsg_from = NULL; last_public_from = NULL;
+        last_timestamp = 0;
 
         sysname = sysrelease = sysarch = NULL;
 #ifdef HAVE_SYS_UTSNAME_H
 
         sysname = sysrelease = sysarch = NULL;
 #ifdef HAVE_SYS_UTSNAME_H
-       if (uname(&un) == 0) {
+       if (uname(&un) >= 0) {
                sysname = g_strdup(un.sysname);
                sysrelease = g_strdup(un.release);
                sysarch = g_strdup(un.machine);
                sysname = g_strdup(un.sysname);
                sysrelease = g_strdup(un.release);
                sysarch = g_strdup(un.machine);
@@ -521,11 +592,14 @@ void expandos_init(void)
                       "window item changed", EXPANDO_ARG_WINDOW, NULL);
        expando_create("V", expando_releasedate,
                       "", EXPANDO_NEVER, NULL);
                       "window item changed", EXPANDO_ARG_WINDOW, NULL);
        expando_create("V", expando_releasedate,
                       "", EXPANDO_NEVER, NULL);
+       expando_create("versiontime", expando_releasetime,
+                      "", EXPANDO_NEVER, NULL);
        expando_create("W", expando_workdir, NULL);
        expando_create("Y", expando_realname,
                       "window changed", EXPANDO_ARG_NONE,
                       "window server changed", EXPANDO_ARG_WINDOW, NULL);
        expando_create("W", expando_workdir, NULL);
        expando_create("Y", expando_realname,
                       "window changed", EXPANDO_ARG_NONE,
                       "window server changed", EXPANDO_ARG_WINDOW, NULL);
-       expando_create("Z", expando_time, NULL);
+       expando_create("Z", expando_time,
+                      "time changed", EXPANDO_ARG_NONE, NULL);
        expando_create("$", expando_dollar,
                       "", EXPANDO_NEVER, NULL);
 
        expando_create("$", expando_dollar,
                       "", EXPANDO_NEVER, NULL);
 
@@ -549,7 +623,7 @@ void expandos_init(void)
 
        read_settings();
 
 
        read_settings();
 
-        timer_tag = g_timeout_add(1000, (GSourceFunc) sig_timer, NULL);
+        timer_tag = g_timeout_add(500, (GSourceFunc) sig_timer, NULL);
        signal_add("message public", (SIGNAL_FUNC) sig_message_public);
        signal_add("message private", (SIGNAL_FUNC) sig_message_private);
        signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
        signal_add("message public", (SIGNAL_FUNC) sig_message_public);
        signal_add("message private", (SIGNAL_FUNC) sig_message_private);
        signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
@@ -560,7 +634,7 @@ void expandos_deinit(void)
 {
        int n;
 
 {
        int n;
 
-       for (n = 0; n < CHAR_EXPANDOS_COUNT; n++)
+       for (n = 0; n < sizeof(char_expandos)/sizeof(char_expandos[0]); n++)
                g_free_not_null(char_expandos[n]);
 
        expando_destroy("sysname", expando_sysname);
                g_free_not_null(char_expandos[n]);
 
        expando_destroy("sysname", expando_sysname);
index 3dcb527e7480528afdf0f4307dca773039e17233..cf057994e7c814f2c7c200a7b8613713fb82361b 100644 (file)
@@ -5,7 +5,7 @@
 
 /* first argument of signal must match to active .. */
 typedef enum {
 
 /* first argument of signal must match to active .. */
 typedef enum {
-        EXPANDO_ARG_NONE,
+        EXPANDO_ARG_NONE = 1,
         EXPANDO_ARG_SERVER,
         EXPANDO_ARG_WINDOW,
        EXPANDO_ARG_WINDOW_ITEM,
         EXPANDO_ARG_SERVER,
         EXPANDO_ARG_WINDOW,
        EXPANDO_ARG_WINDOW_ITEM,
@@ -28,6 +28,9 @@ void expando_destroy(const char *key, EXPANDO_FUNC func);
 void expando_bind(const char *key, int funccount, SIGNAL_FUNC *funcs);
 void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs);
 
 void expando_bind(const char *key, int funccount, SIGNAL_FUNC *funcs);
 void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs);
 
+/* Returns [<signal id>, EXPANDO_ARG_xxx, <signal id>, ..., -1] */
+int *expando_get_signals(const char *key);
+
 /* internal: */
 EXPANDO_FUNC expando_find_char(char chr);
 EXPANDO_FUNC expando_find_long(const char *key);
 /* internal: */
 EXPANDO_FUNC expando_find_char(char chr);
 EXPANDO_FUNC expando_find_long(const char *key);
index 69460c5b359694fcf9823ccf4f27ee3069bc666d..cebbc3b060926ef800d527fb8aa71b02d566206b 100644 (file)
@@ -161,9 +161,8 @@ int ignore_check(SERVER_REC *server, const char *nick, const char *host,
        g_return_val_if_fail(server != NULL, 0);
         if (nick == NULL) nick = "";
 
        g_return_val_if_fail(server != NULL, 0);
         if (nick == NULL) nick = "";
 
-       chanrec = (channel != NULL && server != NULL &&
-                  server->ischannel(channel)) ?
-               channel_find(server, channel) : NULL;
+       chanrec = server == NULL || channel == NULL ? NULL :
+               channel_find(server, channel);
        if (chanrec != NULL && nick != NULL &&
            (nickrec = nicklist_find(chanrec, nick)) != NULL) {
                 /* nick found - check only ignores in nickmatch cache */
        if (chanrec != NULL && nick != NULL &&
            (nickrec = nicklist_find(chanrec, nick)) != NULL) {
                 /* nick found - check only ignores in nickmatch cache */
@@ -317,18 +316,24 @@ static void ignore_remove_config(IGNORE_REC *rec)
        if (node != NULL) iconfig_node_list_remove(node, ignore_index(rec));
 }
 
        if (node != NULL) iconfig_node_list_remove(node, ignore_index(rec));
 }
 
-void ignore_add_rec(IGNORE_REC *rec)
+static void ignore_init_rec(IGNORE_REC *rec)
 {
 #ifdef HAVE_REGEX_H
        rec->regexp_compiled = !rec->regexp || rec->pattern == NULL ? FALSE :
                regcomp(&rec->preg, rec->pattern,
                        REG_EXTENDED|REG_ICASE|REG_NOSUB) == 0;
 #endif
 {
 #ifdef HAVE_REGEX_H
        rec->regexp_compiled = !rec->regexp || rec->pattern == NULL ? FALSE :
                regcomp(&rec->preg, rec->pattern,
                        REG_EXTENDED|REG_ICASE|REG_NOSUB) == 0;
 #endif
+}
+
+void ignore_add_rec(IGNORE_REC *rec)
+{
+       ignore_init_rec(rec);
 
        ignores = g_slist_append(ignores, rec);
        ignore_set_config(rec);
 
        signal_emit("ignore created", 1, rec);
 
        ignores = g_slist_append(ignores, rec);
        ignore_set_config(rec);
 
        signal_emit("ignore created", 1, rec);
+       nickmatch_rebuild(nickmatch);
 }
 
 static void ignore_destroy(IGNORE_REC *rec, int send_signal)
 }
 
 static void ignore_destroy(IGNORE_REC *rec, int send_signal)
@@ -402,7 +407,8 @@ static void read_ignores(void)
                return;
        }
 
                return;
        }
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
@@ -412,25 +418,17 @@ static void read_ignores(void)
                ignores = g_slist_append(ignores, rec);
 
                rec->mask = g_strdup(config_node_get_str(node, "mask", NULL));
                ignores = g_slist_append(ignores, rec);
 
                rec->mask = g_strdup(config_node_get_str(node, "mask", NULL));
-               if (rec->mask != NULL && strcmp(rec->mask, "*") == 0) {
-                       /* FIXME: remove after .98 */
-                        g_free(rec->mask);
-                       rec->mask = NULL;
-               }
                rec->pattern = g_strdup(config_node_get_str(node, "pattern", NULL));
                rec->level = level2bits(config_node_get_str(node, "level", ""));
                 rec->exception = config_node_get_bool(node, "exception", FALSE);
                rec->pattern = g_strdup(config_node_get_str(node, "pattern", NULL));
                rec->level = level2bits(config_node_get_str(node, "level", ""));
                 rec->exception = config_node_get_bool(node, "exception", FALSE);
-               if (*config_node_get_str(node, "except_level", "") != '\0') {
-                       /* FIXME: remove after .98 */
-                       rec->level = level2bits(config_node_get_str(node, "except_level", ""));
-                        rec->exception = TRUE;
-               }
                rec->regexp = config_node_get_bool(node, "regexp", FALSE);
                rec->fullword = config_node_get_bool(node, "fullword", FALSE);
                rec->replies = config_node_get_bool(node, "replies", FALSE);
 
                node = config_node_section(node, "channels", -1);
                if (node != NULL) rec->channels = config_node_get_list(node);
                rec->regexp = config_node_get_bool(node, "regexp", FALSE);
                rec->fullword = config_node_get_bool(node, "fullword", FALSE);
                rec->replies = config_node_get_bool(node, "replies", FALSE);
 
                node = config_node_section(node, "channels", -1);
                if (node != NULL) rec->channels = config_node_get_list(node);
+
+               ignore_init_rec(rec);
        }
 
        nickmatch_rebuild(nickmatch);
        }
 
        nickmatch_rebuild(nickmatch);
index aae506a8212c23b3c8baf67a51d7526813a8ff27..0caf58b650438ea1c0ce66c9e67a851562f521cd 100644 (file)
@@ -43,7 +43,7 @@ static const char *levels[] = {
        "CLIENTNOTICES",
        "CLIENTCRAP",
        "CLIENTERRORS",
        "CLIENTNOTICES",
        "CLIENTCRAP",
        "CLIENTERRORS",
-       "HILIGHT",
+       "HILIGHTS",
 
        "NOHILIGHT",
        NULL
 
        "NOHILIGHT",
        NULL
diff --git a/apps/irssi/src/core/log-away.c b/apps/irssi/src/core/log-away.c
new file mode 100644 (file)
index 0000000..724e4b4
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ log-away.c : Awaylog handling
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "levels.h"
+#include "log.h"
+#include "servers.h"
+#include "settings.h"
+
+static LOG_REC *awaylog;
+static int away_filepos;
+static int away_msgs;
+
+static void sig_log_written(LOG_REC *log)
+{
+       if (log != awaylog) return;
+
+        away_msgs++;
+}
+
+static void awaylog_open(void)
+{
+       const char *fname, *levelstr;
+       LOG_REC *log;
+       int level;
+
+       fname = settings_get_str("awaylog_file");
+       levelstr = settings_get_str("awaylog_level");
+       if (*fname == '\0' || *levelstr == '\0') return;
+
+       level = level2bits(levelstr);
+       if (level == 0) return;
+
+       log = log_find(fname);
+       if (log != NULL && log->handle != -1)
+               return; /* already open */
+
+       if (log == NULL) {
+               log = log_create_rec(fname, level);
+               log->temp = TRUE;
+               log_update(log);
+       }
+
+       if (!log_start_logging(log)) {
+               /* creating log file failed? close it. */
+               log_close(log);
+               return;
+       }
+
+       awaylog = log;
+       away_filepos = lseek(log->handle, 0, SEEK_CUR);
+       away_msgs = 0;
+}
+
+static void awaylog_close(void)
+{
+       const char *fname;
+       LOG_REC *log;
+
+       fname = settings_get_str("awaylog_file");
+       if (*fname == '\0') return;
+
+       log = log_find(fname);
+       if (log == NULL || log->handle == -1) {
+               /* awaylog not open */
+               return;
+       }
+
+       if (awaylog == log) awaylog = NULL;
+
+       signal_emit("awaylog show", 3, log, GINT_TO_POINTER(away_msgs),
+                   GINT_TO_POINTER(away_filepos));
+       log_close(log);
+}
+
+static void sig_away_changed(SERVER_REC *server)
+{
+       if (server->usermode_away)
+               awaylog_open();
+       else
+                awaylog_close();
+}
+
+void log_away_init(void)
+{
+       awaylog = NULL;
+       away_filepos = 0;
+       away_msgs = 0;
+
+       settings_add_str("log", "awaylog_file", IRSSI_DIR_SHORT"/away.log");
+       settings_add_str("log", "awaylog_level", "msgs hilight");
+
+       signal_add("log written", (SIGNAL_FUNC) sig_log_written);
+       signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
+
+void log_away_deinit(void)
+{
+       signal_remove("log written", (SIGNAL_FUNC) sig_log_written);
+       signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
index 368de25fa51f361c86b7a62cc87ba5e9a3e9c0c2..e843ffe732ed58f40eed8a64116f7c296e8c62cb 100644 (file)
@@ -30,7 +30,7 @@
 #include "lib-config/iconfig.h"
 #include "settings.h"
 
 #include "lib-config/iconfig.h"
 #include "settings.h"
 
-#define DEFAULT_LOG_FILE_CREATE_MODE 644
+#define DEFAULT_LOG_FILE_CREATE_MODE 600
 
 #ifdef HAVE_FCNTL
 static struct flock lock;
 
 #ifdef HAVE_FCNTL
 static struct flock lock;
@@ -165,7 +165,7 @@ void log_stop_logging(LOG_REC *log)
 
 static void log_rotate_check(LOG_REC *log)
 {
 
 static void log_rotate_check(LOG_REC *log)
 {
-       char *new_fname;
+       char *new_fname, *dir;
 
        g_return_if_fail(log != NULL);
 
 
        g_return_if_fail(log != NULL);
 
@@ -176,6 +176,12 @@ static void log_rotate_check(LOG_REC *log)
        if (strcmp(new_fname, log->real_fname) != 0) {
                /* rotate log */
                log_stop_logging(log);
        if (strcmp(new_fname, log->real_fname) != 0) {
                /* rotate log */
                log_stop_logging(log);
+               signal_emit("log rotated", 1, log);
+
+               dir = g_dirname(new_fname);
+               mkpath(dir, LOG_DIR_CREATE_MODE);
+               g_free(dir);
+
                log_start_logging(log);
        }
        g_free(new_fname);
                log_start_logging(log);
        }
        g_free(new_fname);
@@ -183,6 +189,7 @@ static void log_rotate_check(LOG_REC *log)
 
 void log_write_rec(LOG_REC *log, const char *str, int level)
 {
 
 void log_write_rec(LOG_REC *log, const char *str, int level)
 {
+        char *colorstr;
        struct tm *tm;
        time_t now;
        int hour, day;
        struct tm *tm;
        time_t now;
        int hour, day;
@@ -214,6 +221,11 @@ void log_write_rec(LOG_REC *log, const char *str, int level)
 
        log->last = now;
 
 
        log->last = now;
 
+       if (log->colorizer == NULL)
+               colorstr = NULL;
+        else
+                str = colorstr = log->colorizer(str);
+
         if ((level & MSGLEVEL_LASTLOG) == 0)
                log_write_timestamp(log->handle, log_timestamp, str, now);
        else
         if ((level & MSGLEVEL_LASTLOG) == 0)
                log_write_timestamp(log->handle, log_timestamp, str, now);
        else
@@ -221,6 +233,8 @@ void log_write_rec(LOG_REC *log, const char *str, int level)
        write_buffer(log->handle, "\n", 1);
 
        signal_emit("log written", 2, log, str);
        write_buffer(log->handle, "\n", 1);
 
        signal_emit("log written", 2, log, str);
+
+        g_free_not_null(colorstr);
 }
 
 LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
 }
 
 LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
@@ -243,11 +257,11 @@ LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
        return NULL;
 }
 
        return NULL;
 }
 
-void log_file_write(SERVER_REC *server, const char *item, int level,
+void log_file_write(const char *server_tag, const char *item, int level,
                    const char *str, int no_fallbacks)
 {
        GSList *tmp, *fallbacks;
                    const char *str, int no_fallbacks)
 {
        GSList *tmp, *fallbacks;
-       char *tmpstr, *servertag;
+       char *tmpstr;
        int found;
 
        g_return_if_fail(str != NULL);
        int found;
 
        g_return_if_fail(str != NULL);
@@ -255,7 +269,6 @@ void log_file_write(SERVER_REC *server, const char *item, int level,
        if (logs == NULL)
                return;
 
        if (logs == NULL)
                return;
 
-       servertag = server == NULL ? NULL : server->tag;
        fallbacks = NULL; found = FALSE;
 
        for (tmp = logs; tmp != NULL; tmp = tmp->next) {
        fallbacks = NULL; found = FALSE;
 
        for (tmp = logs; tmp != NULL; tmp = tmp->next) {
@@ -271,7 +284,7 @@ void log_file_write(SERVER_REC *server, const char *item, int level,
                        fallbacks = g_slist_append(fallbacks, rec);
                else if (item != NULL &&
                         log_item_find(rec, LOG_ITEM_TARGET, item,
                        fallbacks = g_slist_append(fallbacks, rec);
                else if (item != NULL &&
                         log_item_find(rec, LOG_ITEM_TARGET, item,
-                                      servertag) != NULL)
+                                      server_tag) != NULL)
                        log_write_rec(rec, str, level);
        }
 
                        log_write_rec(rec, str, level);
        }
 
@@ -343,6 +356,8 @@ static void log_update_config(LOG_REC *log)
 
        if (log->items != NULL)
                log_items_update_config(log, node);
 
        if (log->items != NULL)
                log_items_update_config(log, node);
+
+       signal_emit("log config save", 2, log, node);
 }
 
 static void log_remove_config(LOG_REC *log)
 }
 
 static void log_remove_config(LOG_REC *log)
@@ -457,7 +472,8 @@ static void log_items_read_config(CONFIG_NODE *node, LOG_REC *log)
        char *item;
        int type;
 
        char *item;
        int type;
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
@@ -500,7 +516,8 @@ static void log_read_config(void)
        node = iconfig_node_traverse("logs", FALSE);
        if (node == NULL) return;
 
        node = iconfig_node_traverse("logs", FALSE);
        if (node == NULL) return;
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
@@ -514,6 +531,8 @@ static void log_read_config(void)
                log->autoopen = config_node_get_bool(node, "auto_open", FALSE);
                log->level = level2bits(config_node_get_str(node, "level", 0));
 
                log->autoopen = config_node_get_bool(node, "auto_open", FALSE);
                log->level = level2bits(config_node_get_str(node, "level", 0));
 
+               signal_emit("log config read", 2, log, node);
+
                node = config_node_section(node, "items", -1);
                if (node != NULL)
                        log_items_read_config(node, log);
                node = config_node_section(node, "items", -1);
                if (node != NULL)
                        log_items_read_config(node, log);
@@ -548,9 +567,9 @@ void log_init(void)
                         "--- Day changed %a %b %d %Y");
 
        read_settings();
                         "--- Day changed %a %b %d %Y");
 
        read_settings();
-       log_read_config();
         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
         signal_add("setup reread", (SIGNAL_FUNC) log_read_config);
         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
         signal_add("setup reread", (SIGNAL_FUNC) log_read_config);
+        signal_add("irssi init finished", (SIGNAL_FUNC) log_read_config);
 }
 
 void log_deinit(void)
 }
 
 void log_deinit(void)
@@ -562,4 +581,5 @@ void log_deinit(void)
 
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
         signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
 
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
         signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
+        signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);
 }
 }
index 7361b6a09abcb71f98e89aa2f09a4ac5fbd10ff8..6ada7c65b0de325bc2a9f7f0efb5874fb093650b 100644 (file)
@@ -1,11 +1,15 @@
 #ifndef __LOG_H
 #define __LOG_H
 
 #ifndef __LOG_H
 #define __LOG_H
 
+#define LOG_DIR_CREATE_MODE 0700
+
 enum {
        LOG_ITEM_TARGET, /* channel, query, .. */
        LOG_ITEM_WINDOW_REFNUM
 };
 
 enum {
        LOG_ITEM_TARGET, /* channel, query, .. */
        LOG_ITEM_WINDOW_REFNUM
 };
 
+typedef char *(*COLORIZE_FUNC)(const char *str);
+
 typedef struct {
        int type;
         char *name;
 typedef struct {
        int type;
         char *name;
@@ -22,6 +26,7 @@ typedef struct {
        GSList *items; /* log only on these items */
 
        time_t last; /* when last message was written */
        GSList *items; /* log only on these items */
 
        time_t last; /* when last message was written */
+        COLORIZE_FUNC colorizer;
 
        unsigned int autoopen:1; /* automatically start logging at startup */
        unsigned int failed:1; /* opening log failed last time */
 
        unsigned int autoopen:1; /* automatically start logging at startup */
        unsigned int failed:1; /* opening log failed last time */
@@ -43,7 +48,7 @@ void log_item_destroy(LOG_REC *log, LOG_ITEM_REC *item);
 LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
                            const char *servertag);
 
 LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
                            const char *servertag);
 
-void log_file_write(SERVER_REC *server, const char *item, int level,
+void log_file_write(const char *server_tag, const char *item, int level,
                    const char *str, int no_fallbacks);
 void log_write_rec(LOG_REC *log, const char *str, int level);
 
                    const char *str, int no_fallbacks);
 void log_write_rec(LOG_REC *log, const char *str, int level);
 
diff --git a/apps/irssi/src/core/memdebug.c b/apps/irssi/src/core/memdebug.c
deleted file mode 100644 (file)
index 213d454..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- memdebug.c : irssi
-
-    Copyright (C) 1999-2000 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <glib.h>
-#include <gmodule.h>
-
-/*#define ENABLE_BUFFER_CHECKS*/
-#define BUFFER_CHECK_SIZE 5
-#define MIN_BUFFER_CHECK_SIZE 2
-
-typedef struct {
-       void *p;
-       int size;
-       char *file;
-       int line;
-       char *comment;
-} MEM_REC;
-
-static GHashTable *data = NULL, *preallocs = NULL;
-static const char *comment = "";
-
-static void add_flow_checks(char *p, unsigned long size)
-{
-#ifdef ENABLE_BUFFER_CHECKS
-       int n;
-
-       for (n = 0; n < BUFFER_CHECK_SIZE; n++)
-               p[n] = n ^ 0x7f;
-       for (n = 0; n < BUFFER_CHECK_SIZE; n++)
-               p[size-BUFFER_CHECK_SIZE+n] = n ^ 0x7f;
-#endif
-}
-
-void ig_memcheck_rec(void *key, MEM_REC *rec)
-{
-       guchar *p;
-       int n;
-
-       if (rec->size != INT_MIN){
-               p = rec->p;
-
-               for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
-                       if (p[n] != (n ^ 0x7f))
-                               g_error("buffer underflow, file %s line %d!\n", rec->file, rec->line);
-
-               for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
-                       if (p[rec->size-BUFFER_CHECK_SIZE+n] != (n ^ 0x7f))
-                               g_error("buffer overflow, file %s line %d!\n", rec->file, rec->line);
-       }
-}
-
-static void mem_check(void)
-{
-#ifdef ENABLE_BUFFER_CHECKS
-       g_hash_table_foreach(data, (GHFunc) ig_memcheck_rec, NULL);
-#endif
-}
-
-static void data_add(char *p, int size, const char *file, int line)
-{
-       MEM_REC *rec;
-
-       if (size <= 0 && size != INT_MIN)
-               g_error("size = %d, file %s line %d", size, file, line);
-
-       if (data == NULL) {
-               data = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
-               preallocs = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
-       }
-
-       if (g_hash_table_lookup(data, p) != NULL)
-               g_error("data_add() already malloc()'ed %p (in %s:%d)", p, file, line);
-
-       rec = g_new(MEM_REC, 1);
-       g_hash_table_insert(data, p, rec);
-
-       rec->p = p;
-       rec->size = size;
-       rec->file = g_strdup(file);
-       rec->line = line;
-       rec->comment = g_strdup(comment);
-
-       if (size == INT_MIN)
-               g_hash_table_insert(preallocs, p-BUFFER_CHECK_SIZE, p);
-       else
-               add_flow_checks(p, size);
-       mem_check();
-}
-
-static void data_clear(char *p)
-{
-       MEM_REC *rec;
-
-       if (g_hash_table_lookup(preallocs, p) != NULL)
-               p += BUFFER_CHECK_SIZE;
-
-       rec = g_hash_table_lookup(data, p);
-       if (rec != NULL && rec->size > 0)
-               memset(p, 'F', rec->size);
-}
-
-static void *data_remove(char *p, const char *file, int line)
-{
-       MEM_REC *rec;
-
-       mem_check();
-
-       if (g_hash_table_lookup(preallocs, p) != NULL) {
-               g_hash_table_remove(preallocs, p);
-               p += BUFFER_CHECK_SIZE;
-       }
-
-       rec = g_hash_table_lookup(data, p);
-       if (rec == NULL) {
-               g_warning("data_remove() data %p not found (in %s:%d)", p, file, line);
-               return p+BUFFER_CHECK_SIZE;
-       }
-
-       g_hash_table_remove(data, p);
-       g_free(rec->file);
-       g_free(rec->comment);
-       g_free(rec);
-
-       return p;
-}
-
-void *ig_malloc(int size, const char *file, int line)
-{
-       char *p;
-
-       size += BUFFER_CHECK_SIZE*2;
-       p = g_malloc(size);
-       data_add(p, size, file, line);
-       return (void *) (p+BUFFER_CHECK_SIZE);
-}
-
-void *ig_malloc0(int size, const char *file, int line)
-{
-       char *p;
-
-       size += BUFFER_CHECK_SIZE*2;
-       p = g_malloc0(size);
-       data_add(p, size, file, line);
-       return (void *) (p+BUFFER_CHECK_SIZE);
-}
-
-void *ig_realloc(void *mem, unsigned long size, const char *file, int line)
-{
-       char *p, *oldmem = mem;
-
-       size += BUFFER_CHECK_SIZE*2;
-       oldmem -= BUFFER_CHECK_SIZE;
-       data_remove(oldmem, file, line);
-       p = g_realloc(oldmem, size);
-       data_add(p, size, file, line);
-       return (void *) (p+BUFFER_CHECK_SIZE);
-}
-
-char *ig_strdup(const char *str, const char *file, int line)
-{
-       void *p;
-
-       if (str == NULL) return NULL;
-
-       p = ig_malloc(strlen(str)+1, file, line);
-       strcpy(p, str);
-
-       return p;
-}
-
-char *ig_strndup(const char *str, int count, const char *file, int line)
-{
-       char *p;
-
-       if (str == NULL) return NULL;
-
-       p = ig_malloc(count+1, file, line);
-       strncpy(p, str, count); p[count] = '\0';
-
-       return p;
-}
-
-char *ig_strconcat(const char *file, int line, const char *str, ...)
-{
-  guint          l;
-  va_list args;
-  char   *s;
-  char   *concat;
-
-  g_return_val_if_fail (str != NULL, NULL);
-
-  l = 1 + strlen (str);
-  va_start (args, str);
-  s = va_arg (args, char*);
-  while (s)
-    {
-      l += strlen (s);
-      s = va_arg (args, char*);
-    }
-  va_end (args);
-
-  concat = ig_malloc(l, file, line);
-  concat[0] = 0;
-
-  strcat (concat, str);
-  va_start (args, str);
-  s = va_arg (args, char*);
-  while (s)
-    {
-      strcat (concat, s);
-      s = va_arg (args, char*);
-    }
-  va_end (args);
-
-  return concat;
-}
-
-char *ig_strdup_printf(const char *file, int line, const char *format, ...)
-{
-       char *buffer, *p;
-       va_list args;
-
-       va_start (args, format);
-       buffer = g_strdup_vprintf (format, args);
-       va_end (args);
-
-       p = ig_malloc(strlen(buffer)+1, file, line);
-       strcpy(p, buffer);
-       g_free(buffer);
-
-       return p;
-}
-
-char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args)
-{
-       char *buffer, *p;
-
-       buffer = g_strdup_vprintf (format, args);
-
-       p = ig_malloc(strlen(buffer)+1, file, line);
-       strcpy(p, buffer);
-       g_free(buffer);
-
-       return p;
-}
-
-void ig_free(void *p)
-{
-       char *cp = p;
-
-       if (cp == NULL) g_error("ig_free() : trying to free NULL");
-
-       cp -= BUFFER_CHECK_SIZE;
-       data_clear(cp);
-       cp = data_remove(cp, "??", 0);
-       if (cp != NULL) g_free(cp);
-}
-
-GString *ig_string_new(const char *file, int line, const char *str)
-{
-       GString *ret;
-
-       ret = g_string_new(str);
-       data_add((void *) ret, INT_MIN, file, line);
-       return ret;
-}
-
-void ig_string_free(const char *file, int line, GString *str, gboolean freeit)
-{
-       data_remove((void *) str, file, line);
-       if (!freeit)
-               data_add(str->str, INT_MIN, file, line);
-
-       g_string_free(str, freeit);
-}
-
-char *ig_strjoinv(const char *file, int line, const char *sepa, char **array)
-{
-       char *ret;
-
-       ret = g_strjoinv(sepa, array);
-       data_add(ret, INT_MIN, file, line);
-       return ret;
-}
-
-char *ig_dirname(const char *file, int line, const char *fname)
-{
-       char *ret;
-
-       ret = g_dirname(fname);
-       data_add(ret, INT_MIN, file, line);
-       return ret;
-}
-
-char *ig_module_build_path(const char *file, int line, const char *dir, const char *module)
-{
-       char *ret;
-
-       ret = g_module_build_path(dir, module);
-       data_add(ret, INT_MIN, file, line);
-       return ret;
-}
-
-void ig_profile_line(void *key, MEM_REC *rec)
-{
-       char *data;
-
-       if (*rec->comment == '\0' &&
-           (strcmp(rec->file, "ig_strdup_printf") == 0 ||
-            strcmp(rec->file, "ig_strdup_vprintf") == 0 ||
-            strcmp(rec->file, "ig_strconcat") == 0 ||
-            strcmp(rec->file, "ig_string_free (free = FALSE)") == 0))
-               data = (char *) rec->p + BUFFER_CHECK_SIZE;
-       else
-               data = rec->comment;
-       fprintf(stderr, "%s:%d %d bytes (%s)\n", rec->file, rec->line, rec->size, data);
-}
-
-void ig_mem_profile(void)
-{
-    g_hash_table_foreach(data, (GHFunc) ig_profile_line, NULL);
-    g_hash_table_destroy(data);
-    g_hash_table_destroy(preallocs);
-}
-
-static MEM_REC *largest[10];
-
-void ig_profile_largest(void *key, MEM_REC *rec)
-{
-    int n;
-
-    for (n = 0; n < 10; n++)
-    {
-       if (largest[n] == NULL || rec->size > largest[n]->size)
-       {
-           g_memmove(largest+n+1, largest+n, sizeof(void *)*(9-n));
-           largest[n] = rec;
-       }
-    }
-}
-
-void ig_mem_profile_largest(void)
-{
-    /*int n;*/
-
-    memset(&largest, 0, sizeof(MEM_REC*)*10);
-    /*g_hash_table_foreach(data, (GHFunc) ig_profile_largest, NULL);
-
-    for (n = 0; n < 10 && largest[n] != NULL; n++)
-    {
-       ig_profile_line(NULL, largest[n]);
-    }*/
-}
-
-void ig_set_data(const char *data)
-{
-    comment = data;
-}
diff --git a/apps/irssi/src/core/memdebug.h b/apps/irssi/src/core/memdebug.h
deleted file mode 100644 (file)
index 3f32880..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifdef MEM_DEBUG
-void ig_mem_profile(void);
-
-void ig_set_data(const char *data);
-
-void *ig_malloc(int size, const char *file, int line);
-void *ig_malloc0(int size, const char *file, int line);
-void *ig_realloc(void *mem, unsigned long size, const char *file, int line);
-char *ig_strdup(const char *str, const char *file, int line);
-char *ig_strndup(const char *str, int count, const char *file, int line);
-char *ig_strconcat(const char *file, int line, const char *str, ...);
-char *ig_strdup_printf(const char *file, int line, const char *format, ...) G_GNUC_PRINTF (3, 4);
-char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args);
-void ig_free(void *p);
-GString *ig_string_new(const char *file, int line, const char *str);
-void ig_string_free(const char *file, int line, GString *str, int freeit);
-char *ig_strjoinv(const char *file, int line, const char *sepa, char **array);
-char *ig_dirname(const char *file, int line, const char *fname);
-char *ig_module_build_path(const char *file, int line, const char *dir, const char *module);
-
-#define g_malloc(a) ig_malloc(a, __FILE__, __LINE__)
-#define g_malloc0(a) ig_malloc0(a, __FILE__, __LINE__)
-#define g_free ig_free
-#define g_realloc(a,b) ig_realloc(a, b, __FILE__, __LINE__)
-#define g_strdup(a) ig_strdup(a, __FILE__, __LINE__)
-#define g_strndup(a, b) ig_strndup(a, b, __FILE__, __LINE__)
-#define g_string_new(a) ig_string_new(__FILE__, __LINE__, a)
-#define g_string_free(a, b) ig_string_free(__FILE__, __LINE__, a, b)
-#define g_strjoinv(a,b) ig_strjoinv(__FILE__, __LINE__, a, b)
-#define g_dirname(a) ig_dirname(__FILE__, __LINE__, a)
-#define g_module_build_path(a, b) ig_module_build_path(__FILE__, __LINE__, a, b)
-
-#ifndef __STRICT_ANSI__
-#define g_strconcat(a...) ig_strconcat(__FILE__, __LINE__, ##a)
-#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b)
-#define g_strdup_vprintf(a, b...) ig_strdup_vprintf(__FILE__, __LINE__, a, ##b)
-#endif
-#endif
index 1af327afb8ff5152eac9ac3396b006481c97be37..d57c7f66977332676ba2e612cb55513d9ca9168f 100644 (file)
@@ -126,7 +126,7 @@ int find_substr(const char *list, const char *item)
                return FALSE;
 
        for (;;) {
                return FALSE;
 
        for (;;) {
-               while (isspace((gint) *list)) list++;
+               while (i_isspace(*list)) list++;
                if (*list == '\0') break;
 
                ptr = strchr(list, ' ');
                if (*list == '\0') break;
 
                ptr = strchr(list, ' ');
@@ -324,7 +324,7 @@ char *stristr(const char *data, const char *key)
                if (key[pos] == '\0')
                         return (char *) data;
 
                if (key[pos] == '\0')
                         return (char *) data;
 
-               if (toupper(data[pos]) == toupper(key[pos]))
+               if (i_toupper(data[pos]) == i_toupper(key[pos]))
                        pos++;
                else {
                        data++;
                        pos++;
                else {
                        data++;
@@ -337,7 +337,7 @@ char *stristr(const char *data, const char *key)
 
 #define isbound(c) \
        ((unsigned char) (c) < 128 && \
 
 #define isbound(c) \
        ((unsigned char) (c) < 128 && \
-       (isspace((int) (c)) || ispunct((int) (c))))
+       (i_isspace(c) || i_ispunct(c)))
 
 char *strstr_full_case(const char *data, const char *key, int icase)
 {
 
 char *strstr_full_case(const char *data, const char *key, int icase)
 {
@@ -364,7 +364,7 @@ char *strstr_full_case(const char *data, const char *key, int icase)
                        return (char *) data;
                }
 
                        return (char *) data;
                }
 
-               match = icase ? (toupper(data[pos]) == toupper(key[pos])) :
+               match = icase ? (i_toupper(data[pos]) == i_toupper(key[pos])) :
                                 data[pos] == key[pos];
 
                if (match && (pos != 0 || data == start || isbound(data[-1])))
                                 data[pos] == key[pos];
 
                if (match && (pos != 0 || data == start || isbound(data[-1])))
@@ -430,10 +430,11 @@ int mkpath(const char *path, int mode)
                dir = g_strndup(path, (int) (p-path));
                if (stat(dir, &statbuf) != 0) {
 #ifndef WIN32
                dir = g_strndup(path, (int) (p-path));
                if (stat(dir, &statbuf) != 0) {
 #ifndef WIN32
-                       if (mkdir(dir, mode) == -1) {
+                       if (mkdir(dir, mode) == -1)
 #else
 #else
-                       if (_mkdir(dir) == -1) {
+                       if (_mkdir(dir) == -1)
 #endif
 #endif
+                       {
                                g_free(dir);
                                return -1;
                        }
                                g_free(dir);
                                return -1;
                        }
@@ -472,7 +473,7 @@ unsigned int g_istr_hash(gconstpointer v)
        unsigned int h = 0, g;
 
        while (*s != '\0') {
        unsigned int h = 0, g;
 
        while (*s != '\0') {
-               h = (h << 4) + toupper(*s);
+               h = (h << 4) + i_toupper(*s);
                if ((g = h & 0xf0000000UL)) {
                        h = h ^ (g >> 24);
                        h = h ^ g;
                if ((g = h & 0xf0000000UL)) {
                        h = h ^ (g >> 24);
                        h = h ^ g;
@@ -492,7 +493,7 @@ int match_wildcards(const char *cmask, const char *data)
        newmask = mask = g_strdup(cmask);
        for (; *mask != '\0' && *data != '\0'; mask++) {
                if (*mask != '*') {
        newmask = mask = g_strdup(cmask);
        for (; *mask != '\0' && *data != '\0'; mask++) {
                if (*mask != '*') {
-                       if (*mask != '?' && toupper(*mask) != toupper(*data))
+                       if (*mask != '?' && i_toupper(*mask) != i_toupper(*data))
                                break;
 
                        data++;
                                break;
 
                        data++;
@@ -538,7 +539,7 @@ int is_numeric(const char *str, char end_char)
                return FALSE;
 
        while (*str != '\0' && *str != end_char) {
                return FALSE;
 
        while (*str != '\0' && *str != end_char) {
-               if (!isdigit(*str)) return FALSE;
+               if (!i_isdigit(*str)) return FALSE;
                str++;
        }
 
                str++;
        }
 
@@ -725,3 +726,47 @@ GSList *columns_sort_list(GSList *list, int rows)
                             g_slist_length(list), sorted);
         return sorted;
 }
                             g_slist_length(list), sorted);
         return sorted;
 }
+
+/* Expand escape string, the first character in data should be the
+   one after '\'. Returns the expanded character or -1 if error. */
+int expand_escape(const char **data)
+{
+        char digit[4];
+
+       switch (**data) {
+       case 't':
+               return '\t';
+       case 'r':
+               return '\r';
+       case 'n':
+               return '\n';
+       case 'e':
+               return 27; /* ESC */
+
+       case 'x':
+                /* hex digit */
+               if (!isxdigit((*data)[1]) || !isxdigit((*data)[2]))
+                       return -1;
+
+               digit[0] = (*data)[1];
+               digit[1] = (*data)[2];
+                digit[2] = '\0';
+               *data += 2;
+               return strtol(digit, NULL, 16);
+       case 'c':
+                /* control character (\cA = ^A) */
+                (*data)++;
+               return i_toupper(**data) - 64;
+       default:
+               if (!i_isdigit(**data))
+                       return -1;
+
+                /* octal */
+                digit[0] = (*data)[0];
+                digit[1] = (*data)[1];
+               digit[2] = (*data)[2];
+                digit[3] = '\0';
+               *data += 2;
+               return strtol(digit, NULL, 8);
+       }
+}
index 45c8ce118ef6ee98bf098bbc792c4b52685b8ab1..804cffc2a9c2a63dbb422cb6f978dac5cc697887 100644 (file)
@@ -101,4 +101,8 @@ int get_max_column_count(GSList *items, COLUMN_LEN_FUNC len_func,
 /* Return a column sorted copy of a list. */
 GSList *columns_sort_list(GSList *list, int rows);
 
 /* Return a column sorted copy of a list. */
 GSList *columns_sort_list(GSList *list, int rows);
 
+/* Expand escape string, the first character in data should be the
+   one after '\'. Returns the expanded character or -1 if error. */
+int expand_escape(const char **data);
+
 #endif
 #endif
diff --git a/apps/irssi/src/core/modules-load.c b/apps/irssi/src/core/modules-load.c
new file mode 100644 (file)
index 0000000..e7e7809
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ modules-load.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "modules-load.h"
+#include "signals.h"
+
+#include "settings.h"
+#include "commands.h"
+#include "misc.h"
+
+#ifdef HAVE_GMODULE
+
+/* Returns the module name without path, "lib" prefix or ".so" suffix */
+static char *module_get_name(const char *path, int *start, int *end)
+{
+       const char *name;
+       char *module_name, *ptr;
+
+        name = NULL;
+       if (*path == '~' || g_path_is_absolute(path)) {
+               name = strrchr(path, G_DIR_SEPARATOR);
+                if (name != NULL) name++;
+       }
+
+       if (name == NULL)
+               name = path;
+
+       if (strncmp(name, "lib", 3) == 0)
+               name += 3;
+
+       module_name = g_strdup(name);
+       ptr = strchr(module_name, '.');
+       if (ptr != NULL) *ptr = '\0';
+
+       *start = (int) (name-path);
+       *end = *start + (ptr == NULL ? strlen(name) :
+                        (int) (ptr-module_name));
+
+       return module_name;
+}
+
+/* Returns the root module name for given submodule (eg. perl_core -> perl) */
+static char *module_get_root(const char *name, char **prefixes)
+{
+       int len;
+
+       /* skip any of the prefixes.. */
+       while (*prefixes != NULL) {
+                len = strlen(*prefixes);
+               if (strncmp(name, *prefixes, len) == 0 && name[len] == '_') {
+                       name += len+1;
+                        break;
+               }
+               prefixes++;
+       }
+
+       /* skip the _core part */
+        len = strlen(name);
+       if (len > 5 && strcmp(name+len-5, "_core") == 0)
+               return g_strndup(name, len-5);
+
+        return g_strdup(name);
+}
+
+/* Returns the sub module name for given submodule (eg. perl_core -> core) */
+static char *module_get_sub(const char *name, const char *root)
+{
+       int rootlen, namelen;
+
+        namelen = strlen(name);
+       rootlen = strlen(root);
+        g_return_val_if_fail(namelen >= rootlen, g_strdup(name));
+
+       if (strncmp(name, root, rootlen) == 0 &&
+           strcmp(name+rootlen, "_core") == 0)
+                return g_strdup("core");
+
+       if (namelen+1 > rootlen && name[namelen-rootlen-1] == '_' &&
+           strcmp(name+namelen-rootlen, root) == 0)
+                return g_strndup(name, namelen-rootlen-1);
+
+        return g_strdup(name);
+}
+
+static GModule *module_open(const char *name, int *found)
+{
+       struct stat statbuf;
+       GModule *module;
+       char *path, *str;
+
+       if (g_path_is_absolute(name) || *name == '~' ||
+           (*name == '.' && name[1] == G_DIR_SEPARATOR))
+               path = g_strdup(name);
+       else {
+               /* first try from home dir */
+               str = g_strdup_printf("%s/modules", get_irssi_dir());
+               path = g_module_build_path(str, name);
+               g_free(str);
+
+               if (stat(path, &statbuf) == 0) {
+                       module = g_module_open(path, (GModuleFlags) 0);
+                       g_free(path);
+                       *found = TRUE;
+                       return module;
+               }
+
+               /* module not found from home dir, try global module dir */
+               g_free(path);
+               path = g_module_build_path(MODULEDIR, name);
+       }
+
+       *found = stat(path, &statbuf) == 0;
+       module = g_module_open(path, (GModuleFlags) 0);
+       g_free(path);
+       return module;
+}
+
+static char *module_get_func(const char *rootmodule, const char *submodule,
+                            const char *function)
+{
+       if (strcmp(submodule, "core") == 0)
+               return g_strconcat(rootmodule, "_core_", function, NULL);
+
+       if (strcmp(rootmodule, submodule) == 0)
+               return g_strconcat(rootmodule, "_", function, NULL);
+
+       return g_strconcat(submodule, "_", rootmodule, "_", function, NULL);
+}
+
+#define module_error(error, text, rootmodule, submodule) \
+       signal_emit("module error", 4, GINT_TO_POINTER(error), text, \
+                   rootmodule, submodule)
+
+/* Returns 1 if ok, 0 if error in module and
+   -1 if module wasn't found */
+static int module_load_name(const char *path, const char *rootmodule,
+                           const char *submodule, int silent)
+{
+       void (*module_init) (void);
+       void (*module_deinit) (void);
+       GModule *gmodule;
+        MODULE_REC *module;
+       MODULE_FILE_REC *rec;
+       char *initfunc, *deinitfunc;
+        int found;
+
+       gmodule = module_open(path, &found);
+       if (gmodule == NULL) {
+               if (!silent || found) {
+                       module_error(MODULE_ERROR_LOAD, g_module_error(),
+                                    rootmodule, submodule);
+               }
+               return found ? 0 : -1;
+       }
+
+       /* get the module's init() and deinit() functions */
+       initfunc = module_get_func(rootmodule, submodule, "init");
+       deinitfunc = module_get_func(rootmodule, submodule, "deinit");
+       found = g_module_symbol(gmodule, initfunc, (gpointer *) &module_init) &&
+               g_module_symbol(gmodule, deinitfunc, (gpointer *) &module_deinit);
+       g_free(initfunc);
+       g_free(deinitfunc);
+
+       if (!found) {
+               module_error(MODULE_ERROR_INVALID, NULL,
+                            rootmodule, submodule);
+               g_module_close(gmodule);
+               return 0;
+       }
+
+       /* Call the module's init() function - it should register itself
+          with module_register() function, abort if it doesn't. */
+       module_init();
+
+       module = module_find(rootmodule);
+       rec = module == NULL ? NULL :
+                strcmp(rootmodule, submodule) == 0 ?
+               module_file_find(module, "core") :
+               module_file_find(module, submodule);
+       if (rec == NULL) {
+               rec = module_register_full(rootmodule, submodule, NULL);
+               rec->gmodule = gmodule;
+               module_file_unload(rec);
+
+               module_error(MODULE_ERROR_INVALID, NULL,
+                            rootmodule, submodule);
+                return 0;
+       }
+
+        rec->module_deinit = module_deinit;
+       rec->gmodule = gmodule;
+        rec->initialized = TRUE;
+
+       settings_check_module(rec->defined_module_name);
+
+       signal_emit("module loaded", 2, rec->root, rec);
+       return 1;
+}
+
+static int module_load_prefixes(const char *path, const char *module,
+                               int start, int end, char **prefixes)
+{
+        GString *realpath;
+        int status, ok;
+
+        /* load module_core */
+       realpath = g_string_new(path);
+       g_string_insert(realpath, end, "_core");
+
+       /* Don't print the error message the first time, since the module
+          may not have the core part at all. */
+       status = module_load_name(realpath->str, module, "core", TRUE);
+        ok = status > 0;
+
+       if (prefixes != NULL) {
+               /* load all the "prefix modules", like the fe-common, irc,
+                  etc. part of the module */
+               while (*prefixes != NULL) {
+                        g_string_assign(realpath, path);
+                       g_string_insert_c(realpath, start, '_');
+                       g_string_insert(realpath, start, *prefixes);
+
+                       status = module_load_name(realpath->str, module,
+                                                 *prefixes, TRUE);
+                       if (status > 0)
+                               ok = TRUE;
+
+                        prefixes++;
+               }
+       }
+
+       if (!ok) {
+                /* error loading module, print the error message */
+               g_string_assign(realpath, path);
+               g_string_insert(realpath, end, "_core");
+               module_load_name(realpath->str, module, "core", FALSE);
+       }
+
+       g_string_free(realpath, TRUE);
+        return ok;
+}
+
+static int module_load_full(const char *path, const char *rootmodule,
+                           const char *submodule, int start, int end,
+                           char **prefixes)
+{
+       MODULE_REC *module;
+        int status, try_prefixes;
+
+       if (!g_module_supported())
+               return FALSE;
+
+       module = module_find(rootmodule);
+       if (module != NULL && (strcmp(submodule, rootmodule) == 0 ||
+                              module_file_find(module, submodule) != NULL)) {
+                /* module is already loaded */
+               module_error(MODULE_ERROR_ALREADY_LOADED, NULL,
+                            rootmodule, submodule);
+                return FALSE;
+       }
+
+       /* check if the given module exists.. */
+       try_prefixes = strcmp(rootmodule, submodule) == 0;
+       status = module_load_name(path, rootmodule, submodule, try_prefixes);
+       if (status == -1 && try_prefixes) {
+               /* nope, try loading the module_core,
+                  fe_module, etc. */
+               status = module_load_prefixes(path, rootmodule,
+                                             start, end, prefixes);
+       }
+
+       return status > 0;
+}
+
+/* Load module - automatically tries to load also the related non-core
+   modules given in `prefixes' (like irc, fe, fe_text, ..) */
+int module_load(const char *path, char **prefixes)
+{
+       char *exppath, *name, *submodule, *rootmodule;
+        int start, end, ret;
+
+       g_return_val_if_fail(path != NULL, FALSE);
+
+       exppath = convert_home(path);
+
+       name = module_get_name(exppath, &start, &end);
+       rootmodule = module_get_root(name, prefixes);
+       submodule = module_get_sub(name, rootmodule);
+       g_free(name);
+
+       ret = module_load_full(exppath, rootmodule, submodule,
+                              start, end, prefixes);
+
+       g_free(rootmodule);
+       g_free(submodule);
+        g_free(exppath);
+        return ret;
+}
+
+/* Load a sub module. */
+int module_load_sub(const char *path, const char *submodule, char **prefixes)
+{
+        GString *full_path;
+       char *exppath, *name, *rootmodule;
+        int start, end, ret;
+
+       g_return_val_if_fail(path != NULL, FALSE);
+       g_return_val_if_fail(submodule != NULL, FALSE);
+
+        exppath = convert_home(path);
+
+       name = module_get_name(exppath, &start, &end);
+       rootmodule = module_get_root(name, prefixes);
+       g_free(name);
+
+        full_path = g_string_new(exppath);
+       if (strcmp(submodule, "core") == 0)
+               g_string_insert(full_path, end, "_core");
+       else {
+               g_string_insert_c(full_path, start, '_');
+               g_string_insert(full_path, start, submodule);
+       }
+
+       ret = module_load_full(full_path->str, rootmodule, submodule,
+                              start, end, NULL);
+
+       g_string_free(full_path, TRUE);
+       g_free(rootmodule);
+       g_free(exppath);
+        return ret;
+}
+
+static void module_file_deinit_gmodule(MODULE_FILE_REC *file)
+{
+       /* call the module's deinit() function */
+        if (file->module_deinit != NULL)
+               file->module_deinit();
+
+       if (file->defined_module_name != NULL) {
+               settings_remove_module(file->defined_module_name);
+               commands_remove_module(file->defined_module_name);
+               signals_remove_module(file->defined_module_name);
+       }
+
+       g_module_close(file->gmodule);
+}
+
+void module_file_unload(MODULE_FILE_REC *file)
+{
+       MODULE_REC *root;
+
+        root = file->root;
+       root->files = g_slist_remove(root->files, file);
+
+        if (file->initialized)
+               signal_emit("module unloaded", 2, file->root, file);
+
+       if (file->gmodule != NULL)
+                module_file_deinit_gmodule(file);
+
+       g_free(file->name);
+       g_free(file->defined_module_name);
+       g_free(file);
+
+       if (root->files == NULL && g_slist_find(modules, root) != NULL)
+                module_unload(root);
+}
+
+void module_unload(MODULE_REC *module)
+{
+       g_return_if_fail(module != NULL);
+
+       modules = g_slist_remove(modules, module);
+
+       signal_emit("module unloaded", 1, module);
+
+       while (module->files != NULL)
+                module_file_unload(module->files->data);
+
+        g_free(module->name);
+       g_free(module);
+}
+
+#else /* !HAVE_GMODULE - modules are not supported */
+
+int module_load(const char *path, char **prefixes)
+{
+        return FALSE;
+}
+
+void module_unload(MODULE_REC *module)
+{
+}
+
+#endif
diff --git a/apps/irssi/src/core/modules-load.h b/apps/irssi/src/core/modules-load.h
new file mode 100644 (file)
index 0000000..42e1438
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __MODULES_LOAD_H
+#define __MODULES_LOAD_H
+
+#include "modules.h"
+
+/* Load module - automatically tries to load also the related non-core
+   modules given in `prefixes' (like irc, fe, fe_text, ..) */
+int module_load(const char *path, char **prefixes);
+
+/* Load a sub module. */
+int module_load_sub(const char *path, const char *submodule, char **prefixes);
+
+void module_unload(MODULE_REC *module);
+void module_file_unload(MODULE_FILE_REC *file);
+
+#endif
index 9797a059c91e6b444cbb3831802d0fae6388b06b..dc37318912d3bcdd0f5135801ccb2738c850e07a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  modules.c : irssi
 
 /*
  modules.c : irssi
 
-    Copyright (C) 1999-2000 Timo Sirainen
+    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
 
     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
@@ -22,9 +22,6 @@
 #include "modules.h"
 #include "signals.h"
 
 #include "modules.h"
 #include "signals.h"
 
-#include "commands.h"
-#include "settings.h"
-
 GSList *modules;
 
 static GHashTable *uniqids, *uniqstrids;
 GSList *modules;
 
 static GHashTable *uniqids, *uniqstrids;
@@ -201,206 +198,62 @@ void module_uniq_destroy(const char *module)
        }
 }
 
        }
 }
 
-MODULE_REC *module_find(const char *name)
+/* Register a new module. The `name' is the root module name, `submodule'
+   specifies the current module to be registered (eg. "perl", "fe").
+   The module is registered as statically loaded by default. */
+MODULE_FILE_REC *module_register_full(const char *name, const char *submodule,
+                                     const char *defined_module_name)
 {
 {
-       GSList *tmp;
+       MODULE_REC *module;
+        MODULE_FILE_REC *file;
 
 
-       for (tmp = modules; tmp != NULL; tmp = tmp->next) {
-               MODULE_REC *rec = tmp->data;
+       module = module_find(name);
+       if (module == NULL) {
+               module = g_new0(MODULE_REC, 1);
+               module->name = g_strdup(name);
 
 
-               if (g_strcasecmp(rec->name, name) == 0)
-                       return rec;
+                modules = g_slist_append(modules, module);
        }
 
        }
 
-       return NULL;
-}
-
-#ifdef HAVE_GMODULE
-static char *module_get_name(const char *path, int *start, int *end)
-{
-       const char *name;
-       char *module_name, *ptr;
+       file = module_file_find(module, submodule);
+       if (file != NULL)
+               return file;
 
 
-        name = NULL;
-       if (g_path_is_absolute(path)) {
-               name = strrchr(path, G_DIR_SEPARATOR);
-                if (name != NULL) name++;
-       }
+       file = g_new0(MODULE_FILE_REC, 1);
+       file->root = module;
+       file->name = g_strdup(submodule);
+        file->defined_module_name = g_strdup(defined_module_name);
 
 
-       if (name == NULL)
-               name = path;
-
-       if (strncmp(name, "lib", 3) == 0)
-               name += 3;
-
-       module_name = g_strdup(name);
-       ptr = strchr(module_name, '.');
-       if (ptr != NULL) *ptr = '\0';
-
-       *start = (int) (name-path);
-       *end = *start + (ptr == NULL ? strlen(name) :
-                        (int) (module_name-ptr));
-
-       return module_name;
+       module->files = g_slist_append(module->files, file);
+        return file;
 }
 
 }
 
-static GModule *module_open(const char *name)
+MODULE_REC *module_find(const char *name)
 {
 {
-       struct stat statbuf;
-       GModule *module;
-       char *path, *str;
-
-       if (g_path_is_absolute(name) ||
-           (*name == '.' && name[1] == G_DIR_SEPARATOR))
-               path = g_strdup(name);
-       else {
-               /* first try from home dir */
-               str = g_strdup_printf("%s/.silc/modules", g_get_home_dir());
-               path = g_module_build_path(str, name);
-               g_free(str);
-
-               if (stat(path, &statbuf) == 0) {
-                       module = g_module_open(path, (GModuleFlags) 0);
-                       g_free(path);
-                       return module;
-               }
-
-               /* module not found from home dir, try global module dir */
-               g_free(path);
-               path = g_module_build_path(MODULEDIR, name);
-       }
-
-       module = g_module_open(path, (GModuleFlags) 0);
-       g_free(path);
-       return module;
-}
-
-#define module_error(error, module, text) \
-       signal_emit("module error", 3, GINT_TO_POINTER(error), module, text)
+       GSList *tmp;
 
 
-static int module_load_name(const char *path, const char *name, int silent)
-{
-       void (*module_init) (void);
-       GModule *gmodule;
-       MODULE_REC *rec;
-       char *initfunc;
-
-       gmodule = module_open(path);
-       if (gmodule == NULL) {
-               if (!silent) {
-                       module_error(MODULE_ERROR_LOAD, name,
-                                    g_module_error());
-               }
-               return FALSE;
-       }
+       for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+               MODULE_REC *rec = tmp->data;
 
 
-       /* get the module's init() function */
-       initfunc = g_strconcat(name, "_init", NULL);
-       if (!g_module_symbol(gmodule, initfunc, (gpointer *) &module_init)) {
-               if (!silent)
-                       module_error(MODULE_ERROR_INVALID, name, NULL);
-               g_module_close(gmodule);
-               g_free(initfunc);
-               return FALSE;
+               if (g_strcasecmp(rec->name, name) == 0)
+                       return rec;
        }
        }
-       g_free(initfunc);
-
-       rec = g_new0(MODULE_REC, 1);
-       rec->name = g_strdup(name);
-        rec->gmodule = gmodule;
-       modules = g_slist_append(modules, rec);
 
 
-       module_init();
-       settings_check_module(name);
-
-       signal_emit("module loaded", 1, rec);
-       return TRUE;
+       return NULL;
 }
 }
-#endif
 
 
-/* Load module - automatically tries to load also the related non-core
-   modules given in `prefixes' (like irc, fe, fe_text, ..) */
-int module_load(const char *path, char **prefixes)
+MODULE_FILE_REC *module_file_find(MODULE_REC *module, const char *name)
 {
 {
-#ifdef HAVE_GMODULE
-        GString *realpath;
-       char *name, *pname;
-       int ret, start, end;
-
-       g_return_val_if_fail(path != NULL, FALSE);
-
-       if (!g_module_supported())
-               return FALSE;
+       GSList *tmp;
 
 
-       name = module_get_name(path, &start, &end);
-       if (module_find(name)) {
-               module_error(MODULE_ERROR_ALREADY_LOADED, name, NULL);
-                g_free(name);
-               return FALSE;
-       }
+       for (tmp = module->files; tmp != NULL; tmp = tmp->next) {
+               MODULE_FILE_REC *rec = tmp->data;
 
 
-        /* load "module_core" instead of "module" if it exists */
-       realpath = g_string_new(path);
-       g_string_insert(realpath, end, "_core");
-
-        pname = g_strconcat(name, "_core", NULL);
-       ret = module_load_name(realpath->str, pname, TRUE);
-       g_free(pname);
-
-       if (!ret) {
-                /* load "module" - complain if it's not found */
-               ret = module_load_name(path, name, FALSE);
-       } else if (prefixes != NULL) {
-               /* load all the "prefix modules", like the fe-common, irc,
-                  etc. part of the module */
-               while (*prefixes != NULL) {
-                        g_string_assign(realpath, path);
-                       g_string_insert(realpath, start, "_");
-                       g_string_insert(realpath, start, *prefixes);
-
-                        pname = g_strconcat(*prefixes, "_", name, NULL);
-                       module_load_name(realpath->str, pname, TRUE);
-                       g_free(pname);
-
-                        prefixes++;
-               }
+               if (strcmp(rec->name, name) == 0)
+                        return rec;
        }
 
        }
 
-        g_string_free(realpath, TRUE);
-       g_free(name);
-       return ret;
-#else
-        return FALSE;
-#endif
-}
-
-void module_unload(MODULE_REC *module)
-{
-#ifdef HAVE_GMODULE
-       void (*module_deinit) (void);
-       char *deinitfunc;
-
-       g_return_if_fail(module != NULL);
-
-       modules = g_slist_remove(modules, module);
-
-       signal_emit("module unloaded", 1, module);
-
-       /* call the module's deinit() function */
-       deinitfunc = g_strconcat(module->name, "_deinit", NULL);
-       if (g_module_symbol(module->gmodule, deinitfunc,
-                           (gpointer *) &module_deinit))
-               module_deinit();
-       g_free(deinitfunc);
-
-        settings_remove_module(module->name);
-       commands_remove_module(module->name);
-       signals_remove_module(module->name);
-
-       g_module_close(module->gmodule);
-       g_free(module->name);
-       g_free(module);
-#endif
+        return NULL;
 }
 
 static void uniq_get_modules(char *key, void *value, GSList **list)
 }
 
 static void uniq_get_modules(char *key, void *value, GSList **list)
index 7a83819f61a6f5b1030f5ef7b362904ef2392c21..75a77c7752dc74d649c4a25d74155d3ad42c7872 100644 (file)
 #define MODULE_DATA_SET(rec, data) \
        g_hash_table_insert((rec)->module_data, MODULE_NAME, data)
 
 #define MODULE_DATA_SET(rec, data) \
        g_hash_table_insert((rec)->module_data, MODULE_NAME, data)
 
+#define MODULE_DATA_UNSET(rec) \
+       g_hash_table_remove((rec)->module_data, MODULE_NAME)
+
 #define MODULE_DATA(rec) \
        g_hash_table_lookup((rec)->module_data, MODULE_NAME)
 
 #define MODULE_DATA(rec) \
        g_hash_table_lookup((rec)->module_data, MODULE_NAME)
 
+
+#ifdef HAVE_GMODULE
+#  define MODULE_IS_STATIC(rec) \
+        ((rec)->gmodule == NULL)
+#else
+#  define MODULE_IS_STATIC(rec) TRUE
+#endif
+
 enum {
        MODULE_ERROR_ALREADY_LOADED,
        MODULE_ERROR_LOAD,
        MODULE_ERROR_INVALID
 };
 
 enum {
        MODULE_ERROR_ALREADY_LOADED,
        MODULE_ERROR_LOAD,
        MODULE_ERROR_INVALID
 };
 
+typedef struct _MODULE_REC MODULE_REC;
+
 typedef struct {
 typedef struct {
+       MODULE_REC *root;
        char *name;
        char *name;
+        char *defined_module_name;
+       void (*module_deinit) (void);
+
 #ifdef HAVE_GMODULE
 #ifdef HAVE_GMODULE
-       GModule *gmodule;
+       GModule *gmodule; /* static, if NULL */
 #endif
 #endif
-} MODULE_REC;
+       unsigned int initialized:1;
+} MODULE_FILE_REC;
+
+struct _MODULE_REC {
+       char *name;
+        GSList *files; /* list of modules that belong to this root module */
+};
 
 extern GSList *modules;
 
 
 extern GSList *modules;
 
-MODULE_REC *module_find(const char *name);
+/* Register a new module. The `name' is the root module name, `submodule'
+   specifies the current module to be registered (eg. "perl", "fe").
+   The module is registered as statically loaded by default. */
+MODULE_FILE_REC *module_register_full(const char *name, const char *submodule,
+                                     const char *defined_module_name);
+#define module_register(name, submodule) \
+        module_register_full(name, submodule, MODULE_NAME)
 
 
-/* Load module - automatically tries to load also the related non-core
-   modules given in `prefixes' (like irc, fe, fe_text, ..) */
-int module_load(const char *path, char **prefixes);
-void module_unload(MODULE_REC *module);
+MODULE_REC *module_find(const char *name);
+MODULE_FILE_REC *module_file_find(MODULE_REC *module, const char *name);
 
 #define MODULE_CHECK_CAST(object, cast, type_field, id) \
        ((cast *) module_check_cast(object, offsetof(cast, type_field), id))
 
 #define MODULE_CHECK_CAST(object, cast, type_field, id) \
        ((cast *) module_check_cast(object, offsetof(cast, type_field), id))
index b0e9535ba4d717019efd9dd55c85eb33cb4f1210..f232f1f46a351bd00f726b6aeabc297a214d9aaa 100644 (file)
@@ -42,6 +42,7 @@ static void net_disconnect_remove(NET_DISCONNECT_REC *rec)
        disconnects = g_slist_remove(disconnects, rec);
 
        g_source_remove(rec->tag);
        disconnects = g_slist_remove(disconnects, rec);
 
        g_source_remove(rec->tag);
+        net_disconnect(rec->handle);
        g_free(rec);
 }
 
        g_free(rec);
 }
 
index 04eab80aabde7927f2b5495ee26b50b62c55bd29..fd1039d67b7fb69c044d0f942c1ed71f3d14e71b 100644 (file)
@@ -62,7 +62,7 @@ void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close)
        g_free(rec);
 }
 
        g_free(rec);
 }
 
-/* Transmit all data from buffer - return TRUE if successful */
+/* Transmit all data from buffer - return TRUE if the whole buffer was sent */
 static int buffer_send(NET_SENDBUF_REC *rec)
 {
        int ret;
 static int buffer_send(NET_SENDBUF_REC *rec)
 {
        int ret;
@@ -140,6 +140,25 @@ int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size)
        return buffer_add(rec, data, size) ? 0 : -1;
 }
 
        return buffer_add(rec, data, size) ? 0 : -1;
 }
 
+/* Flush the buffer, blocks until finished. */
+void net_sendbuffer_flush(NET_SENDBUF_REC *rec)
+{
+       int handle;
+
+       if (rec->buffer == NULL)
+               return;
+
+        /* set the socket blocking while doing this */
+       handle = g_io_channel_unix_get_fd(rec->handle);
+#ifndef WIN32
+       fcntl(handle, F_SETFL, 0);
+#endif
+       while (!buffer_send(rec)) ;
+#ifndef WIN32
+       fcntl(handle, F_SETFL, O_NONBLOCK);
+#endif
+}
+
 /* Returns the socket handle */
 GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec)
 {
 /* Returns the socket handle */
 GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec)
 {
index bb6d8e07f07e6f4e8b370c3deae36d81b06f2e8c..d2388b419746ff9361cca613dac0f0f301fd643d 100644 (file)
@@ -14,6 +14,9 @@ void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close);
    occured. */
 int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size);
 
    occured. */
 int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size);
 
+/* Flush the buffer, blocks until finished. */
+void net_sendbuffer_flush(NET_SENDBUF_REC *rec);
+
 /* Returns the socket handle */
 GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec);
 
 /* Returns the socket handle */
 GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec);
 
index 4fc06c05f2fd06a15eb1fec73b1ad80ca162cc8f..6be871132df4e3695416c166f15a5bf689345b9c 100644 (file)
@@ -196,10 +196,14 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
        setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE,
                   (char *) &opt, sizeof(opt));
 
        setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE,
                   (char *) &opt, sizeof(opt));
 
-       /* set our own address, ignore if bind() fails */
+       /* set our own address */
        if (my_ip != NULL) {
                sin_set_ip(&so, my_ip);
        if (my_ip != NULL) {
                sin_set_ip(&so, my_ip);
-               bind(handle, &so.sa, SIZEOF_SOCKADDR(so));
+               if (bind(handle, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
+                       /* failed, set it back to INADDR_ANY */
+                       sin_set_ip(&so, NULL);
+                       bind(handle, &so.sa, SIZEOF_SOCKADDR(so));
+               }
        }
 
        /* connect */
        }
 
        /* connect */
@@ -208,10 +212,11 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
        ret = connect(handle, &so.sa, SIZEOF_SOCKADDR(so));
 
 #ifndef WIN32
        ret = connect(handle, &so.sa, SIZEOF_SOCKADDR(so));
 
 #ifndef WIN32
-       if (ret < 0 && errno != EINPROGRESS) {
+       if (ret < 0 && errno != EINPROGRESS)
 #else
 #else
-       if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) {
+       if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
 #endif
 #endif
+       {
                close(handle);
                return NULL;
        }
                close(handle);
                return NULL;
        }
@@ -245,7 +250,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
        /* create the socket */
        handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
 #ifdef HAVE_IPV6
        /* create the socket */
        handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
 #ifdef HAVE_IPV6
-       if (handle == -1 && errno == EINVAL) {
+       if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
                /* IPv6 is not supported by OS */
                so.sin.sin_family = AF_INET;
                so.sin.sin_addr.s_addr = INADDR_ANY;
                /* IPv6 is not supported by OS */
                so.sin.sin_family = AF_INET;
                so.sin.sin_addr.s_addr = INADDR_ANY;
@@ -373,9 +378,8 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
 {
 #ifdef HAVE_IPV6
        union sockaddr_union *so;
 {
 #ifdef HAVE_IPV6
        union sockaddr_union *so;
-       struct addrinfo hints, *ai, *origai;
-       char hbuf[NI_MAXHOST];
-       int host_error, count;
+       struct addrinfo hints, *ai, *ailist;
+       int ret, count;
 #else
        struct hostent *hp;
 #endif
 #else
        struct hostent *hp;
 #endif
@@ -390,16 +394,12 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
        hints.ai_socktype = SOCK_STREAM;
 
        /* save error to host_error for later use */
        hints.ai_socktype = SOCK_STREAM;
 
        /* save error to host_error for later use */
-       host_error = getaddrinfo(addr, NULL, &hints, &ai);
-       if (host_error != 0)
-               return host_error;
+       ret = getaddrinfo(addr, NULL, &hints, &ailist);
+       if (ret != 0)
+               return ret;
 
 
-       if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf,
-                       sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
-               return 1;
-
-        origai = ai; count = 0;
-       while (ai != NULL && count < 2) {
+        count = 0;
+       for (ai = ailist; ai != NULL && count < 2; ai = ai->ai_next) {
                so = (union sockaddr_union *) ai->ai_addr;
 
                if (ai->ai_family == AF_INET6 && ip6->family == 0) {
                so = (union sockaddr_union *) ai->ai_addr;
 
                if (ai->ai_family == AF_INET6 && ip6->family == 0) {
@@ -409,18 +409,20 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
                        sin_get_ip(so, ip4);
                         count++;
                }
                        sin_get_ip(so, ip4);
                         count++;
                }
-                ai = ai->ai_next;
        }
        }
-       freeaddrinfo(origai);
+       freeaddrinfo(ailist);
+       return count > 0 ? 0 : 1;
 #else
        hp = gethostbyname(addr);
 #else
        hp = gethostbyname(addr);
-       if (hp == NULL) return h_errno;
+       if (hp == NULL)
+                return -1;
+               //return h_errno;
 
        ip4->family = AF_INET;
        memcpy(&ip4->ip, hp->h_addr, 4);
 
        ip4->family = AF_INET;
        memcpy(&ip4->ip, hp->h_addr, 4);
-#endif
 
        return 0;
 
        return 0;
+#endif
 }
 
 /* Get name for host, *name should be g_free()'d unless it's NULL.
 }
 
 /* Get name for host, *name should be g_free()'d unless it's NULL.
@@ -577,7 +579,7 @@ char *net_getservbyport(int port)
 int is_ipv4_address(const char *host)
 {
        while (*host != '\0') {
 int is_ipv4_address(const char *host)
 {
        while (*host != '\0') {
-               if (*host != '.' && !isdigit(*host))
+               if (*host != '.' && !i_isdigit(*host))
                        return 0;
                 host++;
        }
                        return 0;
                 host++;
        }
index c4cd8bfd56fa79f2f52a87c4b5c4804677b66ffe..d0dbce3dacf96cab9994abd5a83ccf56fbaa4da7 100644 (file)
@@ -20,6 +20,8 @@ unsigned int op:1;
 unsigned int halfop:1;
 unsigned int voice:1;
 
 unsigned int halfop:1;
 unsigned int voice:1;
 
+/*GHashTable *module_data;*/
+
 void *unique_id; /* unique ID to use for comparing if one nick is in another channels,
                    or NULL = nicks are unique, just keep comparing them. */
 NICK_REC *next; /* support for multiple identically named nicks */
 void *unique_id; /* unique ID to use for comparing if one nick is in another channels,
                    or NULL = nicks are unique, just keep comparing them. */
 NICK_REC *next; /* support for multiple identically named nicks */
index 96f6a8ea2b870e7398d338d6506e06aa522f07d3..b7e8540725c4b5c8902e710437e75f1ae9b95dd6 100644 (file)
@@ -28,7 +28,7 @@
 #include "masks.h"
 
 #define isalnumhigh(a) \
 #include "masks.h"
 
 #define isalnumhigh(a) \
-        (isalnum(a) || (unsigned char) (a) >= 128)
+        (i_isalnum(a) || (unsigned char) (a) >= 128)
 
 static void nick_hash_add(CHANNEL_REC *channel, NICK_REC *nick)
 {
 
 static void nick_hash_add(CHANNEL_REC *channel, NICK_REC *nick)
 {
@@ -76,6 +76,8 @@ static void nick_hash_remove(CHANNEL_REC *channel, NICK_REC *nick)
 /* Add new nick to list */
 void nicklist_insert(CHANNEL_REC *channel, NICK_REC *nick)
 {
 /* Add new nick to list */
 void nicklist_insert(CHANNEL_REC *channel, NICK_REC *nick)
 {
+       /*MODULE_DATA_INIT(nick);*/
+
        nick->type = module_get_uniq_id("NICK", 0);
         nick->chat_type = channel->chat_type;
 
        nick->type = module_get_uniq_id("NICK", 0);
         nick->chat_type = channel->chat_type;
 
@@ -100,6 +102,7 @@ static void nicklist_destroy(CHANNEL_REC *channel, NICK_REC *nick)
 {
        signal_emit("nicklist remove", 2, channel, nick);
 
 {
        signal_emit("nicklist remove", 2, channel, nick);
 
+        /*MODULE_DATA_DEINIT(nick);*/
        g_free(nick->nick);
        g_free_not_null(nick->realname);
        g_free_not_null(nick->host);
        g_free(nick->nick);
        g_free_not_null(nick->realname);
        g_free_not_null(nick->host);
@@ -353,17 +356,39 @@ GSList *nicklist_get_same_unique(SERVER_REC *server, void *id)
 /* nick record comparision for sort functions */
 int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
 {
 /* nick record comparision for sort functions */
 int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
 {
+       int status1, status2;
+       
        if (p1 == NULL) return -1;
        if (p2 == NULL) return 1;
 
        if (p1 == NULL) return -1;
        if (p2 == NULL) return 1;
 
-       if (p1->op && !p2->op) return -1;
-       if (!p1->op && p2->op) return 1;
-
-       if (!p1->op) {
-               if (p1->voice && !p2->voice) return -1;
-               if (!p1->voice && p2->voice) return 1;
-       }
-
+       /* we assign each status (op, halfop, voice, normal) a number
+        * and compare them. this is easier than 100,000 if's and
+        * returns :-)
+        * -- yath */
+
+       if (p1->op)
+               status1 = 4;
+       else if (p1->halfop)
+               status1 = 3;
+       else if (p1->voice)
+               status1 = 2;
+       else
+               status1 = 1;
+
+       if (p2->op)
+               status2 = 4;
+       else if (p2->halfop)
+               status2 = 3;
+       else if (p2->voice)
+               status2 = 2;
+       else
+               status2 = 1;
+       
+       if (status1 < status2)
+               return 1;
+       else if (status1 > status2)
+               return -1;
+       
        return g_strcasecmp(p1->nick, p2->nick);
 }
 
        return g_strcasecmp(p1->nick, p2->nick);
 }
 
@@ -507,10 +532,10 @@ int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick)
 
                /* check if it matches for alphanumeric parts of nick */
                while (*nick != '\0' && *msg != '\0') {
 
                /* check if it matches for alphanumeric parts of nick */
                while (*nick != '\0' && *msg != '\0') {
-                       if (toupper(*nick) == toupper(*msg)) {
+                       if (i_toupper(*nick) == i_toupper(*msg)) {
                                /* total match */
                                msg++;
                                /* total match */
                                msg++;
-                       } else if (isalnum(*msg) && !isalnum(*nick)) {
+                       } else if (i_isalnum(*msg) && !i_isalnum(*nick)) {
                                /* some strange char in your nick, pass it */
                                 fullmatch = FALSE;
                        } else
                                /* some strange char in your nick, pass it */
                                 fullmatch = FALSE;
                        } else
@@ -527,7 +552,7 @@ int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick)
                                /* remove the rest of the non-alphanum chars
                                   from nick and check if it then matches. */
                                 fullmatch = FALSE;
                                /* remove the rest of the non-alphanum chars
                                   from nick and check if it then matches. */
                                 fullmatch = FALSE;
-                               while (*nick != '\0' && !isalnum(*nick))
+                               while (*nick != '\0' && !i_isalnum(*nick))
                                        nick++;
                        }
 
                                        nick++;
                        }
 
index f54aa34d06ce6a9de55e5ab104ccfc3a6efa1949..bb48d0d07ebe862dc085f287a95d50baa8cbab19 100644 (file)
@@ -41,6 +41,13 @@ void pidwait_remove(int pid)
        pids = g_slist_remove(pids, GINT_TO_POINTER(pid));
 }
 
        pids = g_slist_remove(pids, GINT_TO_POINTER(pid));
 }
 
+/* return list of pids that are being waited.
+   don't free the return value. */
+GSList *pidwait_get_pids(void)
+{
+        return pids;
+}
+
 static int child_check(void)
 {
        GSList *tmp, *next;
 static int child_check(void)
 {
        GSList *tmp, *next;
index 3f6b84cdf7ac4e1ad6ffa6dcd80cd9d8dddf3627..ae1ba01e2fe9e75cc3b149ecc052d12b01017db5 100644 (file)
@@ -9,4 +9,8 @@ void pidwait_add(int pid);
 /* remove pid from wait list */
 void pidwait_remove(int pid);
 
 /* remove pid from wait list */
 void pidwait_remove(int pid);
 
+/* return list of pids that are being waited.
+   don't free the return value. */
+GSList *pidwait_get_pids(void);
+
 #endif
 #endif
index d1c513529ebe8b5e99a051f07b692269771ec291..cc16ad1e60877141531fb466e46ad9340e73a4d1 100644 (file)
@@ -36,6 +36,7 @@ void query_init(QUERY_REC *query, int automatic)
 
         MODULE_DATA_INIT(query);
        query->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
 
         MODULE_DATA_INIT(query);
        query->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
+        query->destroy = (void (*) (WI_ITEM_REC *)) query_destroy;
        if (query->server_tag != NULL) {
                query->server = server_find_tag(query->server_tag);
                if (query->server != NULL) {
        if (query->server_tag != NULL) {
                query->server = server_find_tag(query->server_tag);
                if (query->server != NULL) {
@@ -66,6 +67,8 @@ void query_destroy(QUERY_REC *query)
         g_free_not_null(query->server_tag);
         g_free_not_null(query->address);
        g_free(query->name);
         g_free_not_null(query->server_tag);
         g_free_not_null(query->address);
        g_free(query->name);
+
+        query->type = 0;
        g_free(query);
 }
 
        g_free(query);
 }
 
index 4e47040cb1e28fe443e4f2bfed193a4d1a75f1c1..d605ccb5934e3791910ebfcd221d24851fe24d96 100644 (file)
 #include "rawlog.h"
 #include "modules.h"
 #include "signals.h"
 #include "rawlog.h"
 #include "modules.h"
 #include "signals.h"
+#include "commands.h"
 #include "misc.h"
 #include "write-buffer.h"
 #include "settings.h"
 
 #include "misc.h"
 #include "write-buffer.h"
 #include "settings.h"
 
+#include "servers.h"
+
 static int rawlog_lines;
 static int signal_rawlog;
 static int log_file_create_mode;
 static int rawlog_lines;
 static int signal_rawlog;
 static int log_file_create_mode;
@@ -158,6 +161,43 @@ static void read_settings(void)
        log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
 }
 
        log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
 }
 
+static void cmd_rawlog(const char *data, SERVER_REC *server, void *item)
+{
+       command_runsub("rawlog", data, server, item);
+}
+
+/* SYNTAX: RAWLOG SAVE <file> */
+static void cmd_rawlog_save(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+       if (server == NULL || server->rawlog == NULL)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+       rawlog_save(server->rawlog, data);
+}
+
+/* SYNTAX: RAWLOG OPEN <file> */
+static void cmd_rawlog_open(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+       if (server == NULL || server->rawlog == NULL)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+       rawlog_open(server->rawlog, data);
+}
+
+/* SYNTAX: RAWLOG CLOSE */
+static void cmd_rawlog_close(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+       if (server == NULL || server->rawlog == NULL)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       rawlog_close(server->rawlog);
+}
+
 void rawlog_init(void)
 {
        signal_rawlog = signal_get_uniq_id("rawlog");
 void rawlog_init(void)
 {
        signal_rawlog = signal_get_uniq_id("rawlog");
@@ -166,9 +206,19 @@ void rawlog_init(void)
        read_settings();
 
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
        read_settings();
 
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_bind("rawlog", NULL, (SIGNAL_FUNC) cmd_rawlog);
+       command_bind("rawlog save", NULL, (SIGNAL_FUNC) cmd_rawlog_save);
+       command_bind("rawlog open", NULL, (SIGNAL_FUNC) cmd_rawlog_open);
+       command_bind("rawlog close", NULL, (SIGNAL_FUNC) cmd_rawlog_close);
 }
 
 void rawlog_deinit(void)
 {
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void rawlog_deinit(void)
 {
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_unbind("rawlog", (SIGNAL_FUNC) cmd_rawlog);
+       command_unbind("rawlog save", (SIGNAL_FUNC) cmd_rawlog_save);
+       command_unbind("rawlog open", (SIGNAL_FUNC) cmd_rawlog_open);
+       command_unbind("rawlog close", (SIGNAL_FUNC) cmd_rawlog_close);
 }
 }
index f1b3d07536c73eaf9a15dd66c787fba93fbd55d0..a59880e4f4fef4e3b5428156dd752ddd57baaccd 100644 (file)
@@ -3,12 +3,15 @@
 int type; /* module_get_uniq_id("SERVER CONNECT", 0) */
 int chat_type; /* chat_protocol_lookup(xx) */
 
 int type; /* module_get_uniq_id("SERVER CONNECT", 0) */
 int chat_type; /* chat_protocol_lookup(xx) */
 
+int refcount;
+
 /* if we're connecting via proxy, or just NULLs */
 char *proxy;
 int proxy_port;
 /* if we're connecting via proxy, or just NULLs */
 char *proxy;
 int proxy_port;
-char *proxy_string, *proxy_password;
+char *proxy_string, *proxy_string_after, *proxy_password;
 
 unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */
 
 unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */
+char *tag; /* try to keep this tag when connected to server */
 char *address;
 int port;
 char *chatnet;
 char *address;
 int port;
 char *chatnet;
@@ -22,5 +25,6 @@ char *realname;
 
 /* when reconnecting, the old server status */
 unsigned int reconnection:1; /* we're trying to reconnect */
 
 /* when reconnecting, the old server status */
 unsigned int reconnection:1; /* we're trying to reconnect */
+unsigned int no_autojoin_channels:1; /* don't autojoin any channels */
 char *channels;
 char *away_reason;
 char *channels;
 char *away_reason;
index 7cdc66e5d9360cb68750c00e1972cd1e102a9d02..0003f698138cbdd500996e5011448dc0a811ee06 100644 (file)
@@ -3,6 +3,8 @@
 int type; /* module_get_uniq_id("SERVER", 0) */
 int chat_type; /* chat_protocol_lookup(xx) */
 
 int type; /* module_get_uniq_id("SERVER", 0) */
 int chat_type; /* chat_protocol_lookup(xx) */
 
+int refcount;
+
 STRUCT_SERVER_CONNECT_REC *connrec;
 time_t connect_time; /* connection time */
 time_t real_connect_time; /* time when server replied that we really are connected */
 STRUCT_SERVER_CONNECT_REC *connrec;
 time_t connect_time; /* connection time */
 time_t real_connect_time; /* time when server replied that we really are connected */
@@ -10,8 +12,11 @@ time_t real_connect_time; /* time when server replied that we really are connect
 char *tag; /* tag name for addressing server */
 char *nick; /* current nick */
 
 char *tag; /* tag name for addressing server */
 char *nick; /* current nick */
 
-unsigned int connected:1; /* connected to server */
+unsigned int connected:1; /* Connected to server */
+unsigned int disconnected:1; /* Disconnected, waiting for refcount to drop zero */
 unsigned int connection_lost:1; /* Connection lost unintentionally */
 unsigned int connection_lost:1; /* Connection lost unintentionally */
+unsigned int session_reconnect:1; /* Connected to this server with /UPGRADE */
+unsigned int no_reconnect:1; /* Don't reconnect to server */
 
 NET_SENDBUF_REC *handle;
 int readtag; /* input tag */
 
 NET_SENDBUF_REC *handle;
 int readtag; /* input tag */
@@ -21,11 +26,6 @@ GIOChannel *connect_pipe[2];
 int connect_tag;
 int connect_pid;
 
 int connect_tag;
 int connect_pid;
 
-/* For deciding if event should be handled internally */
-GHashTable *eventtable; /* "event xxx" : GSList* of REDIRECT_RECs */
-GHashTable *eventgrouptable; /* event group : GSList* of REDIRECT_RECs */
-GHashTable *cmdtable; /* "command xxx" : REDIRECT_CMD_REC* */
-
 RAWLOG_REC *rawlog;
 LINEBUF_REC *buffer; /* receive buffer */
 GHashTable *module_data;
 RAWLOG_REC *rawlog;
 LINEBUF_REC *buffer; /* receive buffer */
 GHashTable *module_data;
@@ -38,7 +38,7 @@ unsigned int usermode_away:1;
 unsigned int banned:1; /* not allowed to connect to this server */
 unsigned int dns_error:1; /* DNS said the host doesn't exist */
 
 unsigned int banned:1; /* not allowed to connect to this server */
 unsigned int dns_error:1; /* DNS said the host doesn't exist */
 
-time_t lag_sent; /* 0 or time when last lag query was sent to server */
+GTimeVal lag_sent; /* 0 or time when last lag query was sent to server */
 time_t lag_last_check; /* last time we checked lag */
 int lag; /* server lag in milliseconds */
 
 time_t lag_last_check; /* last time we checked lag */
 int lag; /* server lag in milliseconds */
 
@@ -55,12 +55,13 @@ void (*channels_join)(SERVER_REC *server, const char *data, int automatic);
 /* returns true if `flag' indicates a nick flag (op/voice/halfop) */
 int (*isnickflag)(char flag);
 /* returns true if `data' indicates a channel */
 /* returns true if `flag' indicates a nick flag (op/voice/halfop) */
 int (*isnickflag)(char flag);
 /* returns true if `data' indicates a channel */
-int (*ischannel)(const char *data);
+int (*ischannel)(SERVER_REC *server, const char *data);
 /* returns all nick flag characters in order op, voice, halfop. If some
    of them aren't supported '\0' can be used. */
 const char *(*get_nick_flags)(void);
 /* send public or private message to server */
 /* returns all nick flag characters in order op, voice, halfop. If some
    of them aren't supported '\0' can be used. */
 const char *(*get_nick_flags)(void);
 /* send public or private message to server */
-void (*send_message)(SERVER_REC *server, const char *target, const char *msg);
+void (*send_message)(SERVER_REC *server, const char *target,
+                    const char *msg, int target_type);
 
 /* -- Default implementations are used if NULL -- */
 CHANNEL_REC *(*channel_find_func)(SERVER_REC *server, const char *name);
 
 /* -- Default implementations are used if NULL -- */
 CHANNEL_REC *(*channel_find_func)(SERVER_REC *server, const char *name);
index 4352bef7b0ce0ef6adefb4a934c3ce82c4945aeb..f94ec9aed6dcccf17b6d2e90844e61b8414e35e8 100644 (file)
@@ -14,6 +14,7 @@ IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
 time_t last_connect; /* to avoid reconnecting too fast.. */
 
 unsigned int autoconnect:1;
 time_t last_connect; /* to avoid reconnecting too fast.. */
 
 unsigned int autoconnect:1;
+unsigned int no_proxy:1;
 unsigned int last_failed:1; /* if last connection attempt failed */
 unsigned int banned:1; /* if we're banned from this server */
 unsigned int dns_error:1; /* DNS said the host doesn't exist */
 unsigned int last_failed:1; /* if last connection attempt failed */
 unsigned int banned:1; /* if we're banned from this server */
 unsigned int dns_error:1; /* DNS said the host doesn't exist */
index a9491f92ba4be3411a2407ee6e2f5f5c6a14e726..e51a3fa23f03393d6a46a05ada427c9c804307d9 100644 (file)
@@ -37,6 +37,9 @@ static int reconnect_time;
 
 void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server)
 {
 
 void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server)
 {
+        g_free_not_null(conn->tag);
+       conn->tag = g_strdup(server->tag);
+
        g_free_not_null(conn->away_reason);
        conn->away_reason = !server->usermode_away ? NULL :
                g_strdup(server->away_reason);
        g_free_not_null(conn->away_reason);
        conn->away_reason = !server->usermode_away ? NULL :
                g_strdup(server->away_reason);
@@ -53,20 +56,22 @@ static void server_reconnect_add(SERVER_CONNECT_REC *conn,
 
        rec = g_new(RECONNECT_REC, 1);
        rec->tag = ++last_reconnect_tag;
 
        rec = g_new(RECONNECT_REC, 1);
        rec->tag = ++last_reconnect_tag;
-       rec->conn = conn;
        rec->next_connect = next_connect;
 
        rec->next_connect = next_connect;
 
+       rec->conn = conn;
+       server_connect_ref(conn);
+
        reconnects = g_slist_append(reconnects, rec);
 }
 
        reconnects = g_slist_append(reconnects, rec);
 }
 
-void server_reconnect_destroy(RECONNECT_REC *rec, int free_conn)
+void server_reconnect_destroy(RECONNECT_REC *rec)
 {
        g_return_if_fail(rec != NULL);
 
        reconnects = g_slist_remove(reconnects, rec);
 
        signal_emit("server reconnect remove", 1, rec);
 {
        g_return_if_fail(rec != NULL);
 
        reconnects = g_slist_remove(reconnects, rec);
 
        signal_emit("server reconnect remove", 1, rec);
-       if (free_conn) server_connect_free(rec->conn);
+       server_connect_unref(rec->conn);
        g_free(rec);
 
        if (reconnects == NULL)
        g_free(rec);
 
        if (reconnects == NULL)
@@ -92,8 +97,10 @@ static int server_reconnect_timeout(void)
 
                if (rec->next_connect <= now) {
                        conn = rec->conn;
 
                if (rec->next_connect <= now) {
                        conn = rec->conn;
-                       server_reconnect_destroy(rec, FALSE);
+                       server_connect_ref(conn);
+                       server_reconnect_destroy(rec);
                        CHAT_PROTOCOL(conn)->server_connect(conn);
                        CHAT_PROTOCOL(conn)->server_connect(conn);
+                       server_connect_unref(conn);
                }
        }
 
                }
        }
 
@@ -108,13 +115,8 @@ static void sserver_connect(SERVER_SETUP_REC *rec, SERVER_CONNECT_REC *conn)
        if (conn->port == 0) conn->port = rec->port;
 
        server_setup_fill_reconn(conn, rec);
        if (conn->port == 0) conn->port = rec->port;
 
        server_setup_fill_reconn(conn, rec);
-       if (rec->last_connect > time(NULL)-reconnect_time) {
-               /* can't reconnect this fast, wait.. */
-               server_reconnect_add(conn, rec->last_connect+reconnect_time);
-       } else {
-               /* connect to server.. */
-               CHAT_PROTOCOL(conn)->server_connect(conn);
-       }
+       server_reconnect_add(conn, rec->last_connect+reconnect_time);
+       server_connect_unref(conn);
 }
 
 static SERVER_CONNECT_REC *
 }
 
 static SERVER_CONNECT_REC *
@@ -126,13 +128,17 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
        signal_emit("server connect copy", 2, &dest, src);
        g_return_val_if_fail(dest != NULL, NULL);
 
        signal_emit("server connect copy", 2, &dest, src);
        g_return_val_if_fail(dest != NULL, NULL);
 
+        server_connect_ref(dest);
        dest->type = module_get_uniq_id("SERVER CONNECT", 0);
        dest->reconnection = src->reconnection;
        dest->proxy = g_strdup(src->proxy);
         dest->proxy_port = src->proxy_port;
        dest->proxy_string = g_strdup(src->proxy_string);
        dest->type = module_get_uniq_id("SERVER CONNECT", 0);
        dest->reconnection = src->reconnection;
        dest->proxy = g_strdup(src->proxy);
         dest->proxy_port = src->proxy_port;
        dest->proxy_string = g_strdup(src->proxy_string);
+       dest->proxy_string_after = g_strdup(src->proxy_string_after);
        dest->proxy_password = g_strdup(src->proxy_password);
 
        dest->proxy_password = g_strdup(src->proxy_password);
 
+       dest->tag = g_strdup(src->tag);
+
        if (connect_info) {
                 dest->family = src->family;
                dest->address = g_strdup(src->address);
        if (connect_info) {
                 dest->family = src->family;
                dest->address = g_strdup(src->address);
@@ -161,8 +167,9 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
 }
 
 #define server_should_reconnect(server) \
 }
 
 #define server_should_reconnect(server) \
-       ((server)->connection_lost && ((server)->connrec->chatnet != NULL || \
-                               (!(server)->banned && !(server)->dns_error)))
+       ((server)->connection_lost && !(server)->no_reconnect && \
+       ((server)->connrec->chatnet != NULL || \
+               (!(server)->banned && !(server)->dns_error)))
 
 #define sserver_connect_ok(rec, net) \
        (!(rec)->banned && !(rec)->dns_error && (rec)->chatnet != NULL && \
 
 #define sserver_connect_ok(rec, net) \
        (!(rec)->banned && !(rec)->dns_error && (rec)->chatnet != NULL && \
@@ -192,7 +199,8 @@ static void sig_reconnect(SERVER_REC *server)
        }
 
        sserver = server_setup_find(server->connrec->address,
        }
 
        sserver = server_setup_find(server->connrec->address,
-                                   server->connrec->port);
+                                   server->connrec->port,
+                                   server->connrec->chatnet);
 
        if (sserver != NULL) {
                /* save the last connection time/status */
 
        if (sserver != NULL) {
                /* save the last connection time/status */
@@ -210,21 +218,14 @@ static void sig_reconnect(SERVER_REC *server)
                conn->port = server->connrec->port;
                conn->password = g_strdup(server->connrec->password);
 
                conn->port = server->connrec->port;
                conn->password = g_strdup(server->connrec->password);
 
-               if (server->connect_time != 0 &&
-                   time(NULL)-server->connect_time > reconnect_time) {
-                       /* there's been enough time since last connection,
-                          reconnect back immediately */
-                       CHAT_PROTOCOL(conn)->server_connect(conn);
-               } else {
-                       /* reconnect later.. */
-                       server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) :
-                                                   server->connect_time) + reconnect_time);
-               }
+               server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) :
+                                           server->connect_time) + reconnect_time);
+               server_connect_unref(conn);
                return;
        }
 
        /* always try to first connect to the first on the list where we
                return;
        }
 
        /* always try to first connect to the first on the list where we
-          haven't got unsuccessful connection attempts for the last half
+          haven't got unsuccessful connection attempts for the past half
           an hour. */
 
        now = time(NULL);
           an hour. */
 
        now = time(NULL);
@@ -264,7 +265,7 @@ static void sig_reconnect(SERVER_REC *server)
                if (through) {
                        /* shouldn't happen unless there's no servers in
                           this chatnet in setup.. */
                if (through) {
                        /* shouldn't happen unless there's no servers in
                           this chatnet in setup.. */
-                        server_connect_free(conn);
+                       server_connect_unref(conn);
                        break;
                }
 
                        break;
                }
 
@@ -288,7 +289,7 @@ static void sig_connected(SERVER_REC *server)
 static void cmd_rmreconns(void)
 {
        while (reconnects != NULL)
 static void cmd_rmreconns(void)
 {
        while (reconnects != NULL)
-               server_reconnect_destroy(reconnects->data, TRUE);
+               server_reconnect_destroy(reconnects->data);
 }
 
 static RECONNECT_REC *reconnect_find_tag(int tag)
 }
 
 static RECONNECT_REC *reconnect_find_tag(int tag)
@@ -319,7 +320,8 @@ static void reconnect_all(void)
                rec = reconnects->data;
 
                list = g_slist_append(list, rec->conn);
                rec = reconnects->data;
 
                list = g_slist_append(list, rec->conn);
-               server_reconnect_destroy(rec, FALSE);
+                server_connect_ref(rec->conn);
+               server_reconnect_destroy(rec);
        }
 
 
        }
 
 
@@ -327,6 +329,7 @@ static void reconnect_all(void)
                conn = list->data;
 
                CHAT_PROTOCOL(conn)->server_connect(conn);
                conn = list->data;
 
                CHAT_PROTOCOL(conn)->server_connect(conn);
+                server_connect_unref(conn);
                 list = g_slist_remove(list, conn);
        }
 }
                 list = g_slist_remove(list, conn);
        }
 }
@@ -342,14 +345,13 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
                /* reconnect back to same server */
                conn = server_connect_copy_skeleton(server->connrec, TRUE);
 
                /* reconnect back to same server */
                conn = server_connect_copy_skeleton(server->connrec, TRUE);
 
-               if (server->connected) {
+               if (server->connected)
                        reconnect_save_status(conn, server);
                        reconnect_save_status(conn, server);
-                       signal_emit("command disconnect", 2,
-                                   "* Reconnecting", server);
-               }
+               signal_emit("command disconnect", 2, "* Reconnecting", server);
 
                conn->reconnection = TRUE;
                CHAT_PROTOCOL(conn)->server_connect(conn);
 
                conn->reconnection = TRUE;
                CHAT_PROTOCOL(conn)->server_connect(conn);
+               server_connect_unref(conn);
                 return;
        }
 
                 return;
        }
 
@@ -378,25 +380,25 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
        }
 
        conn = rec->conn;
        }
 
        conn = rec->conn;
-       server_reconnect_destroy(rec, FALSE);
+       server_connect_ref(conn);
+       server_reconnect_destroy(rec);
        CHAT_PROTOCOL(conn)->server_connect(conn);
        CHAT_PROTOCOL(conn)->server_connect(conn);
+       server_connect_unref(conn);
 }
 
 static void cmd_disconnect(const char *data, SERVER_REC *server)
 {
        RECONNECT_REC *rec;
 }
 
 static void cmd_disconnect(const char *data, SERVER_REC *server)
 {
        RECONNECT_REC *rec;
-       int tag;
 
        if (g_strncasecmp(data, "RECON-", 6) != 0)
                return; /* handle only reconnection removing */
 
 
        if (g_strncasecmp(data, "RECON-", 6) != 0)
                return; /* handle only reconnection removing */
 
-       rec = sscanf(data+6, "%d", &tag) == 1 && tag > 0 ?
-               reconnect_find_tag(tag) : NULL;
+       rec = reconnect_find_tag(atoi(data+6));
 
        if (rec == NULL)
                signal_emit("server reconnect not found", 1, data);
        else
 
        if (rec == NULL)
                signal_emit("server reconnect not found", 1, data);
        else
-               server_reconnect_destroy(rec, TRUE);
+               server_reconnect_destroy(rec);
        signal_stop();
 }
 
        signal_stop();
 }
 
@@ -409,7 +411,7 @@ static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto)
 
                 next = tmp->next;
                 if (rec->conn->chat_type == proto->id)
 
                 next = tmp->next;
                 if (rec->conn->chat_type == proto->id)
-                       server_reconnect_destroy(rec, TRUE);
+                       server_reconnect_destroy(rec);
        }
 }
 
        }
 }
 
index b51486f60513112c798ea538d6175d2a63a6f6f7..835d58d5f4593d707faac7aae2dafe962266af47 100644 (file)
@@ -6,7 +6,7 @@
 #define FAILED_RECONNECT_WAIT (60*30)
 
 typedef struct {
 #define FAILED_RECONNECT_WAIT (60*30)
 
 typedef struct {
-       int tag;
+        int tag;
        time_t next_connect;
 
        SERVER_CONNECT_REC *conn;
        time_t next_connect;
 
        SERVER_CONNECT_REC *conn;
@@ -15,7 +15,7 @@ typedef struct {
 extern GSList *reconnects;
 
 void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server);
 extern GSList *reconnects;
 
 void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server);
-void server_reconnect_destroy(RECONNECT_REC *rec, int free_conn);
+void server_reconnect_destroy(RECONNECT_REC *rec);
 
 void servers_reconnect_init(void);
 void servers_reconnect_deinit(void);
 
 void servers_reconnect_init(void);
 void servers_reconnect_deinit(void);
diff --git a/apps/irssi/src/core/servers-redirect.c b/apps/irssi/src/core/servers-redirect.c
deleted file mode 100644 (file)
index ca340fc..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- server-redirect.c : irssi
-
-    Copyright (C) 1999-2000 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#include "module.h"
-#include "signals.h"
-#include "misc.h"
-
-#include "servers.h"
-#include "servers-redirect.h"
-
-static int redirect_group;
-
-static void server_eventtable_destroy(char *key, GSList *value)
-{
-       GSList *tmp;
-
-       g_free(key);
-
-       for (tmp = value; tmp != NULL; tmp = tmp->next) {
-               REDIRECT_REC *rec = tmp->data;
-
-               g_free_not_null(rec->arg);
-               g_free(rec->name);
-               g_free(rec);
-       }
-       g_slist_free(value);
-}
-
-static void server_eventgrouptable_destroy(gpointer key, GSList *value)
-{
-       g_slist_foreach(value, (GFunc) g_free, NULL);
-       g_slist_free(value);
-}
-
-static void server_cmdtable_destroy(char *key, REDIRECT_CMD_REC *value)
-{
-       g_free(key);
-
-       g_slist_foreach(value->events, (GFunc) g_free, NULL);
-       g_slist_free(value->events);
-       g_free(value);
-}
-
-static void sig_disconnected(SERVER_REC *server)
-{
-       g_return_if_fail(IS_SERVER(server));
-
-       if (server->eventtable != NULL) {
-               g_hash_table_foreach(server->eventtable,
-                                    (GHFunc) server_eventtable_destroy, NULL);
-               g_hash_table_destroy(server->eventtable);
-       }
-
-       g_hash_table_foreach(server->eventgrouptable,
-                            (GHFunc) server_eventgrouptable_destroy, NULL);
-       g_hash_table_destroy(server->eventgrouptable);
-
-       if (server->cmdtable != NULL) {
-               g_hash_table_foreach(server->cmdtable,
-                                    (GHFunc) server_cmdtable_destroy, NULL);
-               g_hash_table_destroy(server->cmdtable);
-       }
-}
-
-void server_redirect_initv(SERVER_REC *server, const char *command,
-                          int last, GSList *list)
-{
-       REDIRECT_CMD_REC *rec;
-
-       g_return_if_fail(IS_SERVER(server));
-       g_return_if_fail(command != NULL);
-       g_return_if_fail(last > 0);
-
-       if (g_hash_table_lookup(server->cmdtable, command) != NULL) {
-               /* already in hash table. list of events SHOULD be the same. */
-               g_slist_foreach(list, (GFunc) g_free, NULL);
-               g_slist_free(list);
-               return;
-       }
-
-       rec = g_new(REDIRECT_CMD_REC, 1);
-       rec->last = last;
-       rec->events = list;
-       g_hash_table_insert(server->cmdtable, g_strdup(command), rec);
-}
-
-void server_redirect_init(SERVER_REC *server, const char *command,
-                         int last, ...)
-{
-       va_list args;
-       GSList *list;
-       char *event;
-
-       va_start(args, last);
-       list = NULL;
-       while ((event = va_arg(args, gchar *)) != NULL)
-               list = g_slist_append(list, g_strdup(event));
-       va_end(args);
-
-       server_redirect_initv(server, command, last, list);
-}
-
-int server_redirect_single_event(SERVER_REC *server, const char *arg,
-                                int last, int group, const char *event,
-                                const char *signal, int argpos)
-{
-       REDIRECT_REC *rec;
-       GSList *list, *grouplist;
-       char *origkey;
-
-       g_return_val_if_fail(IS_SERVER(server), 0);
-       g_return_val_if_fail(event != NULL, 0);
-       g_return_val_if_fail(signal != NULL, 0);
-       g_return_val_if_fail(arg != NULL || argpos == -1, 0);
-
-       if (group == 0) group = ++redirect_group;
-
-       rec = g_new0(REDIRECT_REC, 1);
-       rec->arg = arg == NULL ? NULL : g_strdup(arg);
-       rec->argpos = argpos;
-       rec->name = g_strdup(signal);
-       rec->group = group;
-       rec->last = last;
-
-       if (g_hash_table_lookup_extended(server->eventtable, event,
-                                        (gpointer *) &origkey,
-                                        (gpointer *) &list)) {
-               g_hash_table_remove(server->eventtable, origkey);
-       } else {
-               list = NULL;
-               origkey = g_strdup(event);
-       }
-
-       grouplist = g_hash_table_lookup(server->eventgrouptable,
-                                       GINT_TO_POINTER(group));
-       if (grouplist != NULL) {
-               g_hash_table_remove(server->eventgrouptable,
-                                   GINT_TO_POINTER(group));
-       }
-
-       list = g_slist_append(list, rec);
-       grouplist = g_slist_append(grouplist, g_strdup(event));
-
-       g_hash_table_insert(server->eventtable, origkey, list);
-       g_hash_table_insert(server->eventgrouptable,
-                           GINT_TO_POINTER(group), grouplist);
-
-       return group;
-}
-
-void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...)
-{
-       va_list args;
-       char *event, *signal;
-       int argpos, group;
-
-       g_return_if_fail(IS_SERVER(server));
-
-       va_start(args, last);
-
-       group = 0;
-       while ((event = va_arg(args, gchar *)) != NULL) {
-               signal = va_arg(args, gchar *);
-               argpos = va_arg(args, gint);
-
-               group = server_redirect_single_event(server, arg, last > 0,
-                                                    group, event, signal,
-                                                    argpos);
-               last--;
-       }
-
-       va_end(args);
-}
-
-void server_redirect_default(SERVER_REC *server, const char *command)
-{
-       REDIRECT_CMD_REC *cmdrec;
-       REDIRECT_REC *rec;
-       GSList *events, *list, *grouplist;
-       char *event, *origkey;
-       int last;
-
-       g_return_if_fail(IS_SERVER(server));
-       g_return_if_fail(command != NULL);
-
-       if (server->cmdtable == NULL)
-               return; /* not connected yet */
-
-       cmdrec = g_hash_table_lookup(server->cmdtable, command);
-       if (cmdrec == NULL) return;
-
-       /* add all events used by command to eventtable and eventgrouptable */
-       redirect_group++; grouplist = NULL; last = cmdrec->last;
-       for (events = cmdrec->events; events != NULL; events = events->next) {
-               event = events->data;
-
-               if (g_hash_table_lookup_extended(server->eventtable, event,
-                                                (gpointer *) &origkey,
-                                                (gpointer *) &list)) {
-                       g_hash_table_remove(server->eventtable, origkey);
-               } else {
-                       list = NULL;
-                       origkey = g_strdup(event);
-               }
-
-               rec = g_new0(REDIRECT_REC, 1);
-               rec->argpos = -1;
-               rec->name = g_strdup(event);
-               rec->group = redirect_group;
-               rec->last = last > 0;
-
-               grouplist = g_slist_append(grouplist, g_strdup(event));
-               list = g_slist_append(list, rec);
-               g_hash_table_insert(server->eventtable, origkey, list);
-
-               last--;
-       }
-
-       g_hash_table_insert(server->eventgrouptable,
-                           GINT_TO_POINTER(redirect_group), grouplist);
-}
-
-void server_redirect_remove_next(SERVER_REC *server, const char *event,
-                                GSList *item)
-{
-       REDIRECT_REC *rec;
-       GSList *grouplist, *list, *events, *tmp;
-       char *origkey;
-       int group;
-
-       g_return_if_fail(IS_SERVER(server));
-       g_return_if_fail(event != NULL);
-
-       if (!g_hash_table_lookup_extended(server->eventtable, event,
-                                         (gpointer *) &origkey,
-                                         (gpointer *) &list))
-               return;
-
-       rec = item == NULL ? list->data : item->data;
-       if (!rec->last) {
-               /* this wasn't last expected event */
-               return;
-       }
-       group = rec->group;
-
-       /* get list of events from this group */
-       grouplist = g_hash_table_lookup(server->eventgrouptable,
-                                       GINT_TO_POINTER(group));
-
-       /* remove all of them */
-       for (list = grouplist; list != NULL; list = list->next) {
-               char *event = list->data;
-
-               if (!g_hash_table_lookup_extended(server->eventtable, event,
-                                                 (gpointer *) &origkey,
-                                                 (gpointer *) &events)) {
-                       g_warning("server_redirect_remove_next() : "
-                                 "event in eventgrouptable but not in "
-                                 "eventtable");
-                       continue;
-               }
-
-               /* remove the right group */
-               for (tmp = events; tmp != NULL; tmp = tmp->next) {
-                       rec = tmp->data;
-
-                       if (rec->group == group)
-                               break;
-               }
-
-               if (rec == NULL) {
-                       g_warning("server_redirect_remove_next() : "
-                                 "event in eventgrouptable but not in "
-                                 "eventtable (group)");
-                       continue;
-               }
-
-               g_free(event);
-
-               events = g_slist_remove(events, rec);
-               g_free_not_null(rec->arg);
-               g_free(rec->name);
-               g_free(rec);
-
-               /* update hash table */
-               g_hash_table_remove(server->eventtable, origkey);
-               if (events == NULL)
-                       g_free(origkey);
-               else {
-                       g_hash_table_insert(server->eventtable,
-                                           origkey, events);
-               }
-       }
-
-       g_hash_table_remove(server->eventgrouptable, GINT_TO_POINTER(group));
-       g_slist_free(grouplist);
-}
-
-GSList *server_redirect_getqueue(SERVER_REC *server, const char *event,
-                                const char *args)
-{
-       REDIRECT_REC *rec;
-       GSList *list;
-       char **arglist;
-       int found;
-
-       g_return_val_if_fail(IS_SERVER(server), NULL);
-       g_return_val_if_fail(event != NULL, NULL);
-
-       list = g_hash_table_lookup(server->eventtable, event);
-
-       for (; list != NULL; list = list->next) {
-               rec = list->data;
-               if (rec->argpos == -1)
-                       break;
-
-               if (rec->arg == NULL || args == NULL)
-                       continue;
-
-               /* we need to check that the argument is right.. */
-               arglist = g_strsplit(args, " ", -1);
-               found = (strarray_length(arglist) > rec->argpos &&
-                        find_substr(rec->arg, arglist[rec->argpos]));
-               g_strfreev(arglist);
-
-               if (found) break;
-       }
-
-       return list;
-}
-
-void servers_redirect_init(void)
-{
-       redirect_group = 0;
-
-       signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
-}
-
-void servers_redirect_deinit(void)
-{
-       signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
-}
diff --git a/apps/irssi/src/core/servers-redirect.h b/apps/irssi/src/core/servers-redirect.h
deleted file mode 100644 (file)
index c9e45ce..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef __SERVERS_REDIRECT_H
-#define __SERVERS_REDIRECT_H
-
-typedef struct {
-       int last; /* number of "last" events at the start of the events list */
-       GSList *events; /* char* list of events */
-} REDIRECT_CMD_REC;
-
-typedef struct {
-       char *name; /* event name */
-
-       char *arg; /* argument for event we are expecting or NULL */
-       int argpos; /* argument position */
-
-       int group; /* group of events this belongs to */
-       int last; /* if this event is received, remove all the events in this group */
-}
-REDIRECT_REC;
-
-void server_redirect_init(SERVER_REC *server, const char *command, int last, ...);
-void server_redirect_initv(SERVER_REC *server, const char *command, int last, GSList *list);
-/* ... = char *event1, char *event2, ..., NULL */
-
-void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...);
-/* ... = char *event, char *callback_signal, int argpos, ..., NULL */
-
-int server_redirect_single_event(SERVER_REC *server, const char *arg, int last, int group,
-                                const char *event, const char *signal, int argpos);
-void server_redirect_default(SERVER_REC *server, const char *command);
-void server_redirect_remove_next(SERVER_REC *server, const char *event, GSList *item);
-GSList *server_redirect_getqueue(SERVER_REC *server, const char *event, const char *args);
-
-void servers_redirect_init(void);
-void servers_redirect_deinit(void);
-
-#endif
index deaf3cc941d983ba457fc859ef9558bf5103f22a..99f9f57993d395fee53e79fac182c01b7961033f 100644 (file)
@@ -131,6 +131,7 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
                conn->proxy = g_strdup(settings_get_str("proxy_address"));
                conn->proxy_port = settings_get_int("proxy_port");
                conn->proxy_string = g_strdup(settings_get_str("proxy_string"));
                conn->proxy = g_strdup(settings_get_str("proxy_address"));
                conn->proxy_port = settings_get_int("proxy_port");
                conn->proxy_string = g_strdup(settings_get_str("proxy_string"));
+               conn->proxy_string_after = g_strdup(settings_get_str("proxy_string_after"));
                conn->proxy_password = g_strdup(settings_get_str("proxy_password"));
        }
 
                conn->proxy_password = g_strdup(settings_get_str("proxy_password"));
        }
 
@@ -143,6 +144,8 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
                conn->own_ip6 = g_new(IPADDR, 1);
                memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR));
        }
                conn->own_ip6 = g_new(IPADDR, 1);
                memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR));
        }
+
+       signal_emit("server setup fill connect", 1, conn);
 }
 
 static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
 }
 
 static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
@@ -153,6 +156,9 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
 
        sserver->last_connect = time(NULL);
 
 
        sserver->last_connect = time(NULL);
 
+        if (sserver->no_proxy)
+               g_free_and_null(conn->proxy);
+
        if (sserver->family != 0 && conn->family == 0)
                 conn->family = sserver->family;
        if (sserver->port > 0 && conn->port <= 0)
        if (sserver->family != 0 && conn->family == 0)
                 conn->family = sserver->family;
        if (sserver->port > 0 && conn->port <= 0)
@@ -168,15 +174,15 @@ static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn,
        g_return_if_fail(IS_SERVER_CONNECT(conn));
        g_return_if_fail(IS_CHATNET(chatnet));
 
        g_return_if_fail(IS_SERVER_CONNECT(conn));
        g_return_if_fail(IS_CHATNET(chatnet));
 
-       if (chatnet->nick) {
+       if (chatnet->nick != NULL) {
                g_free(conn->nick);
                conn->nick = g_strdup(chatnet->nick);;
        }
                g_free(conn->nick);
                conn->nick = g_strdup(chatnet->nick);;
        }
-       if (chatnet->username) {
+       if (chatnet->username != NULL) {
                 g_free(conn->username);
                conn->username = g_strdup(chatnet->username);;
        }
                 g_free(conn->username);
                conn->username = g_strdup(chatnet->username);;
        }
-       if (chatnet->realname) {
+       if (chatnet->realname != NULL) {
                 g_free(conn->realname);
                conn->realname = g_strdup(chatnet->realname);;
        }
                 g_free(conn->realname);
                conn->realname = g_strdup(chatnet->realname);;
        }
@@ -200,7 +206,7 @@ create_addr_conn(int chat_type, const char *address, int port,
 
        g_return_val_if_fail(address != NULL, NULL);
 
 
        g_return_val_if_fail(address != NULL, NULL);
 
-       sserver = server_setup_find(address, port);
+       sserver = server_setup_find(address, port, chatnet);
        if (sserver != NULL) {
                if (chat_type < 0)
                        chat_type = sserver->chat_type;
        if (sserver != NULL) {
                if (chat_type < 0)
                        chat_type = sserver->chat_type;
@@ -212,6 +218,8 @@ create_addr_conn(int chat_type, const char *address, int port,
                 chat_protocol_get_default();
 
        conn = proto->create_server_connect();
                 chat_protocol_get_default();
 
        conn = proto->create_server_connect();
+       server_connect_ref(conn);
+
        conn->chat_type = proto->id;
         if (chatnet != NULL && *chatnet != '\0')
                conn->chatnet = g_strdup(chatnet);
        conn->chat_type = proto->id;
         if (chatnet != NULL && *chatnet != '\0')
                conn->chatnet = g_strdup(chatnet);
@@ -240,7 +248,6 @@ create_addr_conn(int chat_type, const char *address, int port,
                conn->nick = g_strdup(nick);
        }
 
                conn->nick = g_strdup(nick);
        }
 
-       signal_emit("server setup fill connect", 1, conn);
        return conn;
 }
 
        return conn;
 }
 
@@ -288,22 +295,29 @@ server_create_conn(int chat_type, const char *dest, int port,
                   const char *nick)
 {
        SERVER_CONNECT_REC *rec;
                   const char *nick)
 {
        SERVER_CONNECT_REC *rec;
+        CHATNET_REC *chatrec;
 
        g_return_val_if_fail(dest != NULL, NULL);
 
 
        g_return_val_if_fail(dest != NULL, NULL);
 
-       if (chatnet_find(dest) != NULL) {
-               rec = create_chatnet_conn(dest, port, password, nick);
+        chatrec = chatnet_find(dest);
+       if (chatrec != NULL) {
+               rec = create_chatnet_conn(chatrec->name, port, password, nick);
                if (rec != NULL)
                        return rec;
        }
 
                if (rec != NULL)
                        return rec;
        }
 
+       chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
+       if (chatrec != NULL)
+               chatnet = chatrec->name;
+
        return create_addr_conn(chat_type, dest, port,
                                chatnet, password, nick);
 }
 
 /* Find matching server from setup. Try to find record with a same port,
    but fallback to any server with the same address. */
        return create_addr_conn(chat_type, dest, port,
                                chatnet, password, nick);
 }
 
 /* Find matching server from setup. Try to find record with a same port,
    but fallback to any server with the same address. */
-SERVER_SETUP_REC *server_setup_find(const char *address, int port)
+SERVER_SETUP_REC *server_setup_find(const char *address, int port,
+                                   const char *chatnet)
 {
        SERVER_SETUP_REC *server;
        GSList *tmp;
 {
        SERVER_SETUP_REC *server;
        GSList *tmp;
@@ -314,7 +328,9 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port)
        for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
                SERVER_SETUP_REC *rec = tmp->data;
 
        for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
                SERVER_SETUP_REC *rec = tmp->data;
 
-               if (g_strcasecmp(rec->address, address) == 0) {
+               if (g_strcasecmp(rec->address, address) == 0 &&
+                   (chatnet == NULL || rec->chatnet == NULL ||
+                    g_strcasecmp(rec->chatnet, chatnet) == 0)) {
                        server = rec;
                        if (rec->port == port)
                                break;
                        server = rec;
                        if (rec->port == port)
                                break;
@@ -329,7 +345,7 @@ SERVER_SETUP_REC *server_setup_find_port(const char *address, int port)
 {
        SERVER_SETUP_REC *rec;
 
 {
        SERVER_SETUP_REC *rec;
 
-       rec = server_setup_find(address, port);
+       rec = server_setup_find(address, port, NULL);
        return rec == NULL || rec->port != port ? NULL : rec;
 }
 
        return rec == NULL || rec->port != port ? NULL : rec;
 }
 
@@ -355,14 +371,6 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
 
        rec = NULL;
        chatnet = config_node_get_str(node, "chatnet", NULL);
 
        rec = NULL;
        chatnet = config_node_get_str(node, "chatnet", NULL);
-       if (chatnet == NULL) /* FIXME: remove this after .98... */ {
-               chatnet = config_node_get_str(node, "ircnet", NULL);
-               if (chatnet != NULL) {
-                        iconfig_node_set_str(node, "chatnet", chatnet);
-                        iconfig_node_set_str(node, "ircnet", NULL);
-                       chatnet = config_node_get_str(node, "chatnet", NULL);
-               }
-       }
 
        chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
        if (chatnetrec == NULL && chatnet != NULL) {
 
        chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
        if (chatnetrec == NULL && chatnet != NULL) {
@@ -385,6 +393,7 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
        rec->password = g_strdup(config_node_get_str(node, "password", NULL));
        rec->port = port;
        rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
        rec->password = g_strdup(config_node_get_str(node, "password", NULL));
        rec->port = port;
        rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
+       rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
        rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL));
 
        signal_emit("server setup read", 2, rec, node);
        rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL));
 
        signal_emit("server setup read", 2, rec, node);
@@ -419,6 +428,8 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
 
        if (rec->autoconnect)
                iconfig_node_set_bool(node, "autoconnect", TRUE);
 
        if (rec->autoconnect)
                iconfig_node_set_bool(node, "autoconnect", TRUE);
+       if (rec->no_proxy)
+               iconfig_node_set_bool(node, "no_proxy", TRUE);
 
        signal_emit("server setup saved", 2, rec, node);
 }
 
        signal_emit("server setup saved", 2, rec, node);
 }
@@ -474,7 +485,8 @@ static void read_servers(void)
        /* Read servers */
        node = iconfig_node_traverse("servers", FALSE);
        if (node != NULL) {
        /* Read servers */
        node = iconfig_node_traverse("servers", FALSE);
        if (node != NULL) {
-               for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
                        server_setup_read(tmp->data);
        }
 }
                        server_setup_read(tmp->data);
        }
 }
@@ -503,6 +515,7 @@ void servers_setup_init(void)
        settings_add_str("proxy", "proxy_address", "");
        settings_add_int("proxy", "proxy_port", 6667);
        settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
        settings_add_str("proxy", "proxy_address", "");
        settings_add_int("proxy", "proxy_port", 6667);
        settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
+       settings_add_str("proxy", "proxy_string_after", "");
        settings_add_str("proxy", "proxy_password", "");
 
         setupservers = NULL;
        settings_add_str("proxy", "proxy_password", "");
 
         setupservers = NULL;
index d0807d111e2b39191ff39b4364fb1e32638c5086..d0101bcb63587076650f2e9d2dbde98d1d245961 100644 (file)
@@ -33,7 +33,8 @@ server_create_conn(int chat_type, const char *dest, int port,
 
 /* Find matching server from setup. Try to find record with a same port,
    but fallback to any server with the same address. */
 
 /* Find matching server from setup. Try to find record with a same port,
    but fallback to any server with the same address. */
-SERVER_SETUP_REC *server_setup_find(const char *address, int port);
+SERVER_SETUP_REC *server_setup_find(const char *address, int port,
+                                   const char *chatnet);
 /* Find matching server from setup. Ports must match or NULL is returned. */
 SERVER_SETUP_REC *server_setup_find_port(const char *address, int port);
 
 /* Find matching server from setup. Ports must match or NULL is returned. */
 SERVER_SETUP_REC *server_setup_find_port(const char *address, int port);
 
index f74e7f58906f4db6b39ff122e280ae620653265b..499f1f919f1165f5ea358f800d13e4be4c60279f 100644 (file)
@@ -31,7 +31,6 @@
 #include "chat-protocols.h"
 #include "servers.h"
 #include "servers-reconnect.h"
 #include "chat-protocols.h"
 #include "servers.h"
 #include "servers-reconnect.h"
-#include "servers-redirect.h"
 #include "servers-setup.h"
 #include "channels.h"
 #include "queries.h"
 #include "servers-setup.h"
 #include "channels.h"
 #include "queries.h"
@@ -46,23 +45,26 @@ void server_connect_failed(SERVER_REC *server, const char *msg)
        lookup_servers = g_slist_remove(lookup_servers, server);
 
        signal_emit("server connect failed", 2, server, msg);
        lookup_servers = g_slist_remove(lookup_servers, server);
 
        signal_emit("server connect failed", 2, server, msg);
-       if (server->connect_tag != -1)
+
+       if (server->connect_tag != -1) {
                g_source_remove(server->connect_tag);
                g_source_remove(server->connect_tag);
-       if (server->handle != NULL)
+               server->connect_tag = -1;
+       }
+       if (server->handle != NULL) {
                net_sendbuffer_destroy(server->handle, TRUE);
                net_sendbuffer_destroy(server->handle, TRUE);
+               server->handle = NULL;
+       }
 
        if (server->connect_pipe[0] != NULL) {
                g_io_channel_close(server->connect_pipe[0]);
                g_io_channel_unref(server->connect_pipe[0]);
                g_io_channel_close(server->connect_pipe[1]);
                g_io_channel_unref(server->connect_pipe[1]);
 
        if (server->connect_pipe[0] != NULL) {
                g_io_channel_close(server->connect_pipe[0]);
                g_io_channel_unref(server->connect_pipe[0]);
                g_io_channel_close(server->connect_pipe[1]);
                g_io_channel_unref(server->connect_pipe[1]);
+               server->connect_pipe[0] = NULL;
+               server->connect_pipe[1] = NULL;
        }
 
        }
 
-       MODULE_DATA_DEINIT(server);
-       server_connect_free(server->connrec);
-       g_free_not_null(server->nick);
-       g_free(server->tag);
-       g_free(server);
+       server_unref(server);
 }
 
 /* generate tag from server's address */
 }
 
 /* generate tag from server's address */
@@ -101,12 +103,24 @@ static char *server_create_tag(SERVER_CONNECT_REC *conn)
        char *tag;
        int num;
 
        char *tag;
        int num;
 
-        g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL);
+       g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL);
 
        tag = conn->chatnet != NULL && *conn->chatnet != '\0' ?
                g_strdup(conn->chatnet) :
                server_create_address_tag(conn->address);
 
 
        tag = conn->chatnet != NULL && *conn->chatnet != '\0' ?
                g_strdup(conn->chatnet) :
                server_create_address_tag(conn->address);
 
+       if (conn->tag != NULL && server_find_tag(conn->tag) == NULL &&
+           strncmp(conn->tag, tag, strlen(tag)) == 0) {
+               /* use the existing tag if it begins with the same ID -
+                  this is useful when you have several connections to
+                  same server and you want to keep the same tags with
+                  the servers (or it would cause problems when rejoining
+                  /LAYOUT SAVEd channels). */
+               g_free(tag);
+               return g_strdup(conn->tag);
+       }
+
+
        /* then just append numbers after tag until unused is found.. */
        str = g_string_new(tag);
        for (num = 2; server_find_tag(str->str) != NULL; num++)
        /* then just append numbers after tag until unused is found.. */
        str = g_string_new(tag);
        for (num = 2; server_find_tag(str->str) != NULL; num++)
@@ -122,11 +136,6 @@ static char *server_create_tag(SERVER_CONNECT_REC *conn)
 void server_connect_finished(SERVER_REC *server)
 {
        server->connect_time = time(NULL);
 void server_connect_finished(SERVER_REC *server)
 {
        server->connect_time = time(NULL);
-       server->rawlog = rawlog_create();
-
-       server->eventtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
-       server->eventgrouptable = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
-       server->cmdtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
 
        servers = g_slist_append(servers, server);
        signal_emit("server connected", 1, server);
 
        servers = g_slist_append(servers, server);
        signal_emit("server connected", 1, server);
@@ -189,10 +198,15 @@ static void server_connect_callback_readpipe(SERVER_REC *server)
        own_ip = ip == NULL ? NULL :
                (IPADDR_IS_V6(ip) ? conn->own_ip6 : conn->own_ip4);
 
        own_ip = ip == NULL ? NULL :
                (IPADDR_IS_V6(ip) ? conn->own_ip6 : conn->own_ip4);
 
-       if (ip != NULL)
+       handle = NULL;
+       if (ip != NULL) {
                signal_emit("server connecting", 2, server, ip);
                signal_emit("server connecting", 2, server, ip);
+                if (server->handle == NULL)
+                       handle = net_connect_ip(ip, port, own_ip);
+               else
+                        handle = net_sendbuffer_handle(server->handle);
+       }
 
 
-       handle = ip == NULL ? NULL : net_connect_ip(ip, port, own_ip);
        if (handle == NULL) {
                /* failed */
                if (iprec.error != 0 && net_hosterror_notfound(iprec.error)) {
        if (handle == NULL) {
                /* failed */
                if (iprec.error != 0 && net_hosterror_notfound(iprec.error)) {
@@ -215,7 +229,8 @@ static void server_connect_callback_readpipe(SERVER_REC *server)
                return;
        }
 
                return;
        }
 
-       server->handle = net_sendbuffer_create(handle, 0);
+        if (server->handle == NULL)
+               server->handle = net_sendbuffer_create(handle, 0);
        server->connect_tag =
                g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
                            (GInputFunction) server_connect_callback_init,
        server->connect_tag =
                g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
                            (GInputFunction) server_connect_callback_init,
@@ -229,6 +244,7 @@ void server_connect_init(SERVER_REC *server)
 
        MODULE_DATA_INIT(server);
        server->type = module_get_uniq_id("SERVER", 0);
 
        MODULE_DATA_INIT(server);
        server->type = module_get_uniq_id("SERVER", 0);
+       server_ref(server);
 
        server->nick = g_strdup(server->connrec->nick);
        if (server->connrec->username == NULL || *server->connrec->username == '\0') {
 
        server->nick = g_strdup(server->connrec->nick);
        if (server->connrec->username == NULL || *server->connrec->username == '\0') {
@@ -279,6 +295,7 @@ int server_start_connect(SERVER_REC *server)
                g_input_add(server->connect_pipe[0], G_INPUT_READ,
                            (GInputFunction) server_connect_callback_readpipe,
                            server);
                g_input_add(server->connect_pipe[0], G_INPUT_READ,
                            (GInputFunction) server_connect_callback_readpipe,
                            server);
+       server->rawlog = rawlog_create();
 
        lookup_servers = g_slist_append(lookup_servers, server);
 
 
        lookup_servers = g_slist_append(lookup_servers, server);
 
@@ -317,6 +334,9 @@ void server_disconnect(SERVER_REC *server)
 
        g_return_if_fail(IS_SERVER(server));
 
 
        g_return_if_fail(IS_SERVER(server));
 
+       if (server->disconnected)
+               return;
+
        if (server->connect_tag != -1) {
                /* still connecting to server.. */
                if (server->connect_pid != -1)
        if (server->connect_tag != -1) {
                /* still connecting to server.. */
                if (server->connect_pid != -1)
@@ -327,6 +347,7 @@ void server_disconnect(SERVER_REC *server)
 
        servers = g_slist_remove(servers, server);
 
 
        servers = g_slist_remove(servers, server);
 
+       server->disconnected = TRUE;
        signal_emit("server disconnected", 1, server);
 
        /* close all channels */
        signal_emit("server disconnected", 1, server);
 
        /* close all channels */
@@ -345,18 +366,46 @@ void server_disconnect(SERVER_REC *server)
                server->handle = NULL;
        }
 
                server->handle = NULL;
        }
 
-       if (server->readtag > 0)
+       if (server->readtag > 0) {
                g_source_remove(server->readtag);
                g_source_remove(server->readtag);
+               server->readtag = -1;
+       }
+
+       server_unref(server);
+}
+
+void server_ref(SERVER_REC *server)
+{
+       g_return_if_fail(IS_SERVER(server));
+
+       server->refcount++;
+}
+
+int server_unref(SERVER_REC *server)
+{
+       g_return_val_if_fail(IS_SERVER(server), FALSE);
+
+       if (--server->refcount > 0)
+               return TRUE;
+
+       if (g_slist_find(servers, server) != NULL) {
+               g_warning("Non-referenced server wasn't disconnected");
+               server_disconnect(server);
+               return TRUE;
+       }
 
         MODULE_DATA_DEINIT(server);
 
         MODULE_DATA_DEINIT(server);
-       server_connect_free(server->connrec);
-       rawlog_destroy(server->rawlog);
-       line_split_free(server->buffer);
-       g_free_not_null(server->version);
-       g_free_not_null(server->away_reason);
+       server_connect_unref(server->connrec);
+       if (server->rawlog != NULL) rawlog_destroy(server->rawlog);
+       if (server->buffer != NULL) line_split_free(server->buffer);
+       g_free(server->version);
+       g_free(server->away_reason);
        g_free(server->nick);
        g_free(server->tag);
        g_free(server->nick);
        g_free(server->tag);
+
+       server->type = 0;
        g_free(server);
        g_free(server);
+        return FALSE;
 }
 
 SERVER_REC *server_find_tag(const char *tag)
 }
 
 SERVER_REC *server_find_tag(const char *tag)
@@ -401,15 +450,30 @@ SERVER_REC *server_find_chatnet(const char *chatnet)
        return NULL;
 }
 
        return NULL;
 }
 
-void server_connect_free(SERVER_CONNECT_REC *conn)
+void server_connect_ref(SERVER_CONNECT_REC *conn)
+{
+        conn->refcount++;
+}
+
+void server_connect_unref(SERVER_CONNECT_REC *conn)
 {
        g_return_if_fail(IS_SERVER_CONNECT(conn));
 
 {
        g_return_if_fail(IS_SERVER_CONNECT(conn));
 
-       signal_emit("server connect free", 1, conn);
-        g_free_not_null(conn->proxy);
+       if (--conn->refcount > 0)
+               return;
+       if (conn->refcount < 0) {
+               g_warning("Connection '%s' refcount = %d",
+                         conn->tag, conn->refcount);
+       }
+
+        CHAT_PROTOCOL(conn)->destroy_server_connect(conn);
+
+       g_free_not_null(conn->proxy);
        g_free_not_null(conn->proxy_string);
        g_free_not_null(conn->proxy_string);
+       g_free_not_null(conn->proxy_string_after);
        g_free_not_null(conn->proxy_password);
 
        g_free_not_null(conn->proxy_password);
 
+       g_free_not_null(conn->tag);
        g_free_not_null(conn->address);
        g_free_not_null(conn->chatnet);
 
        g_free_not_null(conn->address);
        g_free_not_null(conn->chatnet);
 
@@ -423,14 +487,14 @@ void server_connect_free(SERVER_CONNECT_REC *conn)
 
        g_free_not_null(conn->channels);
         g_free_not_null(conn->away_reason);
 
        g_free_not_null(conn->channels);
         g_free_not_null(conn->away_reason);
-        g_free(conn);
+
+        conn->type = 0;
+       g_free(conn);
 }
 
 void server_change_nick(SERVER_REC *server, const char *nick)
 {
 }
 
 void server_change_nick(SERVER_REC *server, const char *nick)
 {
-       g_free(server->connrec->nick);
        g_free(server->nick);
        g_free(server->nick);
-       server->connrec->nick = g_strdup(nick);
        server->nick = g_strdup(nick);
 
        signal_emit("server nick changed", 1, server);
        server->nick = g_strdup(nick);
 
        signal_emit("server nick changed", 1, server);
@@ -527,7 +591,6 @@ void servers_init(void)
        signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
 
        servers_reconnect_init();
        signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
 
        servers_reconnect_init();
-       servers_redirect_init();
        servers_setup_init();
 }
 
        servers_setup_init();
 }
 
@@ -536,7 +599,6 @@ void servers_deinit(void)
        signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
 
        servers_setup_deinit();
        signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
 
        servers_setup_deinit();
-       servers_redirect_deinit();
        servers_reconnect_deinit();
 
        module_uniq_destroy("SERVER");
        servers_reconnect_deinit();
 
        module_uniq_destroy("SERVER");
index 75e4cbf041f3007c703b5c4745f338f9402132b3..dddde2634525986ad85e4b437304a638c3b4c0ce 100644 (file)
@@ -17,6 +17,9 @@
 #define IS_SERVER_CONNECT(conn) \
        (SERVER_CONNECT(conn) ? TRUE : FALSE)
 
 #define IS_SERVER_CONNECT(conn) \
        (SERVER_CONNECT(conn) ? TRUE : FALSE)
 
+#define server_ischannel(server, channel) \
+        (server)->ischannel(server, channel)
+
 /* all strings should be either NULL or dynamically allocated */
 /* address and nick are mandatory, rest are optional */
 struct _SERVER_CONNECT_REC {
 /* all strings should be either NULL or dynamically allocated */
 /* address and nick are mandatory, rest are optional */
 struct _SERVER_CONNECT_REC {
@@ -28,6 +31,9 @@ struct _SERVER_REC {
 #include "server-rec.h"
 };
 
 #include "server-rec.h"
 };
 
+#define SEND_TARGET_CHANNEL    0
+#define SEND_TARGET_NICK       1
+
 extern GSList *servers, *lookup_servers;
 
 void servers_init(void);
 extern GSList *servers, *lookup_servers;
 
 void servers_init(void);
@@ -36,12 +42,16 @@ void servers_deinit(void);
 /* Disconnect from server */
 void server_disconnect(SERVER_REC *server);
 
 /* Disconnect from server */
 void server_disconnect(SERVER_REC *server);
 
+void server_ref(SERVER_REC *server);
+int server_unref(SERVER_REC *server);
+
 SERVER_REC *server_find_tag(const char *tag);
 SERVER_REC *server_find_chatnet(const char *chatnet);
 
 /* starts connecting to server */
 int server_start_connect(SERVER_REC *server);
 SERVER_REC *server_find_tag(const char *tag);
 SERVER_REC *server_find_chatnet(const char *chatnet);
 
 /* starts connecting to server */
 int server_start_connect(SERVER_REC *server);
-void server_connect_free(SERVER_CONNECT_REC *conn);
+void server_connect_ref(SERVER_CONNECT_REC *conn);
+void server_connect_unref(SERVER_CONNECT_REC *conn);
 
 /* initializes server record but doesn't start connecting */
 void server_connect_init(SERVER_REC *server);
 
 /* initializes server record but doesn't start connecting */
 void server_connect_init(SERVER_REC *server);
diff --git a/apps/irssi/src/core/session.c b/apps/irssi/src/core/session.c
new file mode 100644 (file)
index 0000000..0248f13
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ session.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "args.h"
+#include "net-sendbuffer.h"
+#include "pidwait.h"
+#include "lib-config/iconfig.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "servers-setup.h"
+#include "channels.h"
+#include "nicklist.h"
+
+static char *session_file;
+static char *irssi_binary;
+
+static char **session_args;
+
+void session_set_binary(const char *path)
+{
+       char **paths, **tmp;
+        char *str;
+
+       g_free_and_null(irssi_binary);
+
+       if (g_path_is_absolute(path)) {
+                /* full path - easy */
+               irssi_binary = g_strdup(path);
+                return;
+       }
+
+       if (strchr(path, G_DIR_SEPARATOR) != NULL) {
+               /* relative path */
+                str = g_get_current_dir();
+               irssi_binary = g_strconcat(str, G_DIR_SEPARATOR_S, path, NULL);
+               g_free(str);
+                return;
+       }
+
+       /* we'll need to find it from path. */
+       str = g_getenv("PATH");
+       if (str == NULL) return;
+
+       paths = g_strsplit(str, ":", -1);
+       for (tmp = paths; *tmp != NULL; tmp++) {
+                str = g_strconcat(*tmp, G_DIR_SEPARATOR_S, path, NULL);
+               if (access(str, X_OK) == 0) {
+                       irssi_binary = str;
+                        break;
+               }
+                g_free(str);
+       }
+       g_strfreev(paths);
+}
+
+void session_upgrade(void)
+{
+       if (session_args == NULL)
+                return;
+
+       execvp(session_args[0], session_args);
+       fprintf(stderr, "exec failed: %s: %s\n",
+               session_args[0], g_strerror(errno));
+}
+
+/* SYNTAX: UPGRADE [<irssi binary path>] */
+static void cmd_upgrade(const char *data)
+{
+       CONFIG_REC *session;
+       char *session_file, *str;
+
+       if (*data == '\0')
+               data = irssi_binary;
+       if (data == NULL)
+                cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       /* save the session */
+        session_file = g_strdup_printf("%s/session", get_irssi_dir());
+       session = config_open(session_file, 0600);
+        unlink(session_file);
+
+       signal_emit("session save", 1, session);
+        config_write(session, NULL, -1);
+        config_close(session);
+
+       /* data may contain some other program as well, like
+          /UPGRADE /usr/bin/screen irssi */
+       str = g_strdup_printf("%s --noconnect --session=%s --home=%s --config=%s",
+                             data, session_file, get_irssi_dir(), get_irssi_config());
+        session_args = g_strsplit(str, " ", -1);
+        g_free(str);
+
+       signal_emit("gui exit", 0);
+}
+
+static void session_save_nick(CHANNEL_REC *channel, NICK_REC *nick,
+                             CONFIG_REC *config, CONFIG_NODE *node)
+{
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+       config_node_set_str(config, node, "nick", nick->nick);
+       config_node_set_bool(config, node, "op", nick->op);
+       config_node_set_bool(config, node, "halfop", nick->halfop);
+       config_node_set_bool(config, node, "voice", nick->voice);
+
+       signal_emit("session save nick", 4, channel, nick, config, node);
+}
+
+static void session_save_channel_nicks(CHANNEL_REC *channel, CONFIG_REC *config,
+                                      CONFIG_NODE *node)
+{
+       GSList *tmp, *nicks;
+
+       node = config_node_section(node, "nicks", NODE_TYPE_LIST);
+        nicks = nicklist_getnicks(channel);
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next)
+               session_save_nick(channel, tmp->data, config, node);
+        g_slist_free(nicks);
+}
+
+static void session_save_channel(CHANNEL_REC *channel, CONFIG_REC *config,
+                                CONFIG_NODE *node)
+{
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+       config_node_set_str(config, node, "name", channel->name);
+       config_node_set_str(config, node, "topic", channel->topic);
+       config_node_set_str(config, node, "key", channel->key);
+
+       signal_emit("session save channel", 3, channel, config, node);
+}
+
+static void session_save_server_channels(SERVER_REC *server,
+                                        CONFIG_REC *config,
+                                        CONFIG_NODE *node)
+{
+       GSList *tmp;
+
+       /* save channels */
+        node = config_node_section(node, "channels", NODE_TYPE_LIST);
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next)
+               session_save_channel(tmp->data, config, node);
+}
+
+static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
+                               CONFIG_NODE *node)
+{
+       int handle;
+
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+       config_node_set_str(config, node, "chat_type",
+                           chat_protocol_find_id(server->chat_type)->name);
+       config_node_set_str(config, node, "address", server->connrec->address);
+       config_node_set_int(config, node, "port", server->connrec->port);
+       config_node_set_str(config, node, "chatnet", server->connrec->chatnet);
+       config_node_set_str(config, node, "password", server->connrec->password);
+       config_node_set_str(config, node, "nick", server->nick);
+
+       handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
+       config_node_set_int(config, node, "handle", handle);
+
+       signal_emit("session save server", 3, server, config, node);
+
+       /* fake the server disconnection */
+       g_io_channel_unref(net_sendbuffer_handle(server->handle));
+       net_sendbuffer_destroy(server->handle, FALSE);
+       server->handle = NULL;
+
+       server->connection_lost = TRUE;
+        server->no_reconnect = TRUE;
+        server_disconnect(server);
+}
+
+static void session_restore_channel_nicks(CHANNEL_REC *channel,
+                                         CONFIG_NODE *node)
+{
+       GSList *tmp;
+
+       /* restore nicks */
+       node = config_node_section(node, "nicks", -1);
+       if (node != NULL && node->type == NODE_TYPE_LIST) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp)) {
+                       signal_emit("session restore nick", 2,
+                                   channel, tmp->data);
+               }
+       }
+}
+
+static void session_restore_channel(SERVER_REC *server, CONFIG_NODE *node)
+{
+        CHANNEL_REC *channel;
+       const char *name;
+
+       name = config_node_get_str(node, "name", NULL);
+       if (name == NULL)
+               return;
+
+       channel = CHAT_PROTOCOL(server)->channel_create(server, name, TRUE);
+       channel->topic = g_strdup(config_node_get_str(node, "topic", NULL));
+        channel->key = g_strdup(config_node_get_str(node, "key", NULL));
+        channel->session_rejoin = TRUE;
+
+       signal_emit("session restore channel", 2, channel, node);
+}
+
+static void session_restore_server_channels(SERVER_REC *server,
+                                           CONFIG_NODE *node)
+{
+       GSList *tmp;
+
+       /* restore channels */
+       node = config_node_section(node, "channels", -1);
+       if (node != NULL && node->type == NODE_TYPE_LIST) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       session_restore_channel(server, tmp->data);
+       }
+}
+
+static void session_restore_server(CONFIG_NODE *node)
+{
+       CHAT_PROTOCOL_REC *proto;
+       SERVER_CONNECT_REC *conn;
+       SERVER_REC *server;
+       const char *chat_type, *address, *chatnet, *password, *nick;
+        int port, handle;
+
+        chat_type = config_node_get_str(node, "chat_type", NULL);
+       address = config_node_get_str(node, "address", NULL);
+       port = config_node_get_int(node, "port", 0);
+       chatnet = config_node_get_str(node, "chatnet", NULL);
+       password = config_node_get_str(node, "password", NULL);
+       nick = config_node_get_str(node, "nick", NULL);
+       handle = config_node_get_int(node, "handle", -1);
+
+       if (chat_type == NULL || address == NULL || nick == NULL || handle < 0)
+               return;
+
+       proto = chat_protocol_find(chat_type);
+       if (proto == NULL || proto->not_initialized) {
+               if (handle < 0) close(handle);
+               return;
+       }
+
+       conn = server_create_conn(proto->id, address, port,
+                                 chatnet, password, nick);
+       if (conn != NULL) {
+               conn->reconnection = TRUE;
+
+               server = proto->server_connect(conn);
+                server->handle = net_sendbuffer_create(g_io_channel_unix_new(handle), 0);
+               server->session_reconnect = TRUE;
+
+               signal_emit("session restore server", 2, server, node);
+       }
+}
+
+static void sig_session_save(CONFIG_REC *config)
+{
+       CONFIG_NODE *node;
+       GSList *tmp;
+        GString *str;
+
+        /* save servers */
+       node = config_node_traverse(config, "(servers", TRUE);
+       while (servers != NULL)
+               session_save_server(servers->data, config, node);
+
+       /* save pids */
+        str = g_string_new(NULL);
+       for (tmp = pidwait_get_pids(); tmp != NULL; tmp = tmp->next)
+                g_string_sprintfa(str, "%d ", GPOINTER_TO_INT(tmp->data));
+        config_node_set_str(config, config->mainnode, "pids", str->str);
+        g_string_free(str, TRUE);
+}
+
+static void sig_session_restore(CONFIG_REC *config)
+{
+       CONFIG_NODE *node;
+        GSList *tmp;
+        char **pids, **pid;
+
+        /* restore servers */
+       node = config_node_traverse(config, "(servers", FALSE);
+       if (node != NULL) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       session_restore_server(tmp->data);
+       }
+
+       /* restore pids (so we don't leave zombies) */
+       pids = g_strsplit(config_node_get_str(config->mainnode, "pids", ""), " ", -1);
+       for (pid = pids; *pid != NULL; pid++)
+                pidwait_add(atoi(*pid));
+        g_strfreev(pids);
+}
+
+static void sig_init_finished(void)
+{
+       CONFIG_REC *session;
+
+       if (session_file == NULL)
+               return;
+
+       session = config_open(session_file, -1);
+       if (session == NULL)
+               return;
+
+       config_parse(session);
+        signal_emit("session restore", 1, session);
+       config_close(session);
+
+       unlink(session_file);
+       session_file = NULL;
+}
+
+void session_init(void)
+{
+       static struct poptOption options[] = {
+               { "session", 0, POPT_ARG_STRING, &session_file, 0, "Used by /UPGRADE command", "PATH" },
+               { NULL, '\0', 0, NULL }
+       };
+
+        session_file = NULL;
+       args_register(options);
+
+       command_bind("upgrade", NULL, (SIGNAL_FUNC) cmd_upgrade);
+
+       signal_add("session save", (SIGNAL_FUNC) sig_session_save);
+       signal_add("session restore", (SIGNAL_FUNC) sig_session_restore);
+       signal_add("session save server", (SIGNAL_FUNC) session_save_server_channels);
+       signal_add("session restore server", (SIGNAL_FUNC) session_restore_server_channels);
+       signal_add("session save channel", (SIGNAL_FUNC) session_save_channel_nicks);
+       signal_add("session restore channel", (SIGNAL_FUNC) session_restore_channel_nicks);
+       signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+}
+
+void session_deinit(void)
+{
+       g_free_not_null(irssi_binary);
+
+        command_unbind("upgrade", (SIGNAL_FUNC) cmd_upgrade);
+
+       signal_remove("session save", (SIGNAL_FUNC) sig_session_save);
+       signal_remove("session restore", (SIGNAL_FUNC) sig_session_restore);
+       signal_remove("session save server", (SIGNAL_FUNC) session_save_server_channels);
+       signal_remove("session restore server", (SIGNAL_FUNC) session_restore_server_channels);
+       signal_remove("session save channel", (SIGNAL_FUNC) session_save_channel_nicks);
+       signal_remove("session restore channel", (SIGNAL_FUNC) session_restore_channel_nicks);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+}
diff --git a/apps/irssi/src/core/session.h b/apps/irssi/src/core/session.h
new file mode 100644 (file)
index 0000000..5788b5f
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __SESSION_H
+#define __SESSION_H
+
+void session_set_binary(const char *path);
+void session_upgrade(void);
+
+void session_init(void);
+void session_deinit(void);
+
+#endif
index 10ecba92a75aeee8c36c07b7414e1b4ea0d33409..1a98462af1397c273fdd8794649cd0d374c2c8c1 100644 (file)
@@ -32,7 +32,6 @@
 CONFIG_REC *mainconfig;
 
 static GString *last_errors;
 CONFIG_REC *mainconfig;
 
 static GString *last_errors;
-static char *last_config_error_msg;
 static GSList *last_invalid_modules;
 static int fe_initialized;
 static int config_changed; /* FIXME: remove after .98 (unless needed again) */
 static GSList *last_invalid_modules;
 static int fe_initialized;
 static int config_changed; /* FIXME: remove after .98 (unless needed again) */
@@ -274,11 +273,6 @@ static void sig_init_finished(void)
                g_string_free(last_errors, TRUE);
        }
 
                g_string_free(last_errors, TRUE);
        }
 
-       if (last_config_error_msg != NULL) {
-               signal_emit("gui dialog", 2, "error", last_config_error_msg);
-               g_free_and_null(last_config_error_msg);
-       }
-
        if (config_changed) {
                /* some backwards compatibility changes were made to
                   config file, reload it */
        if (config_changed) {
                /* some backwards compatibility changes were made to
                   config file, reload it */
@@ -286,20 +280,6 @@ static void sig_init_finished(void)
        }
 }
 
        }
 }
 
-/* FIXME: remove after 0.7.98 - only for backward compatibility */
-static void settings_move(SETTINGS_REC *rec, char *value)
-{
-       CONFIG_NODE *setnode, *node;
-
-       setnode = iconfig_node_traverse("settings", TRUE);
-       node = config_node_section(setnode, rec->module, NODE_TYPE_BLOCK);
-
-       iconfig_node_set_str(node, rec->key, value);
-       iconfig_node_set_str(setnode, rec->key, NULL);
-
-        config_changed = TRUE;
-}
-
 static void settings_clean_invalid_module(const char *module)
 {
         CONFIG_NODE *node;
 static void settings_clean_invalid_module(const char *module)
 {
         CONFIG_NODE *node;
@@ -312,9 +292,9 @@ static void settings_clean_invalid_module(const char *module)
        node = config_node_section(node, module, -1);
        if (node == NULL) return;
 
        node = config_node_section(node, module, -1);
        if (node == NULL) return;
 
-       for (tmp = node->value; tmp != NULL; tmp = next) {
+       for (tmp = config_node_first(node->value); tmp != NULL; tmp = next) {
                CONFIG_NODE *subnode = tmp->data;
                CONFIG_NODE *subnode = tmp->data;
-                next = tmp->next;
+                next = config_node_next(tmp);
 
                set = g_hash_table_lookup(settings, subnode->key);
                if (set == NULL || strcmp(set->module, module) != 0)
 
                set = g_hash_table_lookup(settings, subnode->key);
                if (set == NULL || strcmp(set->module, module) != 0)
@@ -344,25 +324,12 @@ void settings_check_module(const char *module)
         SETTINGS_REC *set;
        CONFIG_NODE *node;
         GString *errors;
         SETTINGS_REC *set;
        CONFIG_NODE *node;
         GString *errors;
-       GSList *tmp, *next;
+       GSList *tmp;
         int count;
 
         g_return_if_fail(module != NULL);
 
        node = iconfig_node_traverse("settings", FALSE);
         int count;
 
         g_return_if_fail(module != NULL);
 
        node = iconfig_node_traverse("settings", FALSE);
-       if (node != NULL) {
-               /* FIXME: remove after 0.7.98 */
-               for (tmp = node->value; tmp != NULL; tmp = next) {
-                       CONFIG_NODE *node = tmp->data;
-
-                        next = tmp->next;
-                       if (node->type != NODE_TYPE_KEY)
-                               continue;
-                       set = g_hash_table_lookup(settings, node->key);
-                        if (set != NULL)
-                               settings_move(set, node->value);
-               }
-       }
        node = node == NULL ? NULL : config_node_section(node, module, -1);
        if (node == NULL) return;
 
        node = node == NULL ? NULL : config_node_section(node, module, -1);
        if (node == NULL) return;
 
@@ -371,7 +338,8 @@ void settings_check_module(const char *module)
                         "file for module %s:", module);
 
         count = 0;
                         "file for module %s:", module);
 
         count = 0;
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                node = tmp->data;
 
                set = g_hash_table_lookup(settings, node->key);
                node = tmp->data;
 
                set = g_hash_table_lookup(settings, node->key);
@@ -489,18 +457,17 @@ static CONFIG_REC *parse_configfile(const char *fname)
        CONFIG_REC *config;
        struct stat statbuf;
         const char *path;
        CONFIG_REC *config;
        struct stat statbuf;
         const char *path;
-       char *real_fname;
+       char *str;
 
 
-       real_fname = fname != NULL ? g_strdup(fname) :
-               g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc"
-                               G_DIR_SEPARATOR_S"config", g_get_home_dir());
+       if (fname == NULL)
+               fname = get_irssi_config();
 
 
-       if (stat(real_fname, &statbuf) == 0)
-               path = real_fname;
+       if (stat(fname, &statbuf) == 0)
+               path = fname;
        else {
                /* user configuration file not found, use the default one
                   from sysconfdir */
        else {
                /* user configuration file not found, use the default one
                   from sysconfdir */
-                path = SYSCONFDIR"/irssi/config";
+                path = SYSCONFDIR"/irssi.conf";
                if (stat(path, &statbuf) != 0) {
                        /* no configuration file in sysconfdir ..
                           use the build-in configuration */
                if (stat(path, &statbuf) != 0) {
                        /* no configuration file in sysconfdir ..
                           use the build-in configuration */
@@ -510,9 +477,11 @@ static CONFIG_REC *parse_configfile(const char *fname)
 
        config = config_open(path, -1);
        if (config == NULL) {
 
        config = config_open(path, -1);
        if (config == NULL) {
-               last_config_error_msg =
-                       g_strdup_printf("Error opening configuration file %s: %s",
-                                       path, g_strerror(errno));
+               str = g_strdup_printf("Error opening configuration file %s: %s",
+                                     path, g_strerror(errno));
+               signal_emit("gui dialog", 2, "error", str);
+                g_free(str);
+
                config = config_open(NULL, -1);
        }
 
                config = config_open(NULL, -1);
        }
 
@@ -521,9 +490,8 @@ static CONFIG_REC *parse_configfile(const char *fname)
         else
                config_parse_data(config, default_config, "internal");
 
         else
                config_parse_data(config, default_config, "internal");
 
-       config_change_file_name(config, real_fname, 0660);
-        irssi_config_save_state(real_fname);
-       g_free(real_fname);
+       config_change_file_name(config, fname, 0660);
+        irssi_config_save_state(fname);
        return config;
 }
 
        return config;
 }
 
@@ -532,27 +500,26 @@ static void init_configfile(void)
        struct stat statbuf;
        char *str;
 
        struct stat statbuf;
        char *str;
 
-       str = g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc", g_get_home_dir());
-       if (stat(str, &statbuf) != 0) {
+       if (stat(get_irssi_dir(), &statbuf) != 0) {
                /* ~/.irssi not found, create it. */
                /* ~/.irssi not found, create it. */
-               if (mkpath(str, 0700) != 0) {
-                       g_error("Couldn't create %s directory", str);
+               if (mkpath(get_irssi_dir(), 0700) != 0) {
+                       g_error("Couldn't create %s directory", get_irssi_dir());
                }
        } else if (!S_ISDIR(statbuf.st_mode)) {
                g_error("%s is not a directory.\n"
                }
        } else if (!S_ISDIR(statbuf.st_mode)) {
                g_error("%s is not a directory.\n"
-                       "You should remove it with command: rm ~/.irssi", str);
+                       "You should remove it with command: rm %s",
+                       get_irssi_dir(), get_irssi_dir());
        }
        }
-       g_free(str);
 
        mainconfig = parse_configfile(NULL);
        config_last_modifycounter = mainconfig->modifycounter;
 
        /* any errors? */
        if (config_last_error(mainconfig) != NULL) {
 
        mainconfig = parse_configfile(NULL);
        config_last_modifycounter = mainconfig->modifycounter;
 
        /* any errors? */
        if (config_last_error(mainconfig) != NULL) {
-               last_config_error_msg =
-                       g_strdup_printf("Ignored errors in configuration "
-                                       "file:\n%s",
-                                       config_last_error(mainconfig));
+               str = g_strdup_printf("Ignored errors in configuration file:\n%s",
+                                     config_last_error(mainconfig));
+               signal_emit("gui dialog", 2, "error", str);
+                g_free(str);
        }
 
        signal(SIGTERM, sig_term);
        }
 
        signal(SIGTERM, sig_term);
@@ -563,11 +530,9 @@ int settings_reread(const char *fname)
        CONFIG_REC *tempconfig;
        char *str;
 
        CONFIG_REC *tempconfig;
        char *str;
 
-       if (fname == NULL) fname = "~/.silc/config";
-
-       str = convert_home(fname);
+       str = fname == NULL ? NULL : convert_home(fname);
        tempconfig = parse_configfile(str);
        tempconfig = parse_configfile(str);
-       g_free(str);
+        g_free_not_null(str);
 
        if (tempconfig == NULL) {
                signal_emit("gui dialog", 2, "error", g_strerror(errno));
 
        if (tempconfig == NULL) {
                signal_emit("gui dialog", 2, "error", g_strerror(errno));
@@ -589,11 +554,11 @@ int settings_reread(const char *fname)
        config_last_modifycounter = mainconfig->modifycounter;
 
        signal_emit("setup changed", 0);
        config_last_modifycounter = mainconfig->modifycounter;
 
        signal_emit("setup changed", 0);
-       signal_emit("setup reread", 0);
+       signal_emit("setup reread", 1, mainconfig->fname);
         return TRUE;
 }
 
         return TRUE;
 }
 
-int settings_save(const char *fname)
+int settings_save(const char *fname, int autosave)
 {
        char *str;
        int error;
 {
        char *str;
        int error;
@@ -610,19 +575,20 @@ int settings_save(const char *fname)
                signal_emit("gui dialog", 2, "error", str);
                g_free(str);
        }
                signal_emit("gui dialog", 2, "error", str);
                g_free(str);
        }
+       signal_emit("setup saved", 2, fname, GINT_TO_POINTER(autosave));
         return !error;
 }
 
         return !error;
 }
 
-static void sig_autosave(void)
+static int sig_autosave(void)
 {
        char *fname, *str;
 
        if (!settings_get_bool("settings_autosave") ||
            config_last_modifycounter == mainconfig->modifycounter)
 {
        char *fname, *str;
 
        if (!settings_get_bool("settings_autosave") ||
            config_last_modifycounter == mainconfig->modifycounter)
-               return;
+               return 1;
 
        if (!irssi_config_is_changed(NULL))
 
        if (!irssi_config_is_changed(NULL))
-               settings_save(NULL);
+               settings_save(NULL, TRUE);
        else {
                fname = g_strconcat(mainconfig->fname, ".autosave", NULL);
                str = g_strdup_printf("Configuration file was modified "
        else {
                fname = g_strconcat(mainconfig->fname, ".autosave", NULL);
                str = g_strdup_printf("Configuration file was modified "
@@ -633,18 +599,19 @@ static void sig_autosave(void)
                signal_emit("gui dialog", 2, "warning", str);
                g_free(str);
 
                signal_emit("gui dialog", 2, "warning", str);
                g_free(str);
 
-                settings_save(fname);
+                settings_save(fname, TRUE);
                g_free(fname);
        }
                g_free(fname);
        }
+
+        return 1;
 }
 
 void settings_init(void)
 {
 }
 
 void settings_init(void)
 {
-       settings = g_hash_table_new((GHashFunc) g_str_hash,
-                                   (GCompareFunc) g_str_equal);
+       settings = g_hash_table_new((GHashFunc) g_istr_hash,
+                                   (GCompareFunc) g_istr_equal);
 
        last_errors = NULL;
 
        last_errors = NULL;
-       last_config_error_msg = NULL;
         last_invalid_modules = NULL;
        fe_initialized = FALSE;
         config_changed = FALSE;
         last_invalid_modules = NULL;
        fe_initialized = FALSE;
         config_changed = FALSE;
index cea8c4d02d3efbf94063bec8935c5bfc040e07c5..e0df975a185807e459a39ebbaeb2c6ac3e024a62 100644 (file)
@@ -35,6 +35,7 @@ typedef struct {
 #define iconfig_node_add_list(a, b) config_node_add_list(mainconfig, a, b)
 
 extern CONFIG_REC *mainconfig;
 #define iconfig_node_add_list(a, b) config_node_add_list(mainconfig, a, b)
 
 extern CONFIG_REC *mainconfig;
+extern const char *default_config;
 
 /* Functions for handling the "settings" node of Irssi configuration */
 const char *settings_get_str(const char *key);
 
 /* Functions for handling the "settings" node of Irssi configuration */
 const char *settings_get_str(const char *key);
@@ -80,7 +81,7 @@ void settings_clean_invalid(void);
 
 /* if `fname' is NULL, the default is used */
 int settings_reread(const char *fname);
 
 /* if `fname' is NULL, the default is used */
 int settings_reread(const char *fname);
-int settings_save(const char *fname);
+int settings_save(const char *fname, int autosave);
 int irssi_config_is_changed(const char *fname);
 
 void settings_init(void);
 int irssi_config_is_changed(const char *fname);
 
 void settings_init(void);
index cb964eb0d4d52661fa258a96028ff11114e15ac1..92064e8d4cf0d272180baee97f151145d63917bd 100644 (file)
@@ -26,9 +26,9 @@
 
 typedef struct {
        int id; /* signal id */
 
 typedef struct {
        int id; /* signal id */
+        int refcount;
 
        int emitting; /* signal is being emitted */
 
        int emitting; /* signal is being emitted */
-       int altered; /* some signal functions are marked as NULL */
        int stop_emit; /* this signal was stopped */
 
        GPtrArray *modulelist[SIGNAL_LISTS]; /* list of what signals belong
        int stop_emit; /* this signal was stopped */
 
        GPtrArray *modulelist[SIGNAL_LISTS]; /* list of what signals belong
@@ -43,6 +43,37 @@ static GMemChunk *signals_chunk;
 static GHashTable *signals;
 static SIGNAL_REC *current_emitted_signal;
 
 static GHashTable *signals;
 static SIGNAL_REC *current_emitted_signal;
 
+#define signal_ref(signal) ++(signal)->refcount
+#define signal_unref(rec) (signal_unref_full(rec, TRUE))
+
+static int signal_unref_full(SIGNAL_REC *rec, int remove_hash)
+{
+       if (rec->refcount == 0) {
+               g_error("signal_unref(%s) : BUG - reference counter == 0",
+                       signal_get_id_str(rec->id));
+       }
+
+       if (--rec->refcount != 0)
+               return FALSE;
+
+       /* remove whole signal from memory */
+       if (!signal_is_emitlist_empty(rec)) {
+               g_error("signal_unref(%s) : BUG - emitlist wasn't empty",
+                       signal_get_id_str(rec->id));
+       }
+
+       if (remove_hash)
+               g_hash_table_remove(signals, GINT_TO_POINTER(rec->id));
+       g_mem_chunk_free(signals_chunk, rec);
+       return TRUE;
+}
+
+static void signal_unref_count(SIGNAL_REC *rec, int count)
+{
+       while (count-- > 0)
+                signal_unref(rec);
+}
+
 void signal_add_to(const char *module, int pos,
                   const char *signal, SIGNAL_FUNC func)
 {
 void signal_add_to(const char *module, int pos,
                   const char *signal, SIGNAL_FUNC func)
 {
@@ -64,7 +95,7 @@ void signal_add_to_id(const char *module, int pos,
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
        if (rec == NULL) {
                rec = g_mem_chunk_alloc0(signals_chunk);
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
        if (rec == NULL) {
                rec = g_mem_chunk_alloc0(signals_chunk);
-                rec->id = signal_id;
+               rec->id = signal_id;
                g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), rec);
        }
 
                g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), rec);
        }
 
@@ -75,19 +106,8 @@ void signal_add_to_id(const char *module, int pos,
 
        g_ptr_array_add(rec->siglist[pos], (void *) func);
        g_ptr_array_add(rec->modulelist[pos], (void *) module);
 
        g_ptr_array_add(rec->siglist[pos], (void *) func);
        g_ptr_array_add(rec->modulelist[pos], (void *) module);
-}
-
-/* Destroy the whole signal */
-static void signal_destroy(int signal_id)
-{
-       SIGNAL_REC *rec;
 
 
-       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
-       if (rec != NULL) {
-               /* remove whole signal from memory */
-               g_hash_table_remove(signals, GINT_TO_POINTER(signal_id));
-               g_free(rec);
-       }
+        signal_ref(rec);
 }
 
 static int signal_list_find(GPtrArray *array, void *data)
 }
 
 static int signal_list_find(GPtrArray *array, void *data)
@@ -102,23 +122,28 @@ static int signal_list_find(GPtrArray *array, void *data)
        return -1;
 }
 
        return -1;
 }
 
-static void signal_remove_from_list(SIGNAL_REC *rec, int signal_id,
-                                   int list, int index)
+static void signal_list_free(SIGNAL_REC *rec, int list)
 {
 {
-       if (rec->emitting) {
-               g_ptr_array_index(rec->siglist[list], index) = NULL;
-               rec->altered = TRUE;
-       } else {
-               g_ptr_array_remove_index(rec->siglist[list], index);
-               g_ptr_array_remove_index(rec->modulelist[list], index);
-               if (signal_is_emitlist_empty(rec))
-                       signal_destroy(signal_id);
-       }
+       g_ptr_array_free(rec->siglist[list], TRUE);
+       g_ptr_array_free(rec->modulelist[list], TRUE);
+       rec->siglist[list] = NULL;
+       rec->modulelist[list] = NULL;
+}
+
+/* Returns TRUE if the whole signal is removed after this remove */
+static void signal_remove_from_list(SIGNAL_REC *rec, int list, int index)
+{
+       g_ptr_array_remove_index(rec->siglist[list], index);
+       g_ptr_array_remove_index(rec->modulelist[list], index);
+
+       if (rec->siglist[list]->len == 0)
+               signal_list_free(rec, list);
+
+       signal_unref(rec);
 }
 
 /* Remove signal from emit lists */
 }
 
 /* Remove signal from emit lists */
-static int signal_remove_from_lists(SIGNAL_REC *rec, int signal_id,
-                                   SIGNAL_FUNC func)
+static int signal_remove_from_lists(SIGNAL_REC *rec, SIGNAL_FUNC func)
 {
        int n, index;
 
 {
        int n, index;
 
@@ -129,7 +154,7 @@ static int signal_remove_from_lists(SIGNAL_REC *rec, int signal_id,
                index = signal_list_find(rec->siglist[n], (void *) func);
                if (index != -1) {
                        /* remove the function from emit list */
                index = signal_list_find(rec->siglist[n], (void *) func);
                if (index != -1) {
                        /* remove the function from emit list */
-                       signal_remove_from_list(rec, signal_id, n, index);
+                       signal_remove_from_list(rec, n, index);
                        return 1;
                }
        }
                        return 1;
                }
        }
@@ -146,7 +171,7 @@ void signal_remove_id(int signal_id, SIGNAL_FUNC func)
 
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
         if (rec != NULL)
 
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
         if (rec != NULL)
-               signal_remove_from_lists(rec, signal_id, func);
+               signal_remove_from_lists(rec, func);
 }
 
 /* unbind signal */
 }
 
 /* unbind signal */
@@ -157,34 +182,22 @@ void signal_remove(const char *signal, SIGNAL_FUNC func)
        signal_remove_id(signal_get_uniq_id(signal), func);
 }
 
        signal_remove_id(signal_get_uniq_id(signal), func);
 }
 
-/* Remove all NULL functions from signal list */
-static void signal_list_clean(SIGNAL_REC *rec)
-{
-       int n, index;
-
-       for (n = 0; n < SIGNAL_LISTS; n++) {
-               if (rec->siglist[n] == NULL)
-                       continue;
-
-               for (index = rec->siglist[n]->len-1; index >= 0; index--) {
-                       if (g_ptr_array_index(rec->siglist[n], index) == NULL) {
-                               g_ptr_array_remove_index(rec->siglist[n], index);
-                               g_ptr_array_remove_index(rec->modulelist[n], index);
-                       }
-               }
-       }
-}
-
-static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist)
+static int signal_emit_real(SIGNAL_REC *rec, int params, va_list va)
 {
 {
+       gconstpointer arglist[SIGNAL_MAX_ARGUMENTS];
         SIGNAL_REC *prev_emitted_signal;
         SIGNAL_FUNC func;
        int n, index, stopped, stop_emit_count;
 
         SIGNAL_REC *prev_emitted_signal;
         SIGNAL_FUNC func;
        int n, index, stopped, stop_emit_count;
 
+       for (n = 0; n < SIGNAL_MAX_ARGUMENTS; n++)
+               arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer);
+
        /* signal_stop_by_name("signal"); signal_emit("signal", ...);
           fails if we compare rec->stop_emit against 0. */
        stop_emit_count = rec->stop_emit;
 
        /* signal_stop_by_name("signal"); signal_emit("signal", ...);
           fails if we compare rec->stop_emit against 0. */
        stop_emit_count = rec->stop_emit;
 
+        signal_ref(rec);
+
        stopped = FALSE;
        rec->emitting++;
        for (n = 0; n < SIGNAL_LISTS; n++) {
        stopped = FALSE;
        rec->emitting++;
        for (n = 0; n < SIGNAL_LISTS; n++) {
@@ -195,15 +208,13 @@ static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist)
                for (index = rec->siglist[n]->len-1; index >= 0; index--) {
                        func = (SIGNAL_FUNC) g_ptr_array_index(rec->siglist[n], index);
 
                for (index = rec->siglist[n]->len-1; index >= 0; index--) {
                        func = (SIGNAL_FUNC) g_ptr_array_index(rec->siglist[n], index);
 
-                       if (func != NULL) {
-                                prev_emitted_signal = current_emitted_signal;
-                               current_emitted_signal = rec;
+                       prev_emitted_signal = current_emitted_signal;
+                       current_emitted_signal = rec;
 #if SIGNAL_MAX_ARGUMENTS != 6
 #if SIGNAL_MAX_ARGUMENTS != 6
-#  error SIGNAL_MAX_ARGS changed - update code
+#  error SIGNAL_MAX_ARGUMENTS changed - update code
 #endif
 #endif
-                               func(arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5]);
-                               current_emitted_signal = prev_emitted_signal;
-                       }
+                       func(arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5]);
+                       current_emitted_signal = prev_emitted_signal;
 
                        if (rec->stop_emit != stop_emit_count) {
                                stopped = TRUE;
 
                        if (rec->stop_emit != stop_emit_count) {
                                stopped = TRUE;
@@ -220,60 +231,48 @@ static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist)
                        /* signal_stop() used too many times */
                         rec->stop_emit = 0;
                }
                        /* signal_stop() used too many times */
                         rec->stop_emit = 0;
                }
-               if (rec->altered) {
-                       signal_list_clean(rec);
-                       rec->altered = FALSE;
-               }
        }
 
        }
 
+        signal_unref(rec);
        return stopped;
 }
 
        return stopped;
 }
 
-static int signal_emitv_id(int signal_id, int params, va_list va)
+int signal_emit(const char *signal, int params, ...)
 {
 {
-       gconstpointer arglist[SIGNAL_MAX_ARGUMENTS];
        SIGNAL_REC *rec;
        SIGNAL_REC *rec;
-       int n;
+       va_list va;
+       int signal_id;
 
 
-       g_return_val_if_fail(signal_id >= 0, FALSE);
        g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
 
        g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
 
-       for (n = 0; n < SIGNAL_MAX_ARGUMENTS; n++)
-               arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer);
+       signal_id = signal_get_uniq_id(signal);
 
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
 
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
-       if (rec != NULL && signal_emit_real(rec, arglist))
-               return TRUE;
+       if (rec != NULL) {
+               va_start(va, params);
+               signal_emit_real(rec, params, va);
+               va_end(va);
+       }
 
        return rec != NULL;
 }
 
 
        return rec != NULL;
 }
 
-int signal_emit(const char *signal, int params, ...)
-{
-       va_list va;
-       int signal_id, ret;
-
-       /* get arguments */
-       signal_id = signal_get_uniq_id(signal);
-
-       va_start(va, params);
-       ret = signal_emitv_id(signal_id, params, va);
-       va_end(va);
-
-       return ret;
-}
-
 int signal_emit_id(int signal_id, int params, ...)
 {
 int signal_emit_id(int signal_id, int params, ...)
 {
+       SIGNAL_REC *rec;
        va_list va;
        va_list va;
-       int ret;
 
 
-       /* get arguments */
-       va_start(va, params);
-       ret = signal_emitv_id(signal_id, params, va);
-       va_end(va);
+       g_return_val_if_fail(signal_id >= 0, FALSE);
+       g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
 
 
-       return ret;
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       if (rec != NULL) {
+               va_start(va, params);
+               signal_emit_real(rec, params, va);
+               va_end(va);
+       }
+
+       return rec != NULL;
 }
 
 /* stop the current ongoing signal emission */
 }
 
 /* stop the current ongoing signal emission */
@@ -332,28 +331,37 @@ int signal_is_stopped(int signal_id)
 static void signal_remove_module(void *signal, SIGNAL_REC *rec,
                                 const char *module)
 {
 static void signal_remove_module(void *signal, SIGNAL_REC *rec,
                                 const char *module)
 {
-       unsigned int index;
-       int signal_id, list;
-
-       signal_id = GPOINTER_TO_INT(signal);
+        unsigned int index;
+       int list;
 
        for (list = 0; list < SIGNAL_LISTS; list++) {
                if (rec->modulelist[list] == NULL)
                        continue;
 
 
        for (list = 0; list < SIGNAL_LISTS; list++) {
                if (rec->modulelist[list] == NULL)
                        continue;
 
-               for (index = 0; index < rec->modulelist[list]->len; index++) {
-                       if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index), module) == 0)
-                               signal_remove_from_list(rec, signal_id, list, index);
-               }
+               for (index = rec->modulelist[list]->len; index > 0; index--)
+                       if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index-1), module) == 0)
+                               signal_remove_from_list(rec, list, index-1);
        }
 }
 
        }
 }
 
+static void signal_foreach_ref(void *signal, SIGNAL_REC *rec)
+{
+        signal_ref(rec);
+}
+
+static int signal_foreach_unref(void *signal, SIGNAL_REC *rec)
+{
+        return signal_unref_full(rec, FALSE);
+}
+
 /* remove all signals that belong to `module' */
 void signals_remove_module(const char *module)
 {
        g_return_if_fail(module != NULL);
 
 /* remove all signals that belong to `module' */
 void signals_remove_module(const char *module)
 {
        g_return_if_fail(module != NULL);
 
+       g_hash_table_foreach(signals, (GHFunc) signal_foreach_ref, NULL);
        g_hash_table_foreach(signals, (GHFunc) signal_remove_module, (void *) module);
        g_hash_table_foreach(signals, (GHFunc) signal_remove_module, (void *) module);
+       g_hash_table_foreach_remove(signals, (GHRFunc) signal_foreach_unref, NULL);
 }
 
 void signals_init(void)
 }
 
 void signals_init(void)
@@ -367,15 +375,19 @@ static void signal_free(void *key, SIGNAL_REC *rec)
 {
        int n;
 
 {
        int n;
 
+        signal_ref(rec);
+
        for (n = 0; n < SIGNAL_LISTS; n++) {
                if (rec->siglist[n] != NULL) {
        for (n = 0; n < SIGNAL_LISTS; n++) {
                if (rec->siglist[n] != NULL) {
-                       g_ptr_array_free(rec->siglist[n], TRUE);
-                       g_ptr_array_free(rec->modulelist[n], TRUE);
+                       signal_unref_count(rec, rec->siglist[n]->len);
+                        signal_list_free(rec, n);
                }
        }
 
                }
        }
 
-       g_mem_chunk_free(signals_chunk, rec);
-       current_emitted_signal = NULL;
+       if (!signal_unref_full(rec, FALSE)) {
+               g_error("signal_free(%s) : BUG - signal still has %d references",
+                       signal_get_id_str(rec->id), rec->refcount);
+       }
 }
 
 void signals_deinit(void)
 }
 
 void signals_deinit(void)
index 40372f93f98b2c5e3e4750d2bc71066807f808bd..4b1f535b919450695fa1537b94d05282a3d4f64e 100644 (file)
@@ -23,6 +23,7 @@
 #include "special-vars.h"
 #include "expandos.h"
 #include "settings.h"
 #include "special-vars.h"
 #include "expandos.h"
 #include "settings.h"
+#include "servers.h"
 #include "misc.h"
 
 #define ALIGN_RIGHT 0x01
 #include "misc.h"
 
 #define ALIGN_RIGHT 0x01
 #define ALIGN_PAD   0x04
 
 #define isvarchar(c) \
 #define ALIGN_PAD   0x04
 
 #define isvarchar(c) \
-        (isalnum(c) || (c) == '_')
+        (i_isalnum(c) || (c) == '_')
+
+#define isarg(c) \
+       (i_isdigit(c) || (c) == '*' || (c) == '~' || (c) == '-')
 
 static SPECIAL_HISTORY_FUNC history_func = NULL;
 
 
 static SPECIAL_HISTORY_FUNC history_func = NULL;
 
@@ -43,7 +47,7 @@ static char *get_argument(char **cmd, char **arglist)
        arg = 0;
        max = -1;
 
        arg = 0;
        max = -1;
 
-       argcount = strarray_length(arglist);
+       argcount = arglist == NULL ? 0 : strarray_length(arglist);
 
        if (**cmd == '*') {
                /* get all arguments */
 
        if (**cmd == '*') {
                /* get all arguments */
@@ -51,7 +55,7 @@ static char *get_argument(char **cmd, char **arglist)
                /* get last argument */
                arg = max = argcount-1;
        } else {
                /* get last argument */
                arg = max = argcount-1;
        } else {
-               if (isdigit(**cmd)) {
+               if (i_isdigit(**cmd)) {
                        /* first argument */
                        arg = max = (**cmd)-'0';
                        (*cmd)++;
                        /* first argument */
                        arg = max = (**cmd)-'0';
                        (*cmd)++;
@@ -60,7 +64,7 @@ static char *get_argument(char **cmd, char **arglist)
                if (**cmd == '-') {
                        /* get more than one argument */
                        (*cmd)++;
                if (**cmd == '-') {
                        /* get more than one argument */
                        (*cmd)++;
-                       if (!isdigit(**cmd))
+                       if (!i_isdigit(**cmd))
                                max = -1; /* get all the rest */
                        else {
                                max = (**cmd)-'0';
                                max = -1; /* get all the rest */
                        else {
                                max = (**cmd)-'0';
@@ -71,7 +75,7 @@ static char *get_argument(char **cmd, char **arglist)
        }
 
        str = g_string_new(NULL);
        }
 
        str = g_string_new(NULL);
-       while (arg < argcount && (arg <= max || max == -1)) {
+       while (arg >= 0 && arg < argcount && (arg <= max || max == -1)) {
                g_string_append(str, arglist[arg]);
                g_string_append_c(str, ' ');
                arg++;
                g_string_append(str, arglist[arg]);
                g_string_append_c(str, ' ');
                arg++;
@@ -152,7 +156,7 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item,
 {
        EXPANDO_FUNC func;
 
 {
        EXPANDO_FUNC func;
 
-       if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
+       if (isarg(**cmd)) {
                /* argument */
                *free_ret = TRUE;
                if (arg_used != NULL) *arg_used = TRUE;
                /* argument */
                *free_ret = TRUE;
                if (arg_used != NULL) *arg_used = TRUE;
@@ -160,7 +164,7 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item,
                        get_argument(cmd, arglist);
        }
 
                        get_argument(cmd, arglist);
        }
 
-       if (isalpha(**cmd) && isvarchar((*cmd)[1])) {
+       if (i_isalpha(**cmd) && isvarchar((*cmd)[1])) {
                /* long variable name.. */
                return get_long_variable(cmd, server, item, free_ret, getname);
        }
                /* long variable name.. */
                return get_long_variable(cmd, server, item, free_ret, getname);
        }
@@ -186,7 +190,7 @@ static char *get_history(char **cmd, void *item, int *free_ret)
        if (history_func == NULL)
                ret = NULL;
        else {
        if (history_func == NULL)
                ret = NULL;
        else {
-               text = g_strndup(start, (int) (*cmd-start)+1);
+               text = g_strndup(start, (int) (*cmd-start));
                ret = history_func(text, item, free_ret);
                g_free(text);
        }
                ret = history_func(text, item, free_ret);
                g_free(text);
        }
@@ -202,6 +206,11 @@ static char *get_special_value(char **cmd, SERVER_REC *server, void *item,
        char command, *value, *p;
        int len;
 
        char command, *value, *p;
        int len;
 
+       if ((flags & PARSE_FLAG_ONLY_ARGS) && !isarg(**cmd)) {
+               *free_ret = TRUE;
+               return g_strdup_printf("$%c", **cmd);
+       }
+
        if (**cmd == '!') {
                /* find text from command history */
                if (flags & PARSE_FLAG_GETNAME)
        if (**cmd == '!') {
                /* find text from command history */
                if (flags & PARSE_FLAG_GETNAME)
@@ -279,7 +288,7 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
 
        /* '!' = don't cut, '-' = right padding */
        str = *data;
 
        /* '!' = don't cut, '-' = right padding */
        str = *data;
-       while (*str != '\0' && *str != ']' && !isdigit(*str)) {
+       while (*str != '\0' && *str != ']' && !i_isdigit(*str)) {
                if (*str == '!')
                        *flags &= ~ALIGN_CUT;
                else if (*str == '-')
                if (*str == '!')
                        *flags &= ~ALIGN_CUT;
                else if (*str == '-')
@@ -288,11 +297,11 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
                          *flags &= ~ALIGN_PAD;
                str++;
        }
                          *flags &= ~ALIGN_PAD;
                str++;
        }
-       if (!isdigit(*str))
+       if (!i_isdigit(*str))
                return FALSE; /* expecting number */
 
        /* get the alignment size */
                return FALSE; /* expecting number */
 
        /* get the alignment size */
-       while (isdigit(*str)) {
+       while (i_isdigit(*str)) {
                *align = (*align) * 10 + (*str-'0');
                str++;
        }
                *align = (*align) * 10 + (*str-'0');
                str++;
        }
@@ -435,11 +444,31 @@ char *parse_special(char **cmd, SERVER_REC *server, void *item,
        return value;
 }
 
        return value;
 }
 
-static void gstring_append_escaped(GString *str, const char *text)
+static void gstring_append_escaped(GString *str, const char *text, int flags)
 {
 {
+       char esc[4], *escpos;
+       
+       escpos = esc;
+       if (flags & PARSE_FLAG_ESCAPE_VARS)
+               *escpos++ = '%';
+       if (flags & PARSE_FLAG_ESCAPE_THEME) {
+               *escpos++ = '{';
+               *escpos++ = '}';
+       }
+
+       if (escpos == esc) {
+               g_string_append(str, text);
+               return;
+       }
+
+       *escpos = '\0'; 
        while (*text != '\0') {
        while (*text != '\0') {
-               if (*text == '%')
-                        g_string_append_c(str, '%');
+               for (escpos = esc; *escpos != '\0'; escpos++) {
+                       if (*text == *escpos) {
+                               g_string_append_c(str, '%');
+                               break;
+                       }
+               }
                g_string_append_c(str, *text);
                text++;
        }
                g_string_append_c(str, *text);
                text++;
        }
@@ -451,7 +480,7 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
 {
        char code, **arglist, *ret;
        GString *str;
 {
        char code, **arglist, *ret;
        GString *str;
-       int need_free;
+       int need_free, chr;
 
        g_return_val_if_fail(cmd != NULL, NULL);
        g_return_val_if_fail(data != NULL, NULL);
 
        g_return_val_if_fail(cmd != NULL, NULL);
        g_return_val_if_fail(data != NULL, NULL);
@@ -463,16 +492,12 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
        code = 0;
        str = g_string_new(NULL);
        while (*cmd != '\0') {
        code = 0;
        str = g_string_new(NULL);
        while (*cmd != '\0') {
-               if (code == '\\'){
-                       switch (*cmd) {
-                       case 't':
-                               g_string_append_c(str, '\t');
-                               break;
-                       case 'n':
-                               g_string_append_c(str, '\n');
-                               break;
-                       default:
-                               g_string_append_c(str, *cmd);
+               if (code == '\\') {
+                       if (*cmd == ';')
+                               g_string_append_c(str, ';');
+                       else {
+                               chr = expand_escape(&cmd);
+                               g_string_append_c(str, chr != -1 ? chr : *cmd);
                        }
                        code = 0;
                } else if (code == '$') {
                        }
                        code = 0;
                } else if (code == '$') {
@@ -482,10 +507,7 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
                                            arglist, &need_free, arg_used,
                                            flags);
                        if (ret != NULL) {
                                            arglist, &need_free, arg_used,
                                            flags);
                        if (ret != NULL) {
-                                if ((flags & PARSE_FLAG_ESCAPE_VARS) == 0)
-                                       g_string_append(str, ret);
-                               else
-                                        gstring_append_escaped(str, ret);
+                                gstring_append_escaped(str, ret, flags);
                                if (need_free) g_free(ret);
                        }
                        code = 0;
                                if (need_free) g_free(ret);
                        }
                        code = 0;
@@ -525,25 +547,28 @@ void eval_special_string(const char *cmd, const char *data,
        /* get a list of all the commands to run */
        orig = start = str = g_strdup(cmd);
        do {
        /* get a list of all the commands to run */
        orig = start = str = g_strdup(cmd);
        do {
-               if (is_split_char(str, start))
+               if (is_split_char(str, start)) {
                        *str++ = '\0';
                        *str++ = '\0';
-               else if (*str != '\0') {
+                        while (*str == ' ') str++;
+               } else if (*str != '\0') {
                        str++;
                        continue;
                }
 
                ret = parse_special_string(start, server, item,
                                           data, &arg_used, 0);
                        str++;
                        continue;
                }
 
                ret = parse_special_string(start, server, item,
                                           data, &arg_used, 0);
-               if (arg_used) arg_used_ever = TRUE;
+               if (*ret != '\0') {
+                       if (arg_used) arg_used_ever = TRUE;
 
 
-               if (strchr(cmdchars, *ret) == NULL) {
-                        /* no command char - let's put it there.. */
-                       char *old = ret;
+                       if (strchr(cmdchars, *ret) == NULL) {
+                               /* no command char - let's put it there.. */
+                               char *old = ret;
 
 
-                       ret = g_strdup_printf("%c%s", *cmdchars, old);
-                       g_free(old);
+                               ret = g_strdup_printf("%c%s", *cmdchars, old);
+                               g_free(old);
+                       }
+                       commands = g_slist_append(commands, ret);
                }
                }
-               commands = g_slist_append(commands, ret);
                start = str;
        } while (*start != '\0');
 
                start = str;
        } while (*start != '\0');
 
@@ -558,8 +583,20 @@ void eval_special_string(const char *cmd, const char *data,
                        ret = g_strconcat(old, " ", data, NULL);
                        g_free(old);
                }
                        ret = g_strconcat(old, " ", data, NULL);
                        g_free(old);
                }
+
+                if (server != NULL)
+                       server_ref(server);
                signal_emit("send command", 3, ret, server, item);
 
                signal_emit("send command", 3, ret, server, item);
 
+               if (server != NULL && !server_unref(server)) {
+                        /* the server was destroyed */
+                       server = NULL;
+                        item = NULL;
+               }
+
+               /* FIXME: window item would need reference counting as well,
+                  eg. "/EVAL win close;say hello" wouldn't work now.. */
+
                g_free(ret);
                commands = g_slist_remove(commands, commands->data);
        }
                g_free(ret);
                commands = g_slist_remove(commands, commands->data);
        }
@@ -571,41 +608,126 @@ void special_history_func_set(SPECIAL_HISTORY_FUNC func)
        history_func = func;
 }
 
        history_func = func;
 }
 
-static void special_vars_signals_do(const char *text, int funccount,
-                                   SIGNAL_FUNC *funcs, int bind)
+static void update_signals_hash(GHashTable **hash, int *signals)
 {
 {
-       char *ret;
-        int need_free;
+       void *signal_id;
+        int arg_type;
+
+       if (*hash == NULL) {
+               *hash = g_hash_table_new((GHashFunc) g_direct_hash,
+                                        (GCompareFunc) g_direct_equal);
+       }
+
+       while (*signals != -1) {
+                signal_id = GINT_TO_POINTER(*signals);
+               arg_type = GPOINTER_TO_INT(g_hash_table_lookup(*hash, signal_id));
+               if (arg_type != 0 && arg_type != signals[1]) {
+                       /* same signal is used for different purposes ..
+                          not sure if this should ever happen, but change
+                          the argument type to none so it will at least
+                          work. */
+                       arg_type = EXPANDO_ARG_NONE;
+               }
+
+               if (arg_type == 0) arg_type = signals[1];
+               g_hash_table_insert(*hash, signal_id,
+                                   GINT_TO_POINTER(arg_type));
+               signals += 2;
+       }
+}
 
 
+static void get_signal_hash(void *signal_id, void *arg_type, int **pos)
+{
+       (*pos)[0] = GPOINTER_TO_INT(signal_id);
+        (*pos)[1] = GPOINTER_TO_INT(arg_type);
+        (*pos) += 2;
+}
+
+static int *get_signals_list(GHashTable *hash)
+{
+       int *signals, *pos;
+
+       if (hash == NULL) {
+               /* no expandos in text - never needs updating */
+               return NULL;
+       }
+
+        pos = signals = g_new(int, g_hash_table_size(hash)*2 + 1);
+       g_hash_table_foreach(hash, (GHFunc) get_signal_hash, &pos);
+        *pos = -1;
+
+       g_hash_table_destroy(hash);
+        return signals;
+
+}
+
+#define TASK_BIND              1
+#define TASK_UNBIND            2
+#define TASK_GET_SIGNALS       3
+
+static int *special_vars_signals_task(const char *text, int funccount,
+                                     SIGNAL_FUNC *funcs, int task)
+{
+        GHashTable *signals;
+       char *expando;
+       int need_free, *expando_signals;
+
+        signals = NULL;
        while (*text != '\0') {
                if (*text == '\\' && text[1] != '\0') {
        while (*text != '\0') {
                if (*text == '\\' && text[1] != '\0') {
+                        /* escape */
                        text += 2;
                } else if (*text == '$' && text[1] != '\0') {
                        text += 2;
                } else if (*text == '$' && text[1] != '\0') {
+                        /* expando */
                        text++;
                        text++;
-                       ret = parse_special((char **) &text, NULL, NULL,
-                                           NULL, &need_free, NULL,
-                                           PARSE_FLAG_GETNAME);
-                       if (ret != NULL) {
-                                if (bind)
-                                       expando_bind(ret, funccount, funcs);
-                                else
-                                       expando_unbind(ret, funccount, funcs);
-                               if (need_free) g_free(ret);
+                       expando = parse_special((char **) &text, NULL, NULL,
+                                               NULL, &need_free, NULL,
+                                               PARSE_FLAG_GETNAME);
+                       if (expando == NULL)
+                               continue;
+
+                       switch (task) {
+                       case TASK_BIND:
+                               expando_bind(expando, funccount, funcs);
+                               break;
+                       case TASK_UNBIND:
+                               expando_unbind(expando, funccount, funcs);
+                               break;
+                       case TASK_GET_SIGNALS:
+                               expando_signals = expando_get_signals(expando);
+                               if (expando_signals != NULL) {
+                                       update_signals_hash(&signals,
+                                                           expando_signals);
+                                        g_free(expando_signals);
+                               }
+                               break;
                        }
                        }
-
+                       if (need_free) g_free(expando);
+               } else {
+                        /* just a char */
+                       text++;
                }
                }
-                else text++;
        }
        }
+
+       if (task == TASK_GET_SIGNALS)
+                return get_signals_list(signals);
+
+        return NULL;
 }
 
 void special_vars_add_signals(const char *text,
                              int funccount, SIGNAL_FUNC *funcs)
 {
 }
 
 void special_vars_add_signals(const char *text,
                              int funccount, SIGNAL_FUNC *funcs)
 {
-        special_vars_signals_do(text, funccount, funcs, TRUE);
+        special_vars_signals_task(text, funccount, funcs, TASK_BIND);
 }
 
 void special_vars_remove_signals(const char *text,
                                 int funccount, SIGNAL_FUNC *funcs)
 {
 }
 
 void special_vars_remove_signals(const char *text,
                                 int funccount, SIGNAL_FUNC *funcs)
 {
-        special_vars_signals_do(text, funccount, funcs, FALSE);
+        special_vars_signals_task(text, funccount, funcs, TASK_UNBIND);
+}
+
+int *special_vars_get_signals(const char *text)
+{
+       return special_vars_signals_task(text, 0, NULL, TASK_GET_SIGNALS);
 }
 }
index af02e12158bf3f14374493bf457e75c9a90c8a9f..11262dadba637d24ad310ffbafd200152faaf797 100644 (file)
@@ -6,6 +6,8 @@
 #define PARSE_FLAG_GETNAME     0x01 /* return argument name instead of it's value */
 #define PARSE_FLAG_ISSET_ANY   0x02 /* arg_used field specifies that at least one of the $variables was non-empty */
 #define PARSE_FLAG_ESCAPE_VARS  0x04 /* if any arguments/variables contain % chars, escape them with another % */
 #define PARSE_FLAG_GETNAME     0x01 /* return argument name instead of it's value */
 #define PARSE_FLAG_ISSET_ANY   0x02 /* arg_used field specifies that at least one of the $variables was non-empty */
 #define PARSE_FLAG_ESCAPE_VARS  0x04 /* if any arguments/variables contain % chars, escape them with another % */
+#define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */
+#define PARSE_FLAG_ONLY_ARGS   0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */
 
 typedef char* (*SPECIAL_HISTORY_FUNC)
        (const char *text, void *item, int *free_ret);
 
 typedef char* (*SPECIAL_HISTORY_FUNC)
        (const char *text, void *item, int *free_ret);
@@ -29,5 +31,7 @@ void special_vars_add_signals(const char *text,
                              int funccount, SIGNAL_FUNC *funcs);
 void special_vars_remove_signals(const char *text,
                                 int funccount, SIGNAL_FUNC *funcs);
                              int funccount, SIGNAL_FUNC *funcs);
 void special_vars_remove_signals(const char *text,
                                 int funccount, SIGNAL_FUNC *funcs);
+/* Returns [<signal id>, EXPANDO_ARG_xxx, <signal id>, ..., -1] */
+int *special_vars_get_signals(const char *text);
 
 #endif
 
 #endif
index 5c09a5b08a2ddc752c51e8a833e741de25b4aa13..eeb465f4743aa7d4078358f0626fcca8a773b3f5 100644 (file)
@@ -12,4 +12,6 @@ time_t createtime;
 int data_level;
 char *hilight_color;
 
 int data_level;
 char *hilight_color;
 
+void (*destroy)(WI_ITEM_REC *item);
+
 #undef STRUCT_SERVER_REC
 #undef STRUCT_SERVER_REC
index 762fc24eb8382722cd91f79ae546fcfd24b55ada..1c3eef824b4a54ebca451ae99ec71d00a71e9e5b 100644 (file)
@@ -126,13 +126,16 @@ void write_buffer_flush(void)
         block_count = 0;
 }
 
         block_count = 0;
 }
 
+static int flush_timeout(void)
+{
+       write_buffer_flush();
+        return 1;
+}
+
 static void read_settings(void)
 {
        int msecs;
 
 static void read_settings(void)
 {
        int msecs;
 
-       if (timeout_tag != -1)
-                g_source_remove(timeout_tag);
-
        write_buffer_flush();
 
        write_buffer_max_blocks = settings_get_int("write_buffer_kb") *
        write_buffer_flush();
 
        write_buffer_max_blocks = settings_get_int("write_buffer_kb") *
@@ -140,9 +143,14 @@ static void read_settings(void)
 
        if (settings_get_int("write_buffer_mins") > 0) {
                 msecs = settings_get_int("write_buffer_mins")*60*1000;
 
        if (settings_get_int("write_buffer_mins") > 0) {
                 msecs = settings_get_int("write_buffer_mins")*60*1000;
-               timeout_tag = g_timeout_add(msecs,
-                                           (GSourceFunc) write_buffer_flush,
-                                           NULL);
+               if (timeout_tag == -1) {
+                       timeout_tag = g_timeout_add(msecs,
+                                                   (GSourceFunc) flush_timeout,
+                                                   NULL);
+               }
+       } else if (timeout_tag != -1) {
+               g_source_remove(timeout_tag);
+                timeout_tag = -1;
        }
 }
 
        }
 }
 
diff --git a/apps/irssi/src/fe-common/core/.cvsignore b/apps/irssi/src/fe-common/core/.cvsignore
new file mode 100644 (file)
index 0000000..8553e9e
--- /dev/null
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
index 798c271c327cbce84b0e7cc1456651373618bd94..43bb1cd1d5759164de64f9f16256b1498c787b59 100644 (file)
@@ -3,10 +3,10 @@ noinst_LIBRARIES = libfe_common_core.a
 include $(top_srcdir)/Makefile.defines.in
 
 INCLUDES = \
 include $(top_srcdir)/Makefile.defines.in
 
 INCLUDES = \
-       $(GLIB_CFLAGS) \
        -I$(top_srcdir)/src -I$(top_srcdir)/src/core/ \
        -I$(top_srcdir)/src -I$(top_srcdir)/src/core/ \
+       $(GLIB_CFLAGS) \
        -DHELPDIR=\""$(silc_helpdir)"\" \
        -DHELPDIR=\""$(silc_helpdir)"\" \
-       -DSYSCONFDIR=\""$(silc_etcdir)"\"
+       -DTHEMESDIR=\""$(silc_etcdir)"\"
 
 libfe_common_core_a_SOURCES = \
        autorun.c \
 
 libfe_common_core_a_SOURCES = \
        autorun.c \
@@ -41,6 +41,7 @@ libfe_common_core_a_SOURCES = \
        fe-windows.c
 
 noinst_HEADERS = \
        fe-windows.c
 
 noinst_HEADERS = \
+       autorun.h \
        command-history.h \
        chat-completion.h \
        completion.h \
        command-history.h \
        chat-completion.h \
        completion.h \
index f49b6c30b12b5508c7c809cba4ab8feb68bb7d5a..9c1050db313cdb123a9bd1814676f64dea7f5d49 100644 (file)
@@ -1,7 +1,7 @@
 /*
  autorun.c : irssi
 
 /*
  autorun.c : irssi
 
-    Copyright (C) 1999-2000 Timo Sirainen
+    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
 
     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
 
 #include "fe-windows.h"
 
 
 #include "fe-windows.h"
 
-static void sig_autorun(void)
+void autorun_startup(void)
 {
        char tmpbuf[1024], *str, *path;
        LINEBUF_REC *buffer = NULL;
        int f, ret, recvlen;
 
 {
        char tmpbuf[1024], *str, *path;
        LINEBUF_REC *buffer = NULL;
        int f, ret, recvlen;
 
-       /* open ~/.silc/startup and run all commands in it */
-       path = g_strdup_printf("%s/.silc/startup", g_get_home_dir());
+       /* open ~/.irssi/startup and run all commands in it */
+       path = g_strdup_printf("%s/startup", get_irssi_dir());
        f = open(path, O_RDONLY);
        g_free(path);
        if (f == -1) {
        f = open(path, O_RDONLY);
        g_free(path);
        if (f == -1) {
@@ -44,19 +44,13 @@ static void sig_autorun(void)
                recvlen = read(f, tmpbuf, sizeof(tmpbuf));
 
                ret = line_split(tmpbuf, recvlen, &str, &buffer);
                recvlen = read(f, tmpbuf, sizeof(tmpbuf));
 
                ret = line_split(tmpbuf, recvlen, &str, &buffer);
-               if (ret > 0) eval_special_string(str, "", active_win->active_server, active_win->active);
+               if (ret > 0) {
+                       eval_special_string(str, "",
+                                           active_win->active_server,
+                                           active_win->active);
+               }
        } while (ret > 0);
        line_split_free(buffer);
 
        close(f);
 }
        } while (ret > 0);
        line_split_free(buffer);
 
        close(f);
 }
-
-void autorun_init(void)
-{
-       signal_add_last("irssi init finished", (SIGNAL_FUNC) sig_autorun);
-}
-
-void autorun_deinit(void)
-{
-       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_autorun);
-}
diff --git a/apps/irssi/src/fe-common/core/autorun.h b/apps/irssi/src/fe-common/core/autorun.h
new file mode 100644 (file)
index 0000000..59d8e84
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __AUTORUN_H
+#define __AUTORUN_H
+
+void autorun_startup(void);
+
+#endif
index 3cbcbf02258f14d52c16cfb80ade3d4739cec21f..a3d92ccde09384fa103e2f07bbf0de2008572247 100644 (file)
@@ -25,6 +25,7 @@
 #include "settings.h"
 
 #include "chatnets.h"
 #include "settings.h"
 
 #include "chatnets.h"
+#include "servers.h"
 #include "servers-setup.h"
 #include "channels.h"
 #include "channels-setup.h"
 #include "servers-setup.h"
 #include "channels.h"
 #include "channels-setup.h"
@@ -156,6 +157,16 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
        }
 }
 
        }
 }
 
+static void sig_message_join(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *address)
+{
+       CHANNEL_REC *chanrec;
+
+       chanrec = channel_find(server, channel);
+       if (chanrec != NULL)
+               CHANNEL_LAST_MSG_ADD(chanrec, nick, FALSE);
+}
+
 static void sig_message_private(SERVER_REC *server, const char *msg,
                                const char *nick, const char *address)
 {
 static void sig_message_private(SERVER_REC *server, const char *msg,
                                const char *nick, const char *address)
 {
@@ -382,7 +393,7 @@ static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
                /* remove non alnum chars from nick */
                in = rec->nick; out = str;
                while (*in != '\0') {
                /* remove non alnum chars from nick */
                in = rec->nick; out = str;
                while (*in != '\0') {
-                       if (isalnum(*in))
+                       if (i_isalnum(*in))
                                *out++ = *in;
                         in++;
                }
                                *out++ = *in;
                         in++;
                }
@@ -543,7 +554,8 @@ static void complete_window_nicks(GList **list, WINDOW_REC *window,
 }
 
 static void sig_complete_word(GList **list, WINDOW_REC *window,
 }
 
 static void sig_complete_word(GList **list, WINDOW_REC *window,
-                             const char *word, const char *linestart)
+                             const char *word, const char *linestart,
+                             int *want_space)
 {
        SERVER_REC *server;
        CHANNEL_REC *channel;
 {
        SERVER_REC *server;
        CHANNEL_REC *channel;
@@ -559,7 +571,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
        if (server == NULL && servers != NULL)
                server = servers->data;
 
        if (server == NULL && servers != NULL)
                server = servers->data;
 
-       if (server != NULL && server->ischannel(word)) {
+       if (server != NULL && server_ischannel(server, word)) {
                /* probably completing a channel name */
                *list = completion_get_channels(window->active_server, word);
                 return;
                /* probably completing a channel name */
                *list = completion_get_channels(window->active_server, word);
                 return;
@@ -590,7 +602,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
        } else if (channel != NULL) {
                /* nick completion .. we could also be completing a nick
                   after /MSG from nicks in channel */
        } else if (channel != NULL) {
                /* nick completion .. we could also be completing a nick
                   after /MSG from nicks in channel */
-                complete_window_nicks(list, window, word, linestart);
+               complete_window_nicks(list, window, word, linestart);
        }
 
        if (*list != NULL) signal_stop();
        }
 
        if (*list != NULL) signal_stop();
@@ -634,6 +646,41 @@ static void sig_complete_msg(GList **list, WINDOW_REC *window,
        if (*list != NULL) signal_stop();
 }
 
        if (*list != NULL) signal_stop();
 }
 
+static void sig_erase_complete_msg(WINDOW_REC *window, const char *word,
+                                  const char *line)
+{
+       SERVER_REC *server;
+       MODULE_SERVER_REC *mserver;
+        GSList *tmp;
+
+       server = line_get_server(line);
+       if (server == NULL){
+               server = window->active_server;
+               if (server == NULL)
+                        return;
+       }
+
+       if (*word == '\0')
+               return;
+
+        /* check from global list */
+       completion_last_message_remove(word);
+
+       /* check from server specific list */
+       if (server != NULL) {
+               mserver = MODULE_DATA(server);
+               for (tmp = mserver->lastmsgs; tmp != NULL; tmp = tmp->next) {
+                       LAST_MSG_REC *rec = tmp->data;
+
+                       if (g_strcasecmp(rec->nick, word) == 0) {
+                               last_msg_destroy(&mserver->lastmsgs, rec);
+                                break;
+                       }
+               }
+
+       }
+}
+
 GList *completion_get_chatnets(const char *word)
 {
        GList *list;
 GList *completion_get_chatnets(const char *word)
 {
        GList *list;
@@ -688,11 +735,30 @@ static void sig_complete_connect(GList **list, WINDOW_REC *window,
        if (*list != NULL) signal_stop();
 }
 
        if (*list != NULL) signal_stop();
 }
 
+static void sig_complete_topic(GList **list, WINDOW_REC *window,
+                              const char *word, const char *line,
+                              int *want_space)
+{
+       const char *topic;
+
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+
+       if (*word == '\0' && IS_CHANNEL(window->active)) {
+               topic = CHANNEL(window->active)->topic;
+               if (topic != NULL) {
+                       *list = g_list_append(NULL, g_strdup(topic));
+                        signal_stop();
+               }
+       }
+}
+
 /* expand \n, \t and \\ */
 static char *expand_escapes(const char *line, SERVER_REC *server,
                            WI_ITEM_REC *item)
 {
        char *ptr, *ret;
 /* expand \n, \t and \\ */
 static char *expand_escapes(const char *line, SERVER_REC *server,
                            WI_ITEM_REC *item)
 {
        char *ptr, *ret;
+        int chr;
 
        ret = ptr = g_malloc(strlen(line)+1);
        for (; *line != '\0'; line++) {
 
        ret = ptr = g_malloc(strlen(line)+1);
        for (; *line != '\0'; line++) {
@@ -707,25 +773,23 @@ static char *expand_escapes(const char *line, SERVER_REC *server,
                        break;
                }
 
                        break;
                }
 
-               switch (*line) {
-               case 'n':
+               chr = expand_escape(&line);
+               if (chr == '\r' || chr == '\n') {
                        /* newline .. we need to send another "send text"
                           event to handle it (or actually the text before
                           the newline..) */
                        /* newline .. we need to send another "send text"
                           event to handle it (or actually the text before
                           the newline..) */
-                       *ptr = '\0';
-                       signal_emit("send text", 3, ret, server, item);
-                       ptr = ret;
-                       break;
-               case 't':
-                       *ptr++ = '\t';
-                       break;
-               case '\\':
-                       *ptr++ = '\\';
-                       break;
-               default:
+                       if (ret != ptr) {
+                               *ptr = '\0';
+                               signal_emit("send text", 3, ret, server, item);
+                               ptr = ret;
+                       }
+               } else if (chr != -1) {
+                        /* escaping went ok */
+                       *ptr++ = chr;
+               } else {
+                        /* unknown escape, add it as-is */
                        *ptr++ = '\\';
                        *ptr++ = *line;
                        *ptr++ = '\\';
                        *ptr++ = *line;
-                       break;
                }
        }
 
                }
        }
 
@@ -733,48 +797,62 @@ static char *expand_escapes(const char *line, SERVER_REC *server,
        return ret;
 }
 
        return ret;
 }
 
-static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+static char *auto_complete(CHANNEL_REC *channel, const char *line)
 {
 {
-       CHANNEL_REC *channel;
        GList *comp;
        GList *comp;
-       char *line, *str, *ptr, comp_char;
+       const char *p;
+        char *nick, *ret;
+
+       p = strstr(line, completion_char);
+       if (p == NULL)
+               return NULL;
+
+        nick = g_strndup(line, (int) (p-line));
+
+        ret = NULL;
+       if (nicklist_find(channel, nick) == NULL) {
+                /* not an exact match, use the first possible completion */
+               comp = completion_channel_nicks(channel, nick, NULL);
+               if (comp != NULL) {
+                       ret = g_strconcat(comp->data, p, NULL);
+                       g_list_foreach(comp, (GFunc) g_free, NULL);
+                       g_list_free(comp);
+               }
+       }
+
+       g_free(nick);
+
+        return ret;
+}
+
+static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       char *line, *str;
 
        g_return_if_fail(data != NULL);
        if (item == NULL) return;
 
        line = settings_get_bool("expand_escapes") ?
                expand_escapes(data, server, item) : g_strdup(data);
 
        g_return_if_fail(data != NULL);
        if (item == NULL) return;
 
        line = settings_get_bool("expand_escapes") ?
                expand_escapes(data, server, item) : g_strdup(data);
-       comp_char = *completion_char;
 
        /* check for automatic nick completion */
 
        /* check for automatic nick completion */
-        ptr = NULL;
-       comp = NULL;
-       channel = CHANNEL(item);
-
-       if (completion_auto && channel != NULL && comp_char != '\0') {
-               ptr = strchr(line, comp_char);
-               if (ptr != NULL) {
-                       *ptr++ = '\0';
-                       if (nicklist_find(channel, line) == NULL) {
-                               comp = completion_channel_nicks(channel,
-                                                               line, NULL);
-                       }
+       if (completion_auto && IS_CHANNEL(item)) {
+               str = auto_complete(CHANNEL(item), line);
+               if (str != NULL) {
+                       g_free(line);
+                        line = str;
                }
        }
 
                }
        }
 
-       str = g_strdup_printf(ptr == NULL ? "%s %s" : "%s %s%c%s", item->name,
-                             comp != NULL ? (char *) comp->data : line,
-                             comp_char, ptr);
+       str = g_strdup_printf(IS_CHANNEL(item) ? "-channel %s %s" :
+                             IS_QUERY(item) ? "-nick %s %s" : "%s %s",
+                             item->name, line);
+
        signal_emit("command msg", 3, str, server, item);
 
        g_free(str);
        g_free(line);
 
        signal_emit("command msg", 3, str, server, item);
 
        g_free(str);
        g_free(line);
 
-       if (comp != NULL) {
-               g_list_foreach(comp, (GFunc) g_free, NULL);
-               g_list_free(comp);
-       }
-
        signal_stop();
 }
 
        signal_stop();
 }
 
@@ -811,6 +889,11 @@ static void read_settings(void)
        cmdchars = settings_get_str("cmdchars");
        completion_auto = settings_get_bool("completion_auto");
        completion_strict = settings_get_bool("completion_strict");
        cmdchars = settings_get_str("cmdchars");
        completion_auto = settings_get_bool("completion_auto");
        completion_strict = settings_get_bool("completion_strict");
+
+       if (*completion_char == '\0') {
+                /* this would break.. */
+               completion_auto = FALSE;
+       }
 }
 
 void chat_completion_init(void)
 }
 
 void chat_completion_init(void)
@@ -826,9 +909,14 @@ void chat_completion_init(void)
        read_settings();
        signal_add("complete word", (SIGNAL_FUNC) sig_complete_word);
        signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
        read_settings();
        signal_add("complete word", (SIGNAL_FUNC) sig_complete_word);
        signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
+       signal_add("complete command query", (SIGNAL_FUNC) sig_complete_msg);
+       signal_add("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg);
+       signal_add("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg);
        signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
        signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect);
        signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
        signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect);
+       signal_add("complete command topic", (SIGNAL_FUNC) sig_complete_topic);
        signal_add("message public", (SIGNAL_FUNC) sig_message_public);
        signal_add("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_add("message join", (SIGNAL_FUNC) sig_message_join);
        signal_add("message private", (SIGNAL_FUNC) sig_message_private);
        signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public);
        signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
        signal_add("message private", (SIGNAL_FUNC) sig_message_private);
        signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public);
        signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
@@ -847,9 +935,14 @@ void chat_completion_deinit(void)
 
        signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
        signal_remove("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
 
        signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
        signal_remove("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
+       signal_remove("complete command query", (SIGNAL_FUNC) sig_complete_msg);
+       signal_remove("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg);
+       signal_remove("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg);
        signal_remove("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
        signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect);
        signal_remove("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
        signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect);
+       signal_remove("complete command topic", (SIGNAL_FUNC) sig_complete_topic);
        signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
        signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
        signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
        signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
        signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
        signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
        signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
        signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
index cdf4b5a5b70663173beeb87ced14a98dca66cb55..70f68594296dc6faf28a7a034454459658d1ab67 100644 (file)
 #include "fe-windows.h"
 #include "window-items.h"
 
 #include "fe-windows.h"
 #include "window-items.h"
 
+#include "command-history.h"
+
 /* command history */
 /* command history */
-static GList *history, *history_pos;
-static int history_lines, history_over_counter;
+static HISTORY_REC *global_history;
 static int window_history;
 static int window_history;
+static GSList *histories;
 
 
-void command_history_add(WINDOW_REC *window, const char *text)
+void command_history_add(HISTORY_REC *history, const char *text)
 {
 {
-       GList **phistory, *link;
-       int *phistory_lines;
+       GList *link;
 
 
+       g_return_if_fail(history != NULL);
        g_return_if_fail(text != NULL);
 
        g_return_if_fail(text != NULL);
 
-       if (window_history) {
-               /* window specific command history */
-               phistory = &window->history;
-               phistory_lines = &window->history_lines;
-       } else {
-               /* global command history */
-               phistory = &history;
-               phistory_lines = &history_lines;
-       }
+       link = g_list_last(history->list);
+       if (link != NULL && strcmp(link->data, text) == 0)
+         return; /* same as previous entry */
 
 
-       if (settings_get_int("max_command_history") < 1 || *phistory_lines < settings_get_int("max_command_history"))
-               (*phistory_lines)++;
+       if (settings_get_int("max_command_history") < 1 || 
+           history->lines < settings_get_int("max_command_history"))
+               history->lines++;
        else {
        else {
-               link = *phistory;
+               link = history->list;
                g_free(link->data);
                g_free(link->data);
-               *phistory = g_list_remove_link(*phistory, link);
+               history->list = g_list_remove_link(history->list, link);
                g_list_free_1(link);
        }
 
                g_list_free_1(link);
        }
 
-       *phistory = g_list_append(*phistory, g_strdup(text));
+       history->list = g_list_append(history->list, g_strdup(text));
 }
 
 }
 
-const char *command_history_prev(WINDOW_REC *window, const char *text)
+HISTORY_REC *command_history_find(HISTORY_REC *history)
 {
 {
-       GList *pos, **phistory_pos;
-        int *phistory_over_counter;
+       GSList *tmp;
+       tmp = g_slist_find(histories, history);
 
 
-       if (window_history) {
-               phistory_pos = &window->history_pos;
-               phistory_over_counter = &window->history_over_counter;
-       } else {
-               phistory_pos = &history_pos;
-               phistory_over_counter = &history_over_counter;
+       if (tmp == NULL)
+               return NULL;
+       else
+               return tmp->data;
+}
+
+HISTORY_REC *command_history_find_name(const char *name)
+{
+       GSList *tmp;
+
+       if (name == NULL)
+               return NULL;
+
+       for (tmp = histories; tmp != NULL; tmp = tmp->next) {
+               HISTORY_REC *rec = tmp->data;
+               
+               if (rec->name != NULL && g_strcasecmp(rec->name, name) == 0)
+                       return rec;
        }
        }
+       
+       return NULL;
+}
+
+HISTORY_REC *command_history_current(WINDOW_REC *window)
+{
+       HISTORY_REC *rec;
+
+       if (window == NULL)
+               return global_history;
 
 
-       pos = *phistory_pos;
-       if (*phistory_pos != NULL) {
-               *phistory_pos = (*phistory_pos)->prev;
-               if (*phistory_pos == NULL)
-                        (*phistory_over_counter)++;
+       if (window_history)
+               return window->history;
+
+       rec = command_history_find_name(window->history_name);
+       if (rec != NULL)
+               return rec;
+
+       return global_history;
+}
+
+const char *command_history_prev(WINDOW_REC *window, const char *text)
+{
+       HISTORY_REC *history;
+       GList *pos;
+
+       history = command_history_current(window);
+       pos = history->pos;
+
+       if (pos != NULL) {
+               history->pos = history->pos->prev;
+               if (history->pos == NULL)
+                        history->over_counter++;
        } else {
        } else {
-               *phistory_pos = g_list_last(window_history ?
-                                           window->history : history);
+               history->pos = g_list_last(history->list);
        }
 
        if (*text != '\0' &&
            (pos == NULL || strcmp(pos->data, text) != 0)) {
                /* save the old entry to history */
        }
 
        if (*text != '\0' &&
            (pos == NULL || strcmp(pos->data, text) != 0)) {
                /* save the old entry to history */
-               command_history_add(window, text);
+               command_history_add(history, text);
        }
 
        }
 
-       return *phistory_pos == NULL ? "" : (*phistory_pos)->data;
+       return history->pos == NULL ? "" : history->pos->data;
 }
 
 const char *command_history_next(WINDOW_REC *window, const char *text)
 {
 }
 
 const char *command_history_next(WINDOW_REC *window, const char *text)
 {
-       GList *pos, **phistory_pos;
-        int *phistory_over_counter;
-
-       if (window_history) {
-               phistory_pos = &window->history_pos;
-               phistory_over_counter = &window->history_over_counter;
-       } else {
-               phistory_pos = &history_pos;
-               phistory_over_counter = &history_over_counter;
-       }
+       HISTORY_REC *history;
+       GList *pos;
 
 
-       pos = *phistory_pos;
+       history = command_history_current(window);
+       pos = history->pos; 
 
        if (pos != NULL)
 
        if (pos != NULL)
-               *phistory_pos = (*phistory_pos)->next;
-       else if (*phistory_over_counter > 0) {
-               (*phistory_over_counter)--;
-               *phistory_pos = window_history ? window->history : history;
+               history->pos = history->pos->next;
+       else if (history->over_counter > 0) {
+               history->over_counter--;
+               history->pos = history->list;
        }
 
        if (*text != '\0' &&
            (pos == NULL || strcmp(pos->data, text) != 0)) {
                /* save the old entry to history */
        }
 
        if (*text != '\0' &&
            (pos == NULL || strcmp(pos->data, text) != 0)) {
                /* save the old entry to history */
-               command_history_add(window, text);
+               command_history_add(history, text);
        }
        }
-       return *phistory_pos == NULL ? "" : (*phistory_pos)->data;
+       return history->pos == NULL ? "" : history->pos->data;
+}
+
+void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data)
+{
+       history->over_counter = 0;
+       history->pos = NULL;
 }
 
 void command_history_clear_pos(WINDOW_REC *window)
 {
 }
 
 void command_history_clear_pos(WINDOW_REC *window)
 {
-       window->history_over_counter = 0;
-       window->history_pos = NULL;
-        history_over_counter = 0;
-       history_pos = NULL;
+       g_slist_foreach(histories, 
+                      (GFunc) command_history_clear_pos_func, NULL);
+}
+
+HISTORY_REC *command_history_create(const char *name)
+{
+       HISTORY_REC *rec;
+       
+       rec = g_new0(HISTORY_REC, 1);
+       
+       if (name != NULL)
+               rec->name = g_strdup(name);
+
+       histories = g_slist_append(histories, rec);
+       
+       return rec;
+}
+
+void command_history_destroy(HISTORY_REC *history)
+{
+       g_return_if_fail(history != NULL);
+
+       /* history->refcount should be 0 here, or somthing is wrong... */
+       g_return_if_fail(history->refcount == 0);
+
+       histories = g_slist_remove(histories, history);
+
+       g_list_foreach(history->list, (GFunc) g_free, NULL);
+       g_list_free(history->list);
+
+       g_free_not_null(history->name);
+       g_free(history);
+}
+
+void command_history_link(const char *name)
+{
+       HISTORY_REC *rec;
+       rec = command_history_find_name(name);
+
+       if (rec == NULL)
+               rec = command_history_create(name);
+
+       rec->refcount++;
+}
+
+void command_history_unlink(const char *name)
+{
+       HISTORY_REC *rec;
+       rec = command_history_find_name(name);
+
+       if (rec == NULL)
+               return;
+
+       if (--(rec->refcount) <= 0)
+               command_history_destroy(rec);
+}
+
+static void sig_window_created(WINDOW_REC *window, int automatic)
+{
+       window->history = command_history_create(NULL);
 }
 
 static void sig_window_destroyed(WINDOW_REC *window)
 {
 }
 
 static void sig_window_destroyed(WINDOW_REC *window)
 {
-       g_list_foreach(window->history, (GFunc) g_free, NULL);
-       g_list_free(window->history);
+       command_history_unlink(window->history_name);
+       command_history_destroy(window->history);
+       g_free_not_null(window->history_name);
+}
+
+static void sig_window_history_changed(WINDOW_REC *window, const char *oldname)
+{
+       command_history_link(window->history_name);
+       command_history_unlink(oldname);
 }
 
 static char *special_history_func(const char *text, void *item, int *free_ret)
 {
        WINDOW_REC *window;
 }
 
 static char *special_history_func(const char *text, void *item, int *free_ret)
 {
        WINDOW_REC *window;
+       HISTORY_REC *history;
        GList *tmp;
         char *findtext, *ret;
 
        GList *tmp;
         char *findtext, *ret;
 
@@ -148,8 +246,8 @@ static char *special_history_func(const char *text, void *item, int *free_ret)
        findtext = g_strdup_printf("*%s*", text);
        ret = NULL;
 
        findtext = g_strdup_printf("*%s*", text);
        ret = NULL;
 
-       tmp = window_history ? window->history : history;
-       for (; tmp != NULL; tmp = tmp->next) {
+       history = command_history_current(window);
+       for (tmp = history->list; tmp != NULL; tmp = tmp->next) {
                const char *line = tmp->data;
 
                if (match_wildcards(findtext, line)) {
                const char *line = tmp->data;
 
                if (match_wildcards(findtext, line)) {
@@ -174,19 +272,21 @@ void command_history_init(void)
 
        special_history_func_set(special_history_func);
 
 
        special_history_func_set(special_history_func);
 
-       history_lines = 0; history_over_counter = 0;
-       history = NULL; history_pos = NULL;
+       global_history = command_history_create(NULL);
 
        read_settings();
 
        read_settings();
+       signal_add("window created", (SIGNAL_FUNC) sig_window_created);
        signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
        signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_add("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void command_history_deinit(void)
 {
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void command_history_deinit(void)
 {
+       signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
        signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
        signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_remove("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 
-       g_list_foreach(history, (GFunc) g_free, NULL);
-       g_list_free(history);
+       command_history_destroy(global_history);
 }
 }
index 9f37f069296dd536e9bc1535c5b9646faea3e35e..2ca312e19f6016f6f244cd3d0998d40893d7c95f 100644 (file)
@@ -1,16 +1,35 @@
 #ifndef __COMMAND_HISTORY_H
 #define __COMMAND_HISTORY_H
 
 #ifndef __COMMAND_HISTORY_H
 #define __COMMAND_HISTORY_H
 
-#include "fe-windows.h"
+#include "common.h"
+
+typedef struct {
+       char *name;
+
+       GList *list, *pos;
+       int lines, over_counter;
+
+       int refcount;
+} HISTORY_REC;
+
+HISTORY_REC *command_history_find(HISTORY_REC *history);
+HISTORY_REC *command_history_find_name(const char *name);
+
+HISTORY_REC *command_history_current(WINDOW_REC *window);
 
 void command_history_init(void);
 void command_history_deinit(void);
 
 
 void command_history_init(void);
 void command_history_deinit(void);
 
-void command_history_add(WINDOW_REC *window, const char *text, int prepend);
+void command_history_add(HISTORY_REC *window, const char *text);
 
 const char *command_history_prev(WINDOW_REC *window, const char *text);
 const char *command_history_next(WINDOW_REC *window, const char *text);
 
 void command_history_clear_pos(WINDOW_REC *window);
 
 
 const char *command_history_prev(WINDOW_REC *window, const char *text);
 const char *command_history_next(WINDOW_REC *window, const char *text);
 
 void command_history_clear_pos(WINDOW_REC *window);
 
+HISTORY_REC *command_history_create(const char *name);
+void command_history_destroy(HISTORY_REC *history);
+void command_history_link(const char *name);
+void command_history_unlink(const char *name);
+
 #endif
 #endif
index cd86ab4f7438b8a6b2375b4ffc0408eff64dfbb8..f10fbb6dec0414866ecd35da3df8d0607e1eae9f 100644 (file)
@@ -41,7 +41,7 @@ static int last_want_space, last_line_pos;
         ((c) == ',')
 
 #define isseparator(c) \
         ((c) == ',')
 
 #define isseparator(c) \
-       (isspace((int) (c)) || isseparator_notspace(c))
+       (i_isspace(c) || isseparator_notspace(c))
 
 void chat_completion_init(void);
 void chat_completion_deinit(void);
 
 void chat_completion_init(void);
 void chat_completion_deinit(void);
@@ -113,27 +113,28 @@ static void free_completions(void)
 }
 
 /* manual word completion - called when TAB is pressed */
 }
 
 /* manual word completion - called when TAB is pressed */
-char *word_complete(WINDOW_REC *window, const char *line, int *pos)
+char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase)
 {
        static int startpos = 0, wordlen = 0;
 {
        static int startpos = 0, wordlen = 0;
+        int old_startpos, old_wordlen;
 
        GString *result;
        char *word, *wordstart, *linestart, *ret;
 
        GString *result;
        char *word, *wordstart, *linestart, *ret;
-       int want_space;
+       int continue_complete, want_space;
 
        g_return_val_if_fail(line != NULL, NULL);
        g_return_val_if_fail(pos != NULL, NULL);
 
 
        g_return_val_if_fail(line != NULL, NULL);
        g_return_val_if_fail(pos != NULL, NULL);
 
-       if (complist != NULL && *pos == last_line_pos &&
-           strcmp(line, last_line) == 0) {
-               /* complete from old list */
-               complist = complist->next != NULL ? complist->next :
-                       g_list_first(complist);
-               want_space = last_want_space;
-       } else {
-               /* get new completion list */
-               free_completions();
+       continue_complete = complist != NULL && *pos == last_line_pos &&
+               strcmp(line, last_line) == 0;
+
+       old_startpos = startpos;
+       old_wordlen = wordlen;
 
 
+       if (!erase && continue_complete) {
+               word = NULL;
+                linestart = NULL;
+       } else {
                /* get the word we want to complete */
                word = get_word_at(line, *pos, &wordstart);
                startpos = (int) (wordstart-line);
                /* get the word we want to complete */
                word = get_word_at(line, *pos, &wordstart);
                startpos = (int) (wordstart-line);
@@ -156,7 +157,8 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos)
                   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. */
                   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 (*pos > 0 && line[*pos-1] == ' ') {
+               if (!erase && *pos > 0 && line[*pos-1] == ' ' &&
+                   (*linestart == '\0' || wordstart[-1] != ' ')) {
                        char *old;
 
                         old = linestart;
                        char *old;
 
                         old = linestart;
@@ -171,14 +173,38 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos)
                        wordlen = 0;
                }
 
                        wordlen = 0;
                }
 
+       }
+
+       if (erase) {
+               signal_emit("complete erase", 3, window, word, linestart);
+
+               if (!continue_complete)
+                        return NULL;
+
+                /* jump to next completion */
+               word = NULL;
+               linestart = NULL;
+                startpos = old_startpos;
+               wordlen = old_wordlen;
+       }
+
+       if (continue_complete) {
+               /* complete from old list */
+               complist = complist->next != NULL ? complist->next :
+                       g_list_first(complist);
+               want_space = last_want_space;
+       } else {
+               /* get new completion list */
+               free_completions();
+
                want_space = TRUE;
                signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
                last_want_space = want_space;
                want_space = TRUE;
                signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
                last_want_space = want_space;
-
-               g_free(linestart);
-               g_free(word);
        }
 
        }
 
+       g_free(linestart);
+       g_free(word);
+
        if (complist == NULL)
                return NULL;
 
        if (complist == NULL)
                return NULL;
 
@@ -207,7 +233,14 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos)
        return ret;
 }
 
        return ret;
 }
 
-GList *list_add_file(GList *list, const char *name)
+#define IS_CURRENT_DIR(dir) \
+        ((dir)[0] == '.' && ((dir)[1] == '\0' || (dir)[1] == G_DIR_SEPARATOR))
+
+#define USE_DEFAULT_PATH(path, default_path) \
+       ((!g_path_is_absolute(path) || IS_CURRENT_DIR(path)) && \
+        default_path != NULL)
+
+GList *list_add_file(GList *list, const char *name, const char *default_path)
 {
        struct stat statbuf;
        char *fname;
 {
        struct stat statbuf;
        char *fname;
@@ -215,6 +248,11 @@ GList *list_add_file(GList *list, const char *name)
        g_return_val_if_fail(name != NULL, NULL);
 
        fname = convert_home(name);
        g_return_val_if_fail(name != NULL, NULL);
 
        fname = convert_home(name);
+       if (USE_DEFAULT_PATH(fname, default_path)) {
+                g_free(fname);
+               fname = g_strconcat(default_path, G_DIR_SEPARATOR_S,
+                                   name, NULL);
+       }
        if (stat(fname, &statbuf) == 0) {
                list = g_list_append(list, !S_ISDIR(statbuf.st_mode) ? g_strdup(name) :
                                     g_strconcat(name, G_DIR_SEPARATOR_S, NULL));
        if (stat(fname, &statbuf) == 0) {
                list = g_list_append(list, !S_ISDIR(statbuf.st_mode) ? g_strdup(name) :
                                     g_strconcat(name, G_DIR_SEPARATOR_S, NULL));
@@ -224,7 +262,7 @@ GList *list_add_file(GList *list, const char *name)
        return list;
 }
 
        return list;
 }
 
-GList *filename_complete(const char *path)
+GList *filename_complete(const char *path, const char *default_path)
 {
         GList *list;
        DIR *dirp;
 {
         GList *list;
        DIR *dirp;
@@ -238,17 +276,31 @@ GList *filename_complete(const char *path)
 
        /* get directory part of the path - expand ~/ */
        realpath = convert_home(path);
 
        /* get directory part of the path - expand ~/ */
        realpath = convert_home(path);
-       dir = g_dirname(realpath);
-       g_free(realpath);
+       if (USE_DEFAULT_PATH(realpath, default_path)) {
+                g_free(realpath);
+               realpath = g_strconcat(default_path, G_DIR_SEPARATOR_S,
+                                      path, NULL);
+       }
 
        /* open directory for reading */
 
        /* open directory for reading */
+       dir = g_dirname(realpath);
        dirp = opendir(dir);
        g_free(dir);
        dirp = opendir(dir);
        g_free(dir);
-       if (dirp == NULL) return NULL;
+        g_free(realpath);
+
+       if (dirp == NULL)
+               return NULL;
 
        dir = g_dirname(path);
 
        dir = g_dirname(path);
-       if (*dir == G_DIR_SEPARATOR && dir[1] == '\0')
-               *dir = '\0'; /* completing file in root directory */
+       if (*dir == G_DIR_SEPARATOR && dir[1] == '\0') {
+                /* completing file in root directory */
+               *dir = '\0';
+       } else if (IS_CURRENT_DIR(dir) && !IS_CURRENT_DIR(path)) {
+               /* completing file in default_path
+                  (path not set, and leave it that way) */
+               g_free_and_null(dir);
+       }
+
        basename = g_basename(path);
        len = strlen(basename);
 
        basename = g_basename(path);
        len = strlen(basename);
 
@@ -264,14 +316,15 @@ GList *filename_complete(const char *path)
                }
 
                if (len == 0 || strncmp(dp->d_name, basename, len) == 0) {
                }
 
                if (len == 0 || strncmp(dp->d_name, basename, len) == 0) {
-                       name = g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name);
-                       list = list_add_file(list, name);
+                       name = dir == NULL ? g_strdup(dp->d_name) :
+                               g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name);
+                       list = list_add_file(list, name, default_path);
                        g_free(name);
                }
        }
        closedir(dirp);
 
                        g_free(name);
                }
        }
        closedir(dirp);
 
-       g_free(dir);
+       g_free_not_null(dir);
         return list;
 }
 
         return list;
 }
 
@@ -332,11 +385,11 @@ static GList *completion_get_aliases(const char *alias, char cmdchar)
 
        /* get list of aliases from mainconfig */
        node = iconfig_node_traverse("aliases", FALSE);
 
        /* get list of aliases from mainconfig */
        node = iconfig_node_traverse("aliases", FALSE);
-       tmp = node == NULL ? NULL : node->value;
+       tmp = node == NULL ? NULL : config_node_first(node->value);
 
        len = strlen(alias);
        complist = NULL;
 
        len = strlen(alias);
        complist = NULL;
-       for (; tmp != NULL; tmp = tmp->next) {
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                CONFIG_NODE *node = tmp->data;
 
                if (node->type != NODE_TYPE_KEY)
                CONFIG_NODE *node = tmp->data;
 
                if (node->type != NODE_TYPE_KEY)
@@ -461,7 +514,7 @@ static char *line_get_command(const char *line, char **args, int aliases)
                } else {
                        checkcmd = g_strndup(line, (int) (ptr-line));
 
                } else {
                        checkcmd = g_strndup(line, (int) (ptr-line));
 
-                       while (isspace(*ptr)) ptr++;
+                       while (i_isspace(*ptr)) ptr++;
                        cmdargs = ptr;
                }
 
                        cmdargs = ptr;
                }
 
@@ -483,6 +536,8 @@ static char *line_get_command(const char *line, char **args, int aliases)
                *args = (char *) cmdargs;
        } while (ptr != NULL);
 
                *args = (char *) cmdargs;
        } while (ptr != NULL);
 
+        if (cmd != NULL)
+               g_strdown(cmd);
        return cmd;
 }
 
        return cmd;
 }
 
@@ -502,7 +557,8 @@ static char *expand_aliases(const char *line)
 }
 
 static void sig_complete_word(GList **list, WINDOW_REC *window,
 }
 
 static void sig_complete_word(GList **list, WINDOW_REC *window,
-                             const char *word, const char *linestart, int *want_space)
+                             const char *word, const char *linestart,
+                             int *want_space)
 {
        const char *newword, *cmdchars;
        char *signal, *cmd, *args, *line;
 {
        const char *newword, *cmdchars;
        char *signal, *cmd, *args, *line;
@@ -522,7 +578,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
 
        /* command completion? */
        cmdchars = settings_get_str("cmdchars");
 
        /* command completion? */
        cmdchars = settings_get_str("cmdchars");
-       if (strchr(cmdchars, *word) && *linestart == '\0') {
+       if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) {
                /* complete /command */
                *list = completion_get_commands(word+1, *word);
 
                /* complete /command */
                *list = completion_get_commands(word+1, *word);
 
@@ -578,6 +634,39 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
        g_free(line);
 }
 
        g_free(line);
 }
 
+static void sig_complete_erase(WINDOW_REC *window, const char *word,
+                              const char *linestart)
+{
+       const char *cmdchars;
+        char *line, *cmd, *args, *signal;
+
+       if (*linestart == '\0')
+               return;
+
+        /* we only want to check for commands */
+       cmdchars = settings_get_str("cmdchars");
+        cmdchars = strchr(cmdchars, *linestart);
+       if (cmdchars == NULL)
+               return;
+
+        /* check if there's aliases */
+       line = linestart[1] == *cmdchars ? g_strdup(linestart+2) :
+               expand_aliases(linestart+1);
+
+       cmd = line_get_command(line, &args, FALSE);
+       if (cmd == NULL) {
+               g_free(line);
+               return;
+       }
+
+       signal = g_strconcat("complete erase command ", cmd, NULL);
+       signal_emit(signal, 3, window, word, args);
+
+        g_free(signal);
+       g_free(cmd);
+       g_free(line);
+}
+
 static void sig_complete_set(GList **list, WINDOW_REC *window,
                             const char *word, const char *line, int *want_space)
 {
 static void sig_complete_set(GList **list, WINDOW_REC *window,
                             const char *word, const char *line, int *want_space)
 {
@@ -614,7 +703,7 @@ static void sig_complete_filename(GList **list, WINDOW_REC *window,
 
        if (*line != '\0') return;
 
 
        if (*line != '\0') return;
 
-       *list = filename_complete(word);
+       *list = filename_complete(word, NULL);
        if (*list != NULL) {
                *want_space = FALSE;
                signal_stop();
        if (*list != NULL) {
                *want_space = FALSE;
                signal_stop();
@@ -652,10 +741,10 @@ void completion_init(void)
        chat_completion_init();
 
        signal_add_first("complete word", (SIGNAL_FUNC) sig_complete_word);
        chat_completion_init();
 
        signal_add_first("complete word", (SIGNAL_FUNC) sig_complete_word);
+       signal_add_first("complete erase", (SIGNAL_FUNC) sig_complete_erase);
        signal_add("complete command set", (SIGNAL_FUNC) sig_complete_set);
        signal_add("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
        signal_add("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
        signal_add("complete command set", (SIGNAL_FUNC) sig_complete_set);
        signal_add("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
        signal_add("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
-       signal_add("complete command run", (SIGNAL_FUNC) sig_complete_filename);
        signal_add("complete command save", (SIGNAL_FUNC) sig_complete_filename);
        signal_add("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
        signal_add("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
        signal_add("complete command save", (SIGNAL_FUNC) sig_complete_filename);
        signal_add("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
        signal_add("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
@@ -670,10 +759,10 @@ void completion_deinit(void)
        chat_completion_deinit();
 
        signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
        chat_completion_deinit();
 
        signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
+       signal_remove("complete erase", (SIGNAL_FUNC) sig_complete_erase);
        signal_remove("complete command set", (SIGNAL_FUNC) sig_complete_set);
        signal_remove("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
        signal_remove("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
        signal_remove("complete command set", (SIGNAL_FUNC) sig_complete_set);
        signal_remove("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
        signal_remove("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
-       signal_remove("complete command run", (SIGNAL_FUNC) sig_complete_filename);
        signal_remove("complete command save", (SIGNAL_FUNC) sig_complete_filename);
        signal_remove("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
        signal_remove("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
        signal_remove("complete command save", (SIGNAL_FUNC) sig_complete_filename);
        signal_remove("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
        signal_remove("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
index ef0fe06f5940e383be4b5f27b83dbde443513537..9a8b32cbd6c8af4478c89e6fc8ec77bd2df079a2 100644 (file)
@@ -5,10 +5,12 @@
 
 /* automatic word completion - called when space/enter is pressed */
 char *auto_word_complete(const char *line, int *pos);
 
 /* automatic word completion - called when space/enter is pressed */
 char *auto_word_complete(const char *line, int *pos);
-/* manual word completion - called when TAB is pressed */
-char *word_complete(WINDOW_REC *window, const char *line, int *pos);
+/* manual word completion - called when TAB is pressed. if erase is TRUE,
+   the word is removed from completion list entirely (if possible) and
+   next completion is used */
+char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase);
 
 
-GList *filename_complete(const char *path);
+GList *filename_complete(const char *path, const char *default_path);
 
 void completion_init(void);
 void completion_deinit(void);
 
 void completion_init(void);
 void completion_deinit(void);
index 67557b91e783acdd3632bfb2f76e432c1aa40e9c..fe41ea3004b8d5a7fbf4ac603c39c83bcc77b9f9 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "chat-protocols.h"
 #include "chatnets.h"
 
 #include "chat-protocols.h"
 #include "chatnets.h"
+#include "servers.h"
 #include "channels.h"
 #include "channels-setup.h"
 #include "nicklist.h"
 #include "channels.h"
 #include "channels-setup.h"
 #include "nicklist.h"
@@ -74,16 +75,6 @@ static void signal_channel_destroyed(CHANNEL_REC *channel)
                window_auto_destroy(window);
 }
 
                window_auto_destroy(window);
 }
 
-static void signal_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item)
-{
-       CHANNEL_REC *channel;
-
-       g_return_if_fail(window != NULL);
-
-       channel = CHANNEL(item);
-        if (channel != NULL) channel_destroy(channel);
-}
-
 static void sig_disconnected(SERVER_REC *server)
 {
        WINDOW_REC *window;
 static void sig_disconnected(SERVER_REC *server)
 {
        WINDOW_REC *window;
@@ -246,8 +237,11 @@ static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        if (*data == '\0')
                cmd_channel_list_joined();
 {
        if (*data == '\0')
                cmd_channel_list_joined();
-       else
+       else if (server != NULL && server_ischannel(server, data)) {
+               signal_emit("command join", 3, data, server, item);
+       } else {
                command_runsub("channel", data, server, item);
                command_runsub("channel", data, server, item);
+       }
 }
 
 /* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
 }
 
 /* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
@@ -336,10 +330,10 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
        TEXT_DEST_REC dest;
        GString *str;
        GSList *tmp;
        TEXT_DEST_REC dest;
        GString *str;
        GSList *tmp;
-        char *format, *stripped;
+        char *format, *stripped, *prefix_format;
        char *linebuf, nickmode[2] = { 0, 0 };
        int *columns, cols, rows, last_col_rows, col, row, max_width;
        char *linebuf, nickmode[2] = { 0, 0 };
        int *columns, cols, rows, last_col_rows, col, row, max_width;
-        int item_extra, linebuf_size;
+        int item_extra, linebuf_size, formatnum;
 
        window = window_find_closest(channel->server, channel->name,
                                     MSGLEVEL_CLIENTCRAP);
 
        window = window_find_closest(channel->server, channel->name,
                                     MSGLEVEL_CLIENTCRAP);
@@ -355,10 +349,10 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
        g_free(format);
 
        if (settings_get_int("names_max_width") > 0 &&
        g_free(format);
 
        if (settings_get_int("names_max_width") > 0 &&
-           max_width > settings_get_int("names_max_width"))
+           settings_get_int("names_max_width") < max_width)
                max_width = settings_get_int("names_max_width");
 
                max_width = settings_get_int("names_max_width");
 
-        /* remove width of timestamp from max_width */
+        /* remove width of the timestamp from max_width */
        format_create_dest(&dest, channel->server, channel->name,
                           MSGLEVEL_CLIENTCRAP, NULL);
        format = format_get_line_start(current_theme, &dest, time(NULL));
        format_create_dest(&dest, channel->server, channel->name,
                           MSGLEVEL_CLIENTCRAP, NULL);
        format = format_get_line_start(current_theme, &dest, time(NULL));
@@ -369,6 +363,22 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
                g_free(format);
        }
 
                g_free(format);
        }
 
+        /* remove width of the prefix from max_width */
+       prefix_format = format_get_text(MODULE_NAME, NULL,
+                                       channel->server, channel->name,
+                                       TXT_NAMES_PREFIX, channel->name);
+       if (prefix_format != NULL) {
+               stripped = strip_codes(prefix_format);
+               max_width -= strlen(stripped);
+               g_free(stripped);
+       }
+
+       if (max_width <= 0) {
+               /* we should always have at least some space .. if we
+                  really don't, it won't show properly anyway. */
+               max_width = 10;
+       }
+
        /* calculate columns */
        cols = get_max_column_count(nicklist, get_nick_length, max_width,
                                    settings_get_int("names_max_columns"),
        /* calculate columns */
        cols = get_max_column_count(nicklist, get_nick_length, max_width,
                                    settings_get_int("names_max_columns"),
@@ -380,15 +390,22 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
        if (last_col_rows == 0)
                 last_col_rows = rows;
 
        if (last_col_rows == 0)
                 last_col_rows = rows;
 
-       str = g_string_new(NULL);
+       str = g_string_new(prefix_format);
        linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
 
         col = 0; row = 0;
        for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
                NICK_REC *rec = tmp->data;
 
        linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
 
         col = 0; row = 0;
        for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
                NICK_REC *rec = tmp->data;
 
-               nickmode[0] = rec->op ? '@' : rec->voice ? '+' : ' ';
-
+               if (rec->op)
+                       nickmode[0] = '@';
+               else if (rec->halfop)
+                       nickmode[0] = '%';
+               else if (rec->voice)
+                       nickmode[0] = '+';
+               else
+                       nickmode[0] = ' ';
+               
                if (linebuf_size < columns[col]-item_extra+1) {
                        linebuf_size = (columns[col]-item_extra+1)*2;
                         linebuf = g_realloc(linebuf, linebuf_size);
                if (linebuf_size < columns[col]-item_extra+1) {
                        linebuf_size = (columns[col]-item_extra+1)*2;
                         linebuf = g_realloc(linebuf, linebuf_size);
@@ -397,9 +414,13 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
                linebuf[columns[col]-item_extra] = '\0';
                memcpy(linebuf, rec->nick, strlen(rec->nick));
 
                linebuf[columns[col]-item_extra] = '\0';
                memcpy(linebuf, rec->nick, strlen(rec->nick));
 
+               formatnum = rec->op ? TXT_NAMES_NICK_OP :
+                       rec->halfop ? TXT_NAMES_NICK_HALFOP :
+                       rec->voice ? TXT_NAMES_NICK_VOICE :
+                        TXT_NAMES_NICK;
                format = format_get_text(MODULE_NAME, NULL,
                                         channel->server, channel->name,
                format = format_get_text(MODULE_NAME, NULL,
                                         channel->server, channel->name,
-                                        TXT_NAMES_NICK, nickmode, linebuf);
+                                        formatnum, nickmode, linebuf);
                g_string_append(str, format);
                g_free(format);
 
                g_string_append(str, format);
                g_free(format);
 
@@ -407,6 +428,8 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
                        printtext(channel->server, channel->name,
                                  MSGLEVEL_CLIENTCRAP, "%s", str->str);
                        g_string_truncate(str, 0);
                        printtext(channel->server, channel->name,
                                  MSGLEVEL_CLIENTCRAP, "%s", str->str);
                        g_string_truncate(str, 0);
+                       if (prefix_format != NULL)
+                                g_string_assign(str, prefix_format);
                        col = 0; row++;
 
                        if (row == last_col_rows)
                        col = 0; row++;
 
                        if (row == last_col_rows)
@@ -422,6 +445,7 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
        g_slist_free(nicklist);
        g_string_free(str, TRUE);
        g_free_not_null(columns);
        g_slist_free(nicklist);
        g_string_free(str, TRUE);
        g_free_not_null(columns);
+       g_free_not_null(prefix_format);
        g_free(linebuf);
 }
 
        g_free(linebuf);
 }
 
@@ -429,9 +453,9 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
 {
        NICK_REC *nick;
        GSList *tmp, *nicklist, *sorted;
 {
        NICK_REC *nick;
        GSList *tmp, *nicklist, *sorted;
-       int nicks, normal, voices, ops;
+       int nicks, normal, voices, halfops, ops;
 
 
-       nicks = normal = voices = ops = 0;
+       nicks = normal = voices = halfops = ops = 0;
        nicklist = nicklist_getnicks(channel);
        sorted = NULL;
 
        nicklist = nicklist_getnicks(channel);
        sorted = NULL;
 
@@ -445,6 +469,7 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
                        if ((flags & CHANNEL_NICKLIST_FLAG_OPS) == 0)
                                 continue;
                } else if (nick->halfop) {
                        if ((flags & CHANNEL_NICKLIST_FLAG_OPS) == 0)
                                 continue;
                } else if (nick->halfop) {
+                       halfops++;
                        if ((flags & CHANNEL_NICKLIST_FLAG_HALFOPS) == 0)
                                continue;
                } else if (nick->voice) {
                        if ((flags & CHANNEL_NICKLIST_FLAG_HALFOPS) == 0)
                                continue;
                } else if (nick->voice) {
@@ -463,17 +488,19 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
        g_slist_free(nicklist);
 
        /* display the nicks */
        g_slist_free(nicklist);
 
        /* display the nicks */
-       printformat(channel->server, channel->name,
-                   MSGLEVEL_CRAP, TXT_NAMES, channel->name, "");
-       display_sorted_nicks(channel, sorted);
+        if ((flags & CHANNEL_NICKLIST_FLAG_COUNT) == 0) {
+               printformat(channel->server, channel->name,
+                           MSGLEVEL_CRAP, TXT_NAMES, channel->name, "");
+               display_sorted_nicks(channel, sorted);
+       }
        g_slist_free(sorted);
 
        printformat(channel->server, channel->name,
                    MSGLEVEL_CRAP, TXT_ENDOFNAMES,
        g_slist_free(sorted);
 
        printformat(channel->server, channel->name,
                    MSGLEVEL_CRAP, TXT_ENDOFNAMES,
-                   channel->name, nicks, ops, voices, normal);
+                   channel->name, nicks, ops, halfops, voices, normal);
 }
 
 }
 
-/* SYNTAX: NAMES [-ops -halfops -voices -normal] [<channels> | **] */
+/* SYNTAX: NAMES [-count | -ops -halfops -voices -normal] [<channels> | **] */
 static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        CHANNEL_REC *chanrec;
 static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        CHANNEL_REC *chanrec;
@@ -507,6 +534,8 @@ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
                flags |= CHANNEL_NICKLIST_FLAG_VOICES;
        if (g_hash_table_lookup(optlist, "normal") != NULL)
                flags |= CHANNEL_NICKLIST_FLAG_NORMAL;
                flags |= CHANNEL_NICKLIST_FLAG_VOICES;
        if (g_hash_table_lookup(optlist, "normal") != NULL)
                flags |= CHANNEL_NICKLIST_FLAG_NORMAL;
+       if (g_hash_table_lookup(optlist, "count") != NULL)
+               flags |= CHANNEL_NICKLIST_FLAG_COUNT;
 
         if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL;
 
 
         if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL;
 
@@ -572,7 +601,6 @@ void fe_channels_init(void)
 
        signal_add("channel created", (SIGNAL_FUNC) signal_channel_created);
        signal_add("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
 
        signal_add("channel created", (SIGNAL_FUNC) signal_channel_created);
        signal_add("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
-       signal_add_last("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy);
        signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
        signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
 
        signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
        signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
 
@@ -587,7 +615,7 @@ void fe_channels_init(void)
        command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
 
        command_set_options("channel add", "auto noauto -bots -botcmd");
        command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
 
        command_set_options("channel add", "auto noauto -bots -botcmd");
-       command_set_options("names", "ops halfops voices normal");
+       command_set_options("names", "count ops halfops voices normal");
        command_set_options("join", "window");
 }
 
        command_set_options("join", "window");
 }
 
@@ -595,7 +623,6 @@ void fe_channels_deinit(void)
 {
        signal_remove("channel created", (SIGNAL_FUNC) signal_channel_created);
        signal_remove("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
 {
        signal_remove("channel created", (SIGNAL_FUNC) signal_channel_created);
        signal_remove("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
-       signal_remove("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy);
        signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
        signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
 
        signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
        signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
 
index a8aa697e90ae1e54fbb7e9414b37d42bbc131938..e9a03a1273c029b7f55b70e2b01e01a0049c04fb 100644 (file)
@@ -6,6 +6,7 @@
 #define CHANNEL_NICKLIST_FLAG_VOICES    0x04
 #define CHANNEL_NICKLIST_FLAG_NORMAL    0x08
 #define CHANNEL_NICKLIST_FLAG_ALL       0x0f
 #define CHANNEL_NICKLIST_FLAG_VOICES    0x04
 #define CHANNEL_NICKLIST_FLAG_NORMAL    0x08
 #define CHANNEL_NICKLIST_FLAG_ALL       0x0f
+#define CHANNEL_NICKLIST_FLAG_COUNT     0x10
 
 void fe_channels_nicklist(CHANNEL_REC *channel, int flags);
 
 
 void fe_channels_nicklist(CHANNEL_REC *channel, int flags);
 
index 248369057f595da7264b12081a421e29f223eecb..6b57098abc17a3cebbe4794962c52a5df7c776bb 100644 (file)
 #include "misc.h"
 #include "levels.h"
 #include "settings.h"
 #include "misc.h"
 #include "levels.h"
 #include "settings.h"
+#include "irssi-version.h"
 
 
+#include "servers.h"
 #include "channels.h"
 #include "servers-setup.h"
 
 #include "channels.h"
 #include "servers-setup.h"
 
+#include "autorun.h"
 #include "fe-queries.h"
 #include "hilight-text.h"
 #include "command-history.h"
 #include "fe-queries.h"
 #include "hilight-text.h"
 #include "command-history.h"
@@ -51,9 +54,6 @@ static int no_autoconnect;
 static char *cmdline_nick;
 static char *cmdline_hostname;
 
 static char *cmdline_nick;
 static char *cmdline_hostname;
 
-void autorun_init(void);
-void autorun_deinit(void);
-
 void fe_core_log_init(void);
 void fe_core_log_deinit(void);
 
 void fe_core_log_init(void);
 void fe_core_log_deinit(void);
 
@@ -96,6 +96,13 @@ void window_commands_deinit(void);
 void fe_core_commands_init(void);
 void fe_core_commands_deinit(void);
 
 void fe_core_commands_init(void);
 void fe_core_commands_deinit(void);
 
+static void print_version(void)
+{
+       printf(PACKAGE" " IRSSI_VERSION" (%d %04d)\n",
+              IRSSI_VERSION_DATE, IRSSI_VERSION_TIME);
+        exit(0);
+}
+
 static void sig_connected(SERVER_REC *server)
 {
        MODULE_DATA_SET(server, g_new0(MODULE_SERVER_REC, 1));
 static void sig_connected(SERVER_REC *server)
 {
        MODULE_DATA_SET(server, g_new0(MODULE_SERVER_REC, 1));
@@ -104,6 +111,7 @@ static void sig_connected(SERVER_REC *server)
 static void sig_disconnected(SERVER_REC *server)
 {
        g_free(MODULE_DATA(server));
 static void sig_disconnected(SERVER_REC *server)
 {
        g_free(MODULE_DATA(server));
+       MODULE_DATA_UNSET(server);
 }
 
 static void sig_channel_created(CHANNEL_REC *channel)
 }
 
 static void sig_channel_created(CHANNEL_REC *channel)
@@ -114,19 +122,26 @@ static void sig_channel_created(CHANNEL_REC *channel)
 static void sig_channel_destroyed(CHANNEL_REC *channel)
 {
        g_free(MODULE_DATA(channel));
 static void sig_channel_destroyed(CHANNEL_REC *channel)
 {
        g_free(MODULE_DATA(channel));
+       MODULE_DATA_UNSET(channel);
 }
 
 void fe_common_core_init(void)
 {
 }
 
 void fe_common_core_init(void)
 {
+       static struct poptOption version_options[] = {
+               { NULL, '\0', POPT_ARG_CALLBACK, (void *)&print_version, '\0', NULL },
+               { "version", 'v', POPT_ARG_NONE, NULL, 0, "Display irssi version" },
+               { NULL, '\0', 0, NULL }
+       };
+
        static struct poptOption options[] = {
        static struct poptOption options[] = {
+               { NULL, '\0', POPT_ARG_INCLUDE_TABLE, version_options, 0, NULL, NULL },
+               POPT_AUTOHELP
                { "connect", 'c', POPT_ARG_STRING, &autocon_server, 0, "Automatically connect to server/ircnet", "SERVER" },
                { "connect", 'c', POPT_ARG_STRING, &autocon_server, 0, "Automatically connect to server/ircnet", "SERVER" },
-               { "password", 'w', POPT_ARG_STRING, &autocon_password, 0, "Autoconnect password", "SERVER" },
+               { "password", 'w', POPT_ARG_STRING, &autocon_password, 0, "Autoconnect password", "PASSWORD" },
                { "port", 'p', POPT_ARG_INT, &autocon_port, 0, "Autoconnect port", "PORT" },
                { "noconnect", '!', POPT_ARG_NONE, &no_autoconnect, 0, "Disable autoconnecting", NULL },
                { "port", 'p', POPT_ARG_INT, &autocon_port, 0, "Autoconnect port", "PORT" },
                { "noconnect", '!', POPT_ARG_NONE, &no_autoconnect, 0, "Disable autoconnecting", NULL },
-               /*
                { "nick", 'n', POPT_ARG_STRING, &cmdline_nick, 0, "Specify nick to use", NULL },
                { "hostname", 'h', POPT_ARG_STRING, &cmdline_hostname, 0, "Specify host name to use", NULL },
                { "nick", 'n', POPT_ARG_STRING, &cmdline_nick, 0, "Specify nick to use", NULL },
                { "hostname", 'h', POPT_ARG_STRING, &cmdline_hostname, 0, "Specify host name to use", NULL },
-               */
                { NULL, '\0', 0, NULL }
        };
 
                { NULL, '\0', 0, NULL }
        };
 
@@ -139,7 +154,7 @@ void fe_common_core_init(void)
        args_register(options);
 
        settings_add_bool("lookandfeel", "timestamps", TRUE);
        args_register(options);
 
        settings_add_bool("lookandfeel", "timestamps", TRUE);
-       settings_add_bool("lookandfeel", "msgs_timestamps", FALSE);
+       settings_add_str("lookandfeel", "timestamp_level", "ALL");
        settings_add_int("lookandfeel", "timestamp_timeout", 0);
 
        settings_add_bool("lookandfeel", "bell_beeps", FALSE);
        settings_add_int("lookandfeel", "timestamp_timeout", 0);
 
        settings_add_bool("lookandfeel", "bell_beeps", FALSE);
@@ -148,6 +163,7 @@ void fe_common_core_init(void)
        settings_add_bool("lookandfeel", "beep_when_away", TRUE);
 
        settings_add_bool("lookandfeel", "hide_text_style", FALSE);
        settings_add_bool("lookandfeel", "beep_when_away", TRUE);
 
        settings_add_bool("lookandfeel", "hide_text_style", FALSE);
+       settings_add_bool("lookandfeel", "hide_mirc_colors", FALSE);
        settings_add_bool("lookandfeel", "hide_server_tags", FALSE);
 
        settings_add_bool("lookandfeel", "use_status_window", TRUE);
        settings_add_bool("lookandfeel", "hide_server_tags", FALSE);
 
        settings_add_bool("lookandfeel", "use_status_window", TRUE);
@@ -156,7 +172,6 @@ void fe_common_core_init(void)
        themes_init();
         theme_register(fecommon_core_formats);
 
        themes_init();
         theme_register(fecommon_core_formats);
 
-       autorun_init();
        command_history_init();
        completion_init();
        keyboard_init();
        command_history_init();
        completion_init();
        keyboard_init();
@@ -193,11 +208,12 @@ void fe_common_core_init(void)
         signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
         signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
         signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
         signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
         signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
         signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+
+       module_register("core", "fe");
 }
 
 void fe_common_core_deinit(void)
 {
 }
 
 void fe_common_core_deinit(void)
 {
-       autorun_deinit();
        hilight_text_deinit();
        command_history_deinit();
        completion_deinit();
        hilight_text_deinit();
        command_history_deinit();
        completion_deinit();
@@ -331,20 +347,25 @@ static void autoconnect_servers(void)
 
 void fe_common_core_finish_init(void)
 {
 
 void fe_common_core_finish_init(void)
 {
+       int setup_changed;
+
        signal_emit("irssi init read settings", 0);
 
 #ifdef SIGPIPE
        signal(SIGPIPE, SIG_IGN);
 #endif
 
        signal_emit("irssi init read settings", 0);
 
 #ifdef SIGPIPE
        signal(SIGPIPE, SIG_IGN);
 #endif
 
+        setup_changed = FALSE;
        if (cmdline_nick != NULL) {
                /* override nick found from setup */
                settings_set_str("nick", cmdline_nick);
        if (cmdline_nick != NULL) {
                /* override nick found from setup */
                settings_set_str("nick", cmdline_nick);
+               setup_changed = TRUE;
        }
 
        if (cmdline_hostname != NULL) {
                /* override host name found from setup */
                settings_set_str("hostname", cmdline_hostname);
        }
 
        if (cmdline_hostname != NULL) {
                /* override host name found from setup */
                settings_set_str("hostname", cmdline_hostname);
+               setup_changed = TRUE;
        }
 
        create_windows();
        }
 
        create_windows();
@@ -355,5 +376,9 @@ void fe_common_core_finish_init(void)
                                            G_LOG_LEVEL_WARNING),
                          (GLogFunc) glog_func, NULL);
 
                                            G_LOG_LEVEL_WARNING),
                          (GLogFunc) glog_func, NULL);
 
+       if (setup_changed)
+                signal_emit("setup changed", 0);
+
+        autorun_startup();
        autoconnect_servers();
 }
        autoconnect_servers();
 }
index 7b8f7f9bc55ceb1bca0338c66fd300de9a240882..cf7938eaf0ec533d0efb381e0e88dad483f7ef48 100644 (file)
@@ -27,6 +27,7 @@
 #include "line-split.h"
 #include "settings.h"
 #include "irssi-version.h"
 #include "line-split.h"
 #include "settings.h"
 #include "irssi-version.h"
+#include "servers.h"
 
 #include "fe-windows.h"
 #include "printtext.h"
 
 #include "fe-windows.h"
 #include "printtext.h"
@@ -45,6 +46,7 @@ static int ret_texts[] = {
        TXT_NOT_JOINED,
        TXT_CHAN_NOT_FOUND,
        TXT_CHAN_NOT_SYNCED,
        TXT_NOT_JOINED,
        TXT_CHAN_NOT_FOUND,
        TXT_CHAN_NOT_SYNCED,
+        TXT_ILLEGAL_PROTO,
        TXT_NOT_GOOD_IDEA
 };
 
        TXT_NOT_GOOD_IDEA
 };
 
@@ -91,11 +93,15 @@ static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item)
 /* SYNTAX: VERSION */
 static void cmd_version(char *data)
 {
 /* SYNTAX: VERSION */
 static void cmd_version(char *data)
 {
+       char time[10];
+
        g_return_if_fail(data != NULL);
 
        if (*data == '\0') {
        g_return_if_fail(data != NULL);
 
        if (*data == '\0') {
+                g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME);
                printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                         "Client: "PACKAGE" " IRSSI_VERSION);
+                         "Client: "PACKAGE" " IRSSI_VERSION" (%d %s)",
+                         IRSSI_VERSION_DATE, time);
        }
 }
 
        }
 }
 
@@ -144,6 +150,43 @@ static void cmd_beep(void)
         signal_emit("beep", 0);
 }
 
         signal_emit("beep", 0);
 }
 
+static void cmd_nick(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+
+       if (*data != '\0') return;
+       if (server == NULL || !server->connected)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       /* display current nick */
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick);
+       signal_stop();
+}
+
+static void cmd_join(const char *data, SERVER_REC *server)
+{
+       GHashTable *optlist;
+       char *channels;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                           "join", &optlist, &channels))
+               return;
+
+       server = cmd_options_get_server("join", optlist, server);
+       if (g_hash_table_lookup(optlist, "invite") &&
+           server != NULL && server->last_invite == NULL) {
+                /* ..all this trouble just to print this error message */
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED);
+               signal_stop();
+       }
+
+       cmd_params_free(free_arg);
+}
+
 static void sig_stop(void)
 {
        signal_stop();
 static void sig_stop(void)
 {
        signal_stop();
@@ -153,6 +196,12 @@ static void event_command(const char *data)
 {
        const char *cmdchar;
 
 {
        const char *cmdchar;
 
+       if (*data == '\0') {
+               /* empty line, forget it. */
+                signal_stop();
+               return;
+       }
+
        /* save current command line */
        current_cmdline = data;
 
        /* save current command line */
        current_cmdline = data;
 
@@ -170,7 +219,6 @@ static void event_command(const char *data)
                 hide_output = TRUE;
                signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
                signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
                 hide_output = TRUE;
                signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
                signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
-               signal_add_first("print text stripped", (SIGNAL_FUNC) sig_stop);
                signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
        }
 }
                signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
        }
 }
@@ -181,7 +229,6 @@ static void event_command_last(const char *data)
                hide_output = FALSE;
                signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
                signal_remove("print format", (SIGNAL_FUNC) sig_stop);
                hide_output = FALSE;
                signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
                signal_remove("print format", (SIGNAL_FUNC) sig_stop);
-               signal_remove("print text stripped", (SIGNAL_FUNC) sig_stop);
                signal_remove("print text", (SIGNAL_FUNC) sig_stop);
        }
 }
                signal_remove("print text", (SIGNAL_FUNC) sig_stop);
        }
 }
@@ -278,6 +325,8 @@ void fe_core_commands_init(void)
        command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
        command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
        command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
        command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
        command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
        command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
+       command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick);
+       command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_join);
 
        signal_add("send command", (SIGNAL_FUNC) event_command);
        signal_add_last("send command", (SIGNAL_FUNC) event_command_last);
 
        signal_add("send command", (SIGNAL_FUNC) event_command);
        signal_add_last("send command", (SIGNAL_FUNC) event_command_last);
@@ -294,6 +343,8 @@ void fe_core_commands_deinit(void)
        command_unbind("version", (SIGNAL_FUNC) cmd_version);
        command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
        command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
        command_unbind("version", (SIGNAL_FUNC) cmd_version);
        command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
        command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
+       command_unbind("nick", (SIGNAL_FUNC) cmd_nick);
+       command_unbind("join", (SIGNAL_FUNC) cmd_join);
 
        signal_remove("send command", (SIGNAL_FUNC) event_command);
        signal_remove("send command", (SIGNAL_FUNC) event_command_last);
 
        signal_remove("send command", (SIGNAL_FUNC) event_command);
        signal_remove("send command", (SIGNAL_FUNC) event_command_last);
index bc8f46919168cbe3cc2e73482b601ad8208fe63c..809726341d4fc99983cc223601b7f25de0b1c463 100644 (file)
@@ -1,7 +1,7 @@
 /*
  fe-exec.c : irssi
 
 /*
  fe-exec.c : irssi
 
-    Copyright (C) 2000 Timo Sirainen
+    Copyright (C) 2000-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
 
     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
@@ -19,6 +19,7 @@
 */
 
 #include "module.h"
 */
 
 #include "module.h"
+#include "modules.h"
 #include "signals.h"
 #include "commands.h"
 #include "pidwait.h"
 #include "signals.h"
 #include "commands.h"
 #include "pidwait.h"
 #include <signal.h>
 #include <sys/wait.h>
 
 #include <signal.h>
 #include <sys/wait.h>
 
-static GSList *processes;
+GSList *processes;
 static int signal_exec_input;
 
 static int signal_exec_input;
 
+static void exec_wi_destroy(EXEC_WI_REC *rec)
+{
+        g_return_if_fail(rec != NULL);
+
+       if (rec->destroying) return;
+        rec->destroying = TRUE;
+
+       if (window_item_window((WI_ITEM_REC *) rec) != NULL)
+               window_item_destroy((WI_ITEM_REC *) rec);
+
+       MODULE_DATA_DEINIT(rec);
+       g_free(rec->name);
+        g_free(rec);
+}
+
 static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec)
 {
        EXEC_WI_REC *item;
 static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec)
 {
        EXEC_WI_REC *item;
@@ -47,11 +63,11 @@ static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec)
 
        item = g_new0(EXEC_WI_REC, 1);
        item->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "EXEC");
 
        item = g_new0(EXEC_WI_REC, 1);
        item->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "EXEC");
+        item->destroy = (void (*) (WI_ITEM_REC *)) exec_wi_destroy;
        item->name = rec->name != NULL ?
                g_strdup_printf("%%%s", rec->name) :
                g_strdup_printf("%%%d", rec->id);
 
        item->name = rec->name != NULL ?
                g_strdup_printf("%%%s", rec->name) :
                g_strdup_printf("%%%d", rec->id);
 
-       item->window = window;
        item->createtime = time(NULL);
         item->process = rec;
 
        item->createtime = time(NULL);
         item->process = rec;
 
@@ -60,21 +76,6 @@ static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec)
         return item;
 }
 
         return item;
 }
 
-static void exec_wi_destroy(EXEC_WI_REC *rec)
-{
-        g_return_if_fail(rec != NULL);
-
-       if (rec->destroying) return;
-        rec->destroying = TRUE;
-
-       if (window_item_window((WI_ITEM_REC *) rec) != NULL)
-               window_item_destroy((WI_ITEM_REC *) rec);
-
-       MODULE_DATA_DEINIT(rec);
-       g_free(rec->name);
-        g_free(rec);
-}
-
 static int process_get_new_id(void)
 {
         PROCESS_REC *rec;
 static int process_get_new_id(void)
 {
         PROCESS_REC *rec;
@@ -368,7 +369,7 @@ static void handle_exec(const char *args, GHashTable *optlist,
                        WI_ITEM_REC *item)
 {
        PROCESS_REC *rec;
                        WI_ITEM_REC *item)
 {
        PROCESS_REC *rec;
-        char *target;
+        char *target, *level;
        int notice, signum, interactive;
 
        /* check that there's no unknown options. we allowed them
        int notice, signum, interactive;
 
        /* check that there's no unknown options. we allowed them
@@ -491,6 +492,9 @@ static void handle_exec(const char *args, GHashTable *optlist,
         rec->silent = g_hash_table_lookup(optlist, "-") != NULL;
        rec->name = g_strdup(g_hash_table_lookup(optlist, "name"));
 
         rec->silent = g_hash_table_lookup(optlist, "-") != NULL;
        rec->name = g_strdup(g_hash_table_lookup(optlist, "name"));
 
+       level = g_hash_table_lookup(optlist, "level");
+       rec->level = level == NULL ? MSGLEVEL_CLIENTCRAP : level2bits(level);
+
        rec->read_tag = g_input_add(rec->in, G_INPUT_READ,
                                    (GInputFunction) sig_exec_input_reader,
                                    rec);
        rec->read_tag = g_input_add(rec->in, G_INPUT_READ,
                                    (GInputFunction) sig_exec_input_reader,
                                    rec);
@@ -526,12 +530,17 @@ static void cmd_exec(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 static void sig_pidwait(void *pid, void *statusp)
 {
        PROCESS_REC *rec;
 static void sig_pidwait(void *pid, void *statusp)
 {
        PROCESS_REC *rec;
+        char *str;
        int status = GPOINTER_TO_INT(statusp);
 
         rec = process_find_pid(GPOINTER_TO_INT(pid));
        if (rec == NULL) return;
 
        int status = GPOINTER_TO_INT(statusp);
 
         rec = process_find_pid(GPOINTER_TO_INT(pid));
        if (rec == NULL) return;
 
-       /* process exited */
+       /* process exited - print the last line if
+          there wasn't a newline at end. */
+       if (line_split("\n", 1, &str, &rec->databuf) > 0 && *str != '\0')
+               signal_emit_id(signal_exec_input, 2, rec, str);
+
        if (!rec->silent) {
                if (WIFSIGNALED(status)) {
                        status = WTERMSIG(status);
        if (!rec->silent) {
                if (WIFSIGNALED(status)) {
                        status = WTERMSIG(status);
@@ -568,11 +577,10 @@ static void sig_exec_input(PROCESS_REC *rec, const char *text)
                            3, str, server, item);
                 g_free(str);
        } else if (rec->target_item != NULL) {
                            3, str, server, item);
                 g_free(str);
        } else if (rec->target_item != NULL) {
-               printtext(NULL, rec->target_item->name, MSGLEVEL_CLIENTCRAP,
-                         "%s", text);
+               printtext(NULL, rec->target_item->name,
+                         rec->level, "%s", text);
        } else {
        } else {
-               printtext_window(rec->target_win, MSGLEVEL_CLIENTCRAP,
-                                "%s", text);
+               printtext_window(rec->target_win, rec->level, "%s", text);
        }
 }
 
        }
 }
 
@@ -590,14 +598,6 @@ static void sig_window_destroyed(WINDOW_REC *window)
        }
 }
 
        }
 }
 
-static void sig_window_item_destroyed(WINDOW_REC *window, EXEC_WI_REC *item)
-{
-       if (IS_EXEC_WI(item)) {
-                item->process->target_item = NULL;
-               exec_wi_destroy(item);
-       }
-}
-
 static void event_text(const char *data, SERVER_REC *server, EXEC_WI_REC *item)
 {
        if (!IS_EXEC_WI(item)) return;
 static void event_text(const char *data, SERVER_REC *server, EXEC_WI_REC *item)
 {
        if (!IS_EXEC_WI(item)) return;
@@ -610,13 +610,12 @@ static void event_text(const char *data, SERVER_REC *server, EXEC_WI_REC *item)
 void fe_exec_init(void)
 {
        command_bind("exec", NULL, (SIGNAL_FUNC) cmd_exec);
 void fe_exec_init(void)
 {
        command_bind("exec", NULL, (SIGNAL_FUNC) cmd_exec);
-       command_set_options("exec", "!- interactive nosh +name out +msg +notice +in window close");
+       command_set_options("exec", "!- interactive nosh +name out +msg +notice +in window close +level");
 
         signal_exec_input = signal_get_uniq_id("exec input");
         signal_add("pidwait", (SIGNAL_FUNC) sig_pidwait);
         signal_add("exec input", (SIGNAL_FUNC) sig_exec_input);
         signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
 
         signal_exec_input = signal_get_uniq_id("exec input");
         signal_add("pidwait", (SIGNAL_FUNC) sig_pidwait);
         signal_add("exec input", (SIGNAL_FUNC) sig_exec_input);
         signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
-       signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroyed);
        signal_add_first("send text", (SIGNAL_FUNC) event_text);
 }
 
        signal_add_first("send text", (SIGNAL_FUNC) event_text);
 }
 
@@ -636,6 +635,5 @@ void fe_exec_deinit(void)
         signal_remove("pidwait", (SIGNAL_FUNC) sig_pidwait);
         signal_remove("exec input", (SIGNAL_FUNC) sig_exec_input);
         signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
         signal_remove("pidwait", (SIGNAL_FUNC) sig_pidwait);
         signal_remove("exec input", (SIGNAL_FUNC) sig_exec_input);
         signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
-       signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroyed);
        signal_remove("send text", (SIGNAL_FUNC) event_text);
 }
        signal_remove("send text", (SIGNAL_FUNC) event_text);
 }
index 7f569ecea70dd10221421df82d39730f74f969b0..bebd1f82cf6384c0835797a391dad11487e1025a 100644 (file)
@@ -30,6 +30,7 @@ struct PROCESS_REC {
         LINEBUF_REC *databuf;
        int read_tag;
 
         LINEBUF_REC *databuf;
        int read_tag;
 
+        int level; /* what level to use when printing the text */
         char *target; /* send text with /msg <target> ... */
        WINDOW_REC *target_win; /* print text to this window */
         EXEC_WI_REC *target_item; /* print text to this exec window item */
         char *target; /* send text with /msg <target> ... */
        WINDOW_REC *target_win; /* print text to this window */
         EXEC_WI_REC *target_item; /* print text to this exec window item */
@@ -39,6 +40,8 @@ struct PROCESS_REC {
        unsigned int silent:1; /* don't print "process exited with level xx" */
 };
 
        unsigned int silent:1; /* don't print "process exited with level xx" */
 };
 
+extern GSList *processes;
+
 void fe_exec_init(void);
 void fe_exec_deinit(void);
 
 void fe_exec_init(void);
 void fe_exec_deinit(void);
 
index 7eb145dbd31e721e8c7671eff8edc6046ab433ea..c14ac7c3e51f5ad9ab58604530c76f5c72902bec 100644 (file)
@@ -25,6 +25,9 @@
 /* Window ref# */
 static char *expando_winref(SERVER_REC *server, void *item, int *free_ret)
 {
 /* Window ref# */
 static char *expando_winref(SERVER_REC *server, void *item, int *free_ret)
 {
+       if (active_win == NULL)
+               return "";
+
         *free_ret = TRUE;
        return g_strdup_printf("%d", active_win->refnum);
 }
         *free_ret = TRUE;
        return g_strdup_printf("%d", active_win->refnum);
 }
@@ -32,6 +35,9 @@ static char *expando_winref(SERVER_REC *server, void *item, int *free_ret)
 /* Window name */
 static char *expando_winname(SERVER_REC *server, void *item, int *free_ret)
 {
 /* Window name */
 static char *expando_winname(SERVER_REC *server, void *item, int *free_ret)
 {
+       if (active_win == NULL)
+               return "";
+
        return active_win->name;
 }
 
        return active_win->name;
 }
 
@@ -41,6 +47,7 @@ void fe_expandos_init(void)
                       "window changed", EXPANDO_ARG_NONE,
                       "window refnum changed", EXPANDO_ARG_WINDOW, NULL);
        expando_create("winname", expando_winname,
                       "window changed", EXPANDO_ARG_NONE,
                       "window refnum changed", EXPANDO_ARG_WINDOW, NULL);
        expando_create("winname", expando_winname,
+                      "window changed", EXPANDO_ARG_NONE,
                       "window name changed", EXPANDO_ARG_WINDOW, NULL);
 }
 
                       "window name changed", EXPANDO_ARG_WINDOW, NULL);
 }
 
index 3523d527d6db7c98b3d3748212c4ff30d46d2b22..55a882664abbf586ada3cdb948aadf673556a2a4 100644 (file)
@@ -54,9 +54,18 @@ static void ignore_print(int index, IGNORE_REC *rec)
 
        options = g_string_new(NULL);
        if (rec->exception) g_string_sprintfa(options, "-except ");
 
        options = g_string_new(NULL);
        if (rec->exception) g_string_sprintfa(options, "-except ");
-       if (rec->regexp) g_string_sprintfa(options, "-regexp ");
+       if (rec->regexp) {
+               g_string_sprintfa(options, "-regexp ");
+#ifdef HAVE_REGEX_H
+               if (!rec->regexp_compiled)
+                       g_string_sprintfa(options, "[INVALID!] ");
+#endif
+       }
        if (rec->fullword) g_string_sprintfa(options, "-full ");
        if (rec->replies) g_string_sprintfa(options, "-replies ");
        if (rec->fullword) g_string_sprintfa(options, "-full ");
        if (rec->replies) g_string_sprintfa(options, "-replies ");
+       if (rec->pattern != NULL)
+               g_string_sprintfa(options, "-pattern %s ", rec->pattern);
+
        if (options->len > 1) g_string_truncate(options, options->len-1);
 
        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
        if (options->len > 1) g_string_truncate(options, options->len-1);
 
        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
@@ -73,6 +82,12 @@ static void cmd_ignore_show(void)
        GSList *tmp;
        int index;
 
        GSList *tmp;
        int index;
 
+       if (ignores == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                           TXT_IGNORE_NO_IGNORES);
+                return;
+       }
+
        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_IGNORE_HEADER);
        index = 1;
        for (tmp = ignores; tmp != NULL; tmp = tmp->next, index++) {
        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_IGNORE_HEADER);
        index = 1;
        for (tmp = ignores; tmp != NULL; tmp = tmp->next, index++) {
@@ -112,7 +127,7 @@ static void cmd_ignore(const char *data)
         if (*levels == '\0') levels = "ALL";
 
        if (active_win->active_server != NULL &&
         if (*levels == '\0') levels = "ALL";
 
        if (active_win->active_server != NULL &&
-           active_win->active_server->ischannel(mask)) {
+           server_ischannel(active_win->active_server, mask)) {
                chanarg = mask;
                mask = NULL;
        }
                chanarg = mask;
                mask = NULL;
        }
@@ -134,6 +149,18 @@ static void cmd_ignore(const char *data)
        }
 
        rec->level = combine_level(rec->level, levels);
        }
 
        rec->level = combine_level(rec->level, levels);
+
+       if (new_ignore && rec->level == 0) {
+               /* tried to unignore levels from nonexisting ignore */
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_IGNORE_NOT_FOUND, rec->mask);
+               g_free(rec->mask);
+               g_strfreev(rec->channels);
+               g_free(rec);
+               cmd_params_free(free_arg);
+                return;
+       }
+
        rec->pattern = (patternarg == NULL || *patternarg == '\0') ?
                NULL : g_strdup(patternarg);
        rec->exception = g_hash_table_lookup(optlist, "except") != NULL;
        rec->pattern = (patternarg == NULL || *patternarg == '\0') ?
                NULL : g_strdup(patternarg);
        rec->exception = g_hash_table_lookup(optlist, "except") != NULL;
@@ -144,11 +171,6 @@ static void cmd_ignore(const char *data)
         if (timestr != NULL)
                rec->unignore_time = time(NULL)+atoi(timestr);
 
         if (timestr != NULL)
                rec->unignore_time = time(NULL)+atoi(timestr);
 
-       if (rec->level == 0) {
-               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_UNIGNORED,
-                           rec->mask == NULL ? "*" : rec->mask);
-       }
-
        if (new_ignore)
                ignore_add_rec(rec);
        else
        if (new_ignore)
                ignore_add_rec(rec);
        else
@@ -162,24 +184,29 @@ static void cmd_unignore(const char *data)
 {
        IGNORE_REC *rec;
        GSList *tmp;
 {
        IGNORE_REC *rec;
        GSList *tmp;
+        char *mask;
+       void *free_arg;
 
 
-       if (*data == '\0')
+       if (!cmd_get_params(data, &free_arg, 1, &mask))
+               return;
+
+       if (*mask == '\0')
                 cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
 
                 cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
 
-       if (is_numeric(data, ' ')) {
+       if (is_numeric(mask, ' ')) {
                /* with index number */
                /* with index number */
-               tmp = g_slist_nth(ignores, atoi(data)-1);
+               tmp = g_slist_nth(ignores, atoi(mask)-1);
                rec = tmp == NULL ? NULL : tmp->data;
        } else {
                /* with mask */
                const char *chans[2] = { "*", NULL };
 
                if (active_win->active_server != NULL &&
                rec = tmp == NULL ? NULL : tmp->data;
        } else {
                /* with mask */
                const char *chans[2] = { "*", NULL };
 
                if (active_win->active_server != NULL &&
-                   active_win->active_server->ischannel(data)) {
-                       chans[0] = data;
-                       data = NULL;
+                   server_ischannel(active_win->active_server, mask)) {
+                       chans[0] = mask;
+                       mask = NULL;
                }
                }
-               rec = ignore_find("*", data, (char **) chans);
+               rec = ignore_find("*", mask, (char **) chans);
        }
 
        if (rec != NULL) {
        }
 
        if (rec != NULL) {
@@ -187,8 +214,9 @@ static void cmd_unignore(const char *data)
                ignore_update_rec(rec);
        } else {
                printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                ignore_update_rec(rec);
        } else {
                printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                           TXT_IGNORE_NOT_FOUND, data);
+                           TXT_IGNORE_NOT_FOUND, mask);
        }
        }
+       cmd_params_free(free_arg);
 }
 
 static void sig_ignore_created(IGNORE_REC *rec)
 }
 
 static void sig_ignore_created(IGNORE_REC *rec)
@@ -196,7 +224,7 @@ static void sig_ignore_created(IGNORE_REC *rec)
        char *key, *levels;
 
        key = ignore_get_key(rec);
        char *key, *levels;
 
        key = ignore_get_key(rec);
-        levels = bits2level(rec->level);
+       levels = bits2level(rec->level);
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_IGNORED, key, levels);
        g_free(key);
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_IGNORED, key, levels);
        g_free(key);
index 60d96327ddba918c357405a95d7712ea0999c537..a0967e1ab7f0165626428f6f1c7478d0d9c68fb2 100644 (file)
 #include "module-formats.h"
 #include "signals.h"
 #include "commands.h"
 #include "module-formats.h"
 #include "signals.h"
 #include "commands.h"
+#include "chat-protocols.h"
 #include "servers.h"
 #include "levels.h"
 #include "misc.h"
 #include "log.h"
 #include "special-vars.h"
 #include "settings.h"
 #include "servers.h"
 #include "levels.h"
 #include "misc.h"
 #include "log.h"
 #include "special-vars.h"
 #include "settings.h"
+#include "lib-config/iconfig.h"
 
 #include "fe-windows.h"
 #include "window-items.h"
 
 #include "fe-windows.h"
 #include "window-items.h"
@@ -38,8 +40,6 @@
 /* close autologs after 5 minutes of inactivity */
 #define AUTOLOG_INACTIVITY_CLOSE (60*5)
 
 /* close autologs after 5 minutes of inactivity */
 #define AUTOLOG_INACTIVITY_CLOSE (60*5)
 
-#define LOG_DIR_CREATE_MODE 0770
-
 static int autolog_level;
 static int autoremove_tag;
 static const char *autolog_path;
 static int autolog_level;
 static int autoremove_tag;
 static const char *autolog_path;
@@ -48,6 +48,11 @@ static THEME_REC *log_theme;
 static int skip_next_printtext;
 static const char *log_theme_name;
 
 static int skip_next_printtext;
 static const char *log_theme_name;
 
+static char *log_colorizer_strip(const char *str)
+{
+        return strip_codes(str);
+}
+
 static void log_add_targets(LOG_REC *log, const char *targets, const char *tag)
 {
        char **tmp, **items;
 static void log_add_targets(LOG_REC *log, const char *targets, const char *tag)
 {
        char **tmp, **items;
@@ -64,7 +69,8 @@ static void log_add_targets(LOG_REC *log, const char *targets, const char *tag)
 }
 
 /* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-<server tag>]
 }
 
 /* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-<server tag>]
-                    [-targets <targets>] <fname> [<levels>] */
+                    [-targets <targets>] [-colors]
+                   <fname> [<levels>] */
 static void cmd_log_open(const char *data)
 {
         SERVER_REC *server;
 static void cmd_log_open(const char *data)
 {
         SERVER_REC *server;
@@ -90,8 +96,12 @@ static void cmd_log_open(const char *data)
 
        if (g_hash_table_lookup(optlist, "window")) {
                /* log by window ref# */
 
        if (g_hash_table_lookup(optlist, "window")) {
                /* log by window ref# */
-               ltoa(window, active_win->refnum);
-               log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window,
+               targetarg = g_hash_table_lookup(optlist, "targets");
+               if (targetarg == NULL || !is_numeric(targetarg, '\0')) {
+                       ltoa(window, active_win->refnum);
+                       targetarg = window;
+               }
+               log_item_add(log, LOG_ITEM_WINDOW_REFNUM, targetarg,
                             servertag);
        } else {
                targetarg = g_hash_table_lookup(optlist, "targets");
                             servertag);
        } else {
                targetarg = g_hash_table_lookup(optlist, "targets");
@@ -102,6 +112,9 @@ static void cmd_log_open(const char *data)
        if (g_hash_table_lookup(optlist, "autoopen"))
                log->autoopen = TRUE;
 
        if (g_hash_table_lookup(optlist, "autoopen"))
                log->autoopen = TRUE;
 
+       if (g_hash_table_lookup(optlist, "colors") == NULL)
+               log->colorizer = log_colorizer_strip;
+
        log_update(log);
 
        if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) {
        log_update(log);
 
        if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) {
@@ -277,6 +290,7 @@ static void cmd_window_log(const char *data)
                                        active_win->name != NULL ? active_win->name : "Window",
                                        active_win->name != NULL ? "" : window);
                log = log_create_rec(fname, MSGLEVEL_ALL);
                                        active_win->name != NULL ? active_win->name : "Window",
                                        active_win->name != NULL ? "" : window);
                log = log_create_rec(fname, MSGLEVEL_ALL);
+               log->colorizer = log_colorizer_strip;
                 log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
                log_update(log);
                g_free(fname);
                 log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
                log_update(log);
                g_free(fname);
@@ -309,6 +323,7 @@ static void cmd_window_logfile(const char *data)
        }
 
        log = log_create_rec(data, MSGLEVEL_ALL);
        }
 
        log = log_create_rec(data, MSGLEVEL_ALL);
+       log->colorizer = log_colorizer_strip;
        log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
        log_update(log);
 
        log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
        log_update(log);
 
@@ -347,7 +362,9 @@ static void sig_server_disconnected(SERVER_REC *server)
 
                logitem = log->items->data;
                if (logitem->type == LOG_ITEM_TARGET &&
 
                logitem = log->items->data;
                if (logitem->type == LOG_ITEM_TARGET &&
-                   g_strcasecmp(logitem->servertag, server->tag) == 0)
+                   logitem->servertag != NULL &&
+                   g_strcasecmp(logitem->servertag, server->tag) == 0 &&
+                   server_ischannel(server, logitem->name)) /* kludge again.. so we won't close dcc chats */
                        log_close(log);
        }
 }
                        log_close(log);
        }
 }
@@ -364,29 +381,57 @@ static void autologs_close_all(void)
        }
 }
 
        }
 }
 
-static void autolog_open(SERVER_REC *server, const char *target)
+/* '%' -> '%%', '/' -> '_' */
+static char *escape_target(const char *target)
+{
+       char *str, *p;
+
+       p = str = g_malloc(strlen(target)*2+1);
+       while (*target != '\0') {
+               if (*target == '/')
+                       *p++ = '_';
+               else {
+                       if (*target == '%')
+                               *p++ = '%';
+                       *p++ = *target;
+               }
+
+                target++;
+       }
+       *p = '\0';
+
+        return str;
+}
+
+static void autolog_open(SERVER_REC *server, const char *server_tag,
+                        const char *target)
 {
        LOG_REC *log;
 {
        LOG_REC *log;
-       char *fname, *dir, *fixed_target, *tag;
+       char *fname, *dir, *fixed_target;
 
 
-        tag = server == NULL ? NULL : server->tag;
-       log = logs_find_item(LOG_ITEM_TARGET, target, tag, NULL);
+       log = logs_find_item(LOG_ITEM_TARGET, target, server_tag, NULL);
        if (log != NULL && !log->failed) {
                log_start_logging(log);
                return;
        }
 
        /* '/' -> '_' - don't even accidentally try to log to
        if (log != NULL && !log->failed) {
                log_start_logging(log);
                return;
        }
 
        /* '/' -> '_' - don't even accidentally try to log to
-          #../../../file if you happen to join to such channel.. */
-       fixed_target = g_strdup(target);
-        replace_chars(fixed_target, '/', '_');
+          #../../../file if you happen to join to such channel..
+
+          '%' -> '%%' - so strftime() won't mess with them */
+       fixed_target = escape_target(target);
+       if (CHAT_PROTOCOL(server)->case_insensitive)
+               g_strdown(fixed_target);
+
        fname = parse_special_string(autolog_path, server, NULL,
                                     fixed_target, NULL, 0);
        g_free(fixed_target);
 
        if (log_find(fname) == NULL) {
                log = log_create_rec(fname, autolog_level);
        fname = parse_special_string(autolog_path, server, NULL,
                                     fixed_target, NULL, 0);
        g_free(fixed_target);
 
        if (log_find(fname) == NULL) {
                log = log_create_rec(fname, autolog_level);
-               log_item_add(log, LOG_ITEM_TARGET, target, tag);
+                if (!settings_get_bool("autolog_colors"))
+                       log->colorizer = log_colorizer_strip;
+               log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
 
                dir = g_dirname(log->real_fname);
                mkpath(dir, LOG_DIR_CREATE_MODE);
 
                dir = g_dirname(log->real_fname);
                mkpath(dir, LOG_DIR_CREATE_MODE);
@@ -399,23 +444,27 @@ static void autolog_open(SERVER_REC *server, const char *target)
        g_free(fname);
 }
 
        g_free(fname);
 }
 
-static void autolog_open_check(SERVER_REC *server, const char *target,
-                              int level)
+static void autolog_open_check(SERVER_REC *server, const char *server_tag,
+                              const char *target, int level)
 {
        char **targets, **tmp;
 
 {
        char **targets, **tmp;
 
-       if (level == MSGLEVEL_PARTS || /* FIXME: kind of a kludge, but we don't want to reopen logs when we're parting the channel with /WINDOW CLOSE.. */
+       /* FIXME: kind of a kludge, but we don't want to reopen logs when
+          we're parting the channel with /WINDOW CLOSE.. Maybe a small
+          timeout would be nice instead of immediately closing the log file
+          after "window item destroyed" */
+       if (level == MSGLEVEL_PARTS ||
            (autolog_level & level) == 0 || target == NULL || *target == '\0')
                return;
 
        /* there can be multiple targets separated with comma */
        targets = g_strsplit(target, ",", -1);
        for (tmp = targets; *tmp != NULL; tmp++)
            (autolog_level & level) == 0 || target == NULL || *target == '\0')
                return;
 
        /* there can be multiple targets separated with comma */
        targets = g_strsplit(target, ",", -1);
        for (tmp = targets; *tmp != NULL; tmp++)
-               autolog_open(server, *tmp);
+               autolog_open(server, server_tag, *tmp);
        g_strfreev(targets);
 }
 
        g_strfreev(targets);
 }
 
-static void log_single_line(WINDOW_REC *window, void *server,
+static void log_single_line(WINDOW_REC *window, const char *server_tag,
                            const char *target, int level, const char *text)
 {
        char windownum[MAX_INT_STRLEN];
                            const char *target, int level, const char *text)
 {
        char windownum[MAX_INT_STRLEN];
@@ -426,29 +475,31 @@ static void log_single_line(WINDOW_REC *window, void *server,
        ltoa(windownum, window->refnum);
        log = logs_find_item(LOG_ITEM_WINDOW_REFNUM,
                             windownum, NULL, NULL);
        ltoa(windownum, window->refnum);
        log = logs_find_item(LOG_ITEM_WINDOW_REFNUM,
                             windownum, NULL, NULL);
-       if (log != NULL) log_write_rec(log, text, level);
+       if (log != NULL) {
+               log_write_rec(log, text, level);
+       }
 
        if (target == NULL)
 
        if (target == NULL)
-               log_file_write(server, NULL, level, text, FALSE);
+               log_file_write(server_tag, NULL, level, text, FALSE);
        else {
                /* there can be multiple items separated with comma */
                targets = g_strsplit(target, ",", -1);
                for (tmp = targets; *tmp != NULL; tmp++)
        else {
                /* there can be multiple items separated with comma */
                targets = g_strsplit(target, ",", -1);
                for (tmp = targets; *tmp != NULL; tmp++)
-                       log_file_write(server, *tmp, level, text, FALSE);
+                       log_file_write(server_tag, *tmp, level, text, FALSE);
                g_strfreev(targets);
        }
 }
 
                g_strfreev(targets);
        }
 }
 
-static void log_line(WINDOW_REC *window, SERVER_REC *server,
-                    const char *target, int level, const char *text)
+static void log_line(TEXT_DEST_REC *dest, const char *text)
 {
        char **lines, **tmp;
 
 {
        char **lines, **tmp;
 
-       if (level == MSGLEVEL_NEVER)
+       if (dest->level == MSGLEVEL_NEVER)
                return;
 
        /* let autolog open the log records */
                return;
 
        /* let autolog open the log records */
-       autolog_open_check(server, target, level);
+       autolog_open_check(dest->server, dest->server_tag,
+                          dest->target, dest->level);
 
        if (logs == NULL)
                return;
 
        if (logs == NULL)
                return;
@@ -457,25 +508,26 @@ static void log_line(WINDOW_REC *window, SERVER_REC *server,
           line at a time */
        lines = g_strsplit(text, "\n", -1);
        for (tmp = lines; *tmp != NULL; tmp++)
           line at a time */
        lines = g_strsplit(text, "\n", -1);
        for (tmp = lines; *tmp != NULL; tmp++)
-               log_single_line(window, server, target, level, *tmp);
+               log_single_line(dest->window, dest->server_tag,
+                               dest->target, dest->level, *tmp);
        g_strfreev(lines);
 }
 
        g_strfreev(lines);
 }
 
-static void sig_printtext_stripped(TEXT_DEST_REC *dest, const char *text)
+static void sig_printtext(TEXT_DEST_REC *dest, const char *text,
+                         const char *stripped)
 {
        if (skip_next_printtext) {
                skip_next_printtext = FALSE;
                return;
        }
 
 {
        if (skip_next_printtext) {
                skip_next_printtext = FALSE;
                return;
        }
 
-       log_line(dest->window, dest->server, dest->target,
-                dest->level, text);
+       log_line(dest, text);
 }
 
 static void sig_print_format(THEME_REC *theme, const char *module,
                             TEXT_DEST_REC *dest, void *formatnum, char **args)
 {
 }
 
 static void sig_print_format(THEME_REC *theme, const char *module,
                             TEXT_DEST_REC *dest, void *formatnum, char **args)
 {
-       char *str, *stripped, *linestart, *tmp;
+       char *str, *linestart, *tmp;
 
        if (log_theme == NULL) {
                /* theme isn't loaded for some reason (/reload destroys it),
 
        if (log_theme == NULL) {
                /* theme isn't loaded for some reason (/reload destroys it),
@@ -500,10 +552,7 @@ static void sig_print_format(THEME_REC *theme, const char *module,
                g_free(tmp);
 
                /* strip colors from text, log it. */
                g_free(tmp);
 
                /* strip colors from text, log it. */
-               stripped = strip_codes(str);
-               log_line(dest->window, dest->server, dest->target,
-                        dest->level, stripped);
-               g_free(stripped);
+               log_line(dest, str);
        }
        g_free(str);
 
        }
        g_free(str);
 
@@ -532,7 +581,7 @@ static int sig_autoremove(void)
 
                server = server_find_tag(logitem->servertag);
                if (logitem->type == LOG_ITEM_TARGET &&
 
                server = server_find_tag(logitem->servertag);
                if (logitem->type == LOG_ITEM_TARGET &&
-                   server != NULL && !server->ischannel(logitem->name))
+                   server != NULL && !server_ischannel(server, logitem->name))
                        log_close(log);
        }
        return 1;
                        log_close(log);
        }
        return 1;
@@ -558,7 +607,29 @@ static void sig_log_locked(LOG_REC *log)
 static void sig_log_create_failed(LOG_REC *log)
 {
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
 static void sig_log_create_failed(LOG_REC *log)
 {
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   TXT_LOG_CREATE_FAILED, log->fname, g_strerror(errno));
+                   TXT_LOG_CREATE_FAILED,
+                   log->real_fname, g_strerror(errno));
+}
+
+static void sig_log_new(LOG_REC *log)
+{
+       if (!settings_get_bool("awaylog_colors") &&
+           strcmp(log->fname, settings_get_str("awaylog_file")) == 0)
+                log->colorizer = log_colorizer_strip;
+}
+
+static void sig_log_config_read(LOG_REC *log, CONFIG_NODE *node)
+{
+        if (!config_node_get_bool(node, "colors", FALSE))
+               log->colorizer = log_colorizer_strip;
+}
+
+static void sig_log_config_save(LOG_REC *log, CONFIG_NODE *node)
+{
+        if (log->colorizer == NULL)
+               iconfig_node_set_bool(node, "colors", TRUE);
+        else
+               iconfig_node_set_str(node, "colors", NULL);
 }
 
 static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
 }
 
 static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
@@ -615,9 +686,11 @@ void fe_log_init(void)
        autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL);
        skip_next_printtext = FALSE;
 
        autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL);
        skip_next_printtext = FALSE;
 
+       settings_add_bool("log", "awaylog_colors", TRUE);
+        settings_add_bool("log", "autolog", FALSE);
+       settings_add_bool("log", "autolog_colors", FALSE);
         settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
        settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps");
         settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
        settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps");
-        settings_add_bool("log", "autolog", FALSE);
         settings_add_str("log", "log_theme", "");
 
        autolog_level = 0;
         settings_add_str("log", "log_theme", "");
 
        autolog_level = 0;
@@ -631,17 +704,20 @@ void fe_log_init(void)
        command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop);
        command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log);
        command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile);
        command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop);
        command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log);
        command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile);
-       signal_add_first("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped);
+       signal_add_first("print text", (SIGNAL_FUNC) sig_printtext);
        signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
        signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
        signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
        signal_add("log locked", (SIGNAL_FUNC) sig_log_locked);
        signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
        signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
        signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
        signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
        signal_add("log locked", (SIGNAL_FUNC) sig_log_locked);
        signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
+       signal_add("log new", (SIGNAL_FUNC) sig_log_new);
+       signal_add("log config read", (SIGNAL_FUNC) sig_log_config_read);
+       signal_add("log config save", (SIGNAL_FUNC) sig_log_config_save);
        signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
        signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 
        signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
        signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 
-       command_set_options("log open", "noopen autoopen -targets window");
+       command_set_options("log open", "noopen autoopen -targets window colors");
 }
 
 void fe_log_deinit(void)
 }
 
 void fe_log_deinit(void)
@@ -657,12 +733,15 @@ void fe_log_deinit(void)
        command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop);
        command_unbind("window log", (SIGNAL_FUNC) cmd_window_log);
        command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile);
        command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop);
        command_unbind("window log", (SIGNAL_FUNC) cmd_window_log);
        command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile);
-       signal_remove("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped);
+       signal_remove("print text", (SIGNAL_FUNC) sig_printtext);
        signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
        signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
        signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
        signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked);
        signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
        signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
        signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
        signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
        signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked);
        signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
+       signal_remove("log new", (SIGNAL_FUNC) sig_log_new);
+       signal_remove("log config read", (SIGNAL_FUNC) sig_log_config_read);
+       signal_remove("log config save", (SIGNAL_FUNC) sig_log_config_save);
        signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
        signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
        signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
        signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
index fa5e88af0200cf9852398c527e4895bbaf2d6744..4b4e4db4f758776a561bdc1cd9324b8354f604b8 100644 (file)
 #include "special-vars.h"
 #include "settings.h"
 
 #include "special-vars.h"
 #include "settings.h"
 
-#include "window-items.h"
-#include "fe-queries.h"
+#include "servers.h"
 #include "channels.h"
 #include "nicklist.h"
 #include "channels.h"
 #include "nicklist.h"
-#include "hilight-text.h"
 #include "ignore.h"
 #include "ignore.h"
+
+#include "window-items.h"
+#include "fe-queries.h"
+#include "hilight-text.h"
 #include "printtext.h"
 
 #include "printtext.h"
 
-#define ishighalnum(c) ((unsigned char) (c) >= 128 || isalnum(c))
+#define ishighalnum(c) ((unsigned char) (c) >= 128 || i_isalnum(c))
 
 static GHashTable *printnicks;
 
 
 static GHashTable *printnicks;
 
@@ -65,7 +67,7 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
 
                /* check that the beginning marker starts a word, and
                   that the matching end marker ends a word */
 
                /* check that the beginning marker starts a word, and
                   that the matching end marker ends a word */
-               if ((pos > 0 && !isspace(bgn[-1])) || !ishighalnum(bgn[1]))
+               if ((pos > 0 && !i_isspace(bgn[-1])) || !ishighalnum(bgn[1]))
                        continue;
                if ((end = strchr(bgn+1, *bgn)) == NULL)
                        continue;
                        continue;
                if ((end = strchr(bgn+1, *bgn)) == NULL)
                        continue;
@@ -111,35 +113,28 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
        return ret;
 }
 
        return ret;
 }
 
-char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick)
+static char *channel_get_nickmode_rec(NICK_REC *nickrec)
 {
 {
-        NICK_REC *nickrec;
         char *emptystr;
 
         char *emptystr;
 
-       g_return_val_if_fail(nick != NULL, NULL);
-
        if (!settings_get_bool("show_nickmode"))
                 return "";
 
         emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
 
        if (!settings_get_bool("show_nickmode"))
                 return "";
 
         emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
 
-       nickrec = channel == NULL ? NULL :
-               nicklist_find(channel, nick);
        return nickrec == NULL ? emptystr :
        return nickrec == NULL ? emptystr :
-               (nickrec->op ? "@" : (nickrec->voice ? "+" : emptystr));
+               nickrec->op ? "@" :
+               nickrec->halfop ? "%" :
+               nickrec->voice ? "+" :
+               emptystr;
 }
 
 }
 
-static char *channel_get_nickmode_rec(NICK_REC *nickrec)
+char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick)
 {
 {
-        char *emptystr;
-
-       if (!settings_get_bool("show_nickmode"))
-                return "";
-
-        emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
+       g_return_val_if_fail(nick != NULL, NULL);
 
 
-       return nickrec == NULL ? emptystr :
-               (nickrec->op ? "@" : (nickrec->voice ? "+" : emptystr));
+        return channel_get_nickmode_rec(channel == NULL ? NULL :
+                                       nicklist_find(channel, nick));
 }
 
 static void sig_message_public(SERVER_REC *server, const char *msg,
 }
 
 static void sig_message_public(SERVER_REC *server, const char *msg,
@@ -167,8 +162,9 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
            window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL)
                print_channel = TRUE;
 
            window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL)
                print_channel = TRUE;
 
-       level = MSGLEVEL_PUBLIC | (for_me || color != NULL ?
-                                  MSGLEVEL_HILIGHT : MSGLEVEL_NOHILIGHT);
+       level = MSGLEVEL_PUBLIC;
+       if (for_me || color != NULL)
+               level |= MSGLEVEL_HILIGHT;
 
        if (settings_get_bool("emphasis"))
                msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg);
 
        if (settings_get_bool("emphasis"))
                msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg);
@@ -408,11 +404,16 @@ static void print_nick_change_channel(SERVER_REC *server, const char *channel,
                                      const char *address,
                                      int ownnick)
 {
                                      const char *address,
                                      int ownnick)
 {
+       int level;
+
        if (ignore_check(server, oldnick, address,
                         channel, newnick, MSGLEVEL_NICKS))
                return;
 
        if (ignore_check(server, oldnick, address,
                         channel, newnick, MSGLEVEL_NICKS))
                return;
 
-       printformat(server, channel, MSGLEVEL_NICKS,
+       level = MSGLEVEL_NICKS;
+        if (ownnick) level |= MSGLEVEL_NO_ACT;
+
+       printformat(server, channel, level,
                    ownnick ? TXT_YOUR_NICK_CHANGED : TXT_NICK_CHANGED,
                    oldnick, newnick, channel);
 }
                    ownnick ? TXT_YOUR_NICK_CHANGED : TXT_NICK_CHANGED,
                    oldnick, newnick, channel);
 }
@@ -444,20 +445,6 @@ static void print_nick_change(SERVER_REC *server, const char *newnick,
                msgprint = TRUE;
        }
 
                msgprint = TRUE;
        }
 
-       for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
-               QUERY_REC *query = tmp->data;
-               WINDOW_REC *window =
-                       window_item_window((WI_ITEM_REC *) query);
-
-               if (g_strcasecmp(query->name, oldnick) != 0 ||
-                   g_slist_find(windows, window) != NULL)
-                       continue;
-
-               windows = g_slist_append(windows, window);
-               print_nick_change_channel(server, query->name, newnick,
-                                         oldnick, address, ownnick);
-               msgprint = TRUE;
-       }
        g_slist_free(windows);
 
        if (!msgprint && ownnick) {
        g_slist_free(windows);
 
        if (!msgprint && ownnick) {
@@ -475,7 +462,12 @@ static void sig_message_nick(SERVER_REC *server, const char *newnick,
 static void sig_message_own_nick(SERVER_REC *server, const char *newnick,
                                 const char *oldnick, const char *address)
 {
 static void sig_message_own_nick(SERVER_REC *server, const char *newnick,
                                 const char *oldnick, const char *address)
 {
-       print_nick_change(server, newnick, oldnick, address, TRUE);
+        if (!settings_get_bool("show_own_nickchange_once"))
+               print_nick_change(server, newnick, oldnick, address, TRUE);
+       else {
+               printformat(server, NULL, MSGLEVEL_NICKS,
+                           TXT_YOUR_NICK_CHANGED, oldnick, newnick, "");
+       }
 }
 
 static void sig_message_invite(SERVER_REC *server, const char *channel,
 }
 
 static void sig_message_invite(SERVER_REC *server, const char *channel,
@@ -637,19 +629,20 @@ void fe_messages_init(void)
        settings_add_bool("lookandfeel", "show_nickmode_empty", TRUE);
        settings_add_bool("lookandfeel", "print_active_channel", FALSE);
        settings_add_bool("lookandfeel", "show_quit_once", FALSE);
        settings_add_bool("lookandfeel", "show_nickmode_empty", TRUE);
        settings_add_bool("lookandfeel", "print_active_channel", FALSE);
        settings_add_bool("lookandfeel", "show_quit_once", FALSE);
-
-       signal_add("message public", (SIGNAL_FUNC) sig_message_public);
-       signal_add("message private", (SIGNAL_FUNC) sig_message_private);
-       signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public);
-       signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
-       signal_add("message join", (SIGNAL_FUNC) sig_message_join);
-       signal_add("message part", (SIGNAL_FUNC) sig_message_part);
-       signal_add("message quit", (SIGNAL_FUNC) sig_message_quit);
-       signal_add("message kick", (SIGNAL_FUNC) sig_message_kick);
-       signal_add("message nick", (SIGNAL_FUNC) sig_message_nick);
-       signal_add("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
-       signal_add("message invite", (SIGNAL_FUNC) sig_message_invite);
-       signal_add("message topic", (SIGNAL_FUNC) sig_message_topic);
+       settings_add_bool("lookandfeel", "show_own_nickchange_once", FALSE);
+
+       signal_add_last("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_add_last("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_add_last("message own_public", (SIGNAL_FUNC) sig_message_own_public);
+       signal_add_last("message own_private", (SIGNAL_FUNC) sig_message_own_private);
+       signal_add_last("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_add_last("message part", (SIGNAL_FUNC) sig_message_part);
+       signal_add_last("message quit", (SIGNAL_FUNC) sig_message_quit);
+       signal_add_last("message kick", (SIGNAL_FUNC) sig_message_kick);
+       signal_add_last("message nick", (SIGNAL_FUNC) sig_message_nick);
+       signal_add_last("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
+       signal_add_last("message invite", (SIGNAL_FUNC) sig_message_invite);
+       signal_add_last("message topic", (SIGNAL_FUNC) sig_message_topic);
 
        signal_add("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
        signal_add("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
 
        signal_add("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
        signal_add("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
index e351dc7142f481b9054a6972be92d2db36ced374..5bbcf0b53ab61a5e54898a6ca845e8db1eb3cc28 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "module.h"
 #include "modules.h"
 
 #include "module.h"
 #include "modules.h"
+#include "modules-load.h"
 #include "module-formats.h"
 #include "signals.h"
 #include "commands.h"
 #include "module-formats.h"
 #include "signals.h"
 #include "commands.h"
 
 #include "printtext.h"
 
 
 #include "printtext.h"
 
-static void sig_module_error(void *number, const char *module,
-                            const char *data)
+#ifdef HAVE_GMODULE
+
+static void sig_module_error(void *number, const char *data,
+                            const char *rootmodule, const char *submodule)
 {
        switch (GPOINTER_TO_INT(number)) {
        case MODULE_ERROR_ALREADY_LOADED:
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
 {
        switch (GPOINTER_TO_INT(number)) {
        case MODULE_ERROR_ALREADY_LOADED:
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
-                           TXT_MODULE_ALREADY_LOADED, module);
+                           TXT_MODULE_ALREADY_LOADED, rootmodule, submodule);
                break;
        case MODULE_ERROR_LOAD:
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
                break;
        case MODULE_ERROR_LOAD:
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
-                           TXT_MODULE_LOAD_ERROR, module, data);
+                           TXT_MODULE_LOAD_ERROR, rootmodule, submodule, data);
                break;
        case MODULE_ERROR_INVALID:
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
                break;
        case MODULE_ERROR_INVALID:
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
-                           TXT_MODULE_INVALID, module);
+                           TXT_MODULE_INVALID, rootmodule, submodule);
                break;
        }
 }
 
                break;
        }
 }
 
-static void sig_module_loaded(MODULE_REC *rec)
+static void sig_module_loaded(MODULE_REC *module, MODULE_FILE_REC *file)
 {
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
 {
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   TXT_MODULE_LOADED, rec->name);
+                   TXT_MODULE_LOADED, module->name, file->name);
 }
 
 }
 
-static void sig_module_unloaded(MODULE_REC *rec)
+static void sig_module_unloaded(MODULE_REC *module, MODULE_FILE_REC *file)
 {
 {
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   TXT_MODULE_UNLOADED, rec->name);
+       if (file != NULL && file->gmodule != NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_MODULE_UNLOADED, module->name, file->name);
+       }
+}
+
+static int module_list_sub(MODULE_REC *module, int mark_type,
+                          GString *submodules)
+{
+       GSList *tmp;
+        int all_dynamic, dynamic;
+
+       g_string_truncate(submodules, 0);
+
+        all_dynamic = -1;
+       for (tmp = module->files; tmp != NULL; tmp = tmp->next) {
+               MODULE_FILE_REC *file = tmp->data;
+
+               /* if there's dynamic and static modules mixed, we'll need
+                  to specify them separately */
+               if (!mark_type) {
+                       dynamic = file->gmodule != NULL;
+                       if (all_dynamic != -1 && all_dynamic != dynamic) {
+                               return module_list_sub(module, TRUE,
+                                                      submodules);
+                       }
+                       all_dynamic = dynamic;
+               }
+
+               if (submodules->len > 0)
+                       g_string_append_c(submodules, ' ');
+               g_string_append(submodules, file->name);
+               if (mark_type) {
+                       g_string_append(submodules, file->gmodule == NULL ?
+                                       " (static)" : " (dynamic)");
+               }
+       }
+
+        return all_dynamic;
 }
 
 static void cmd_load_list(void)
 {
        GSList *tmp;
 }
 
 static void cmd_load_list(void)
 {
        GSList *tmp;
+       GString *submodules;
+        const char *type;
+        int dynamic;
+
+        submodules = g_string_new(NULL);
 
 
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_MODULE_HEADER);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_HEADER);
        for (tmp = modules; tmp != NULL; tmp = tmp->next) {
                MODULE_REC *rec = tmp->data;
 
        for (tmp = modules; tmp != NULL; tmp = tmp->next) {
                MODULE_REC *rec = tmp->data;
 
-               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                           TXT_MODULE_LINE, rec->name);
+                dynamic = module_list_sub(rec, FALSE, submodules);
+               type = dynamic == -1 ? "mixed" :
+                       dynamic ? "dynamic" : "static";
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                           TXT_MODULE_LINE, rec->name, type, submodules->str);
        }
        }
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_MODULE_FOOTER);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_FOOTER);
+
+       g_string_free(submodules, TRUE);
 }
 
 static char **module_prefixes_get(void)
 }
 
 static char **module_prefixes_get(void)
@@ -108,36 +159,67 @@ static void module_prefixes_free(char **list)
         g_free(list);
 }
 
         g_free(list);
 }
 
-/* SYNTAX: LOAD <module> */
+/* SYNTAX: LOAD <module> [<submodule>] */
 static void cmd_load(const char *data)
 {
 static void cmd_load(const char *data)
 {
-#ifdef HAVE_GMODULE
+        char *rootmodule, *submodule;
        char **module_prefixes;
        char **module_prefixes;
+       void *free_arg;
 
        g_return_if_fail(data != NULL);
 
        g_return_if_fail(data != NULL);
-       if (*data == '\0')
+
+       if (!cmd_get_params(data, &free_arg, 2 , &rootmodule, &submodule))
+               return;
+
+       if (*rootmodule == '\0')
                cmd_load_list();
        else {
                module_prefixes = module_prefixes_get();
                cmd_load_list();
        else {
                module_prefixes = module_prefixes_get();
-               module_load(data, module_prefixes);
+               if (*submodule == '\0')
+                       module_load(rootmodule, module_prefixes);
+               else {
+                       module_load_sub(rootmodule, submodule,
+                                       module_prefixes);
+               }
                 module_prefixes_free(module_prefixes);
        }
                 module_prefixes_free(module_prefixes);
        }
-#else
-       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
-                 "Dynamic modules loading not supported");
-#endif
+
+       cmd_params_free(free_arg);
 }
 
 }
 
-/* SYNTAX: UNLOAD <module> */
+/* SYNTAX: UNLOAD <module> [<submodule>] */
 static void cmd_unload(const char *data)
 {
 static void cmd_unload(const char *data)
 {
-       MODULE_REC *rec;
+       MODULE_REC *module;
+        MODULE_FILE_REC *file;
+        char *rootmodule, *submodule;
+       void *free_arg;
 
        g_return_if_fail(data != NULL);
 
        g_return_if_fail(data != NULL);
-       if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
 
 
-       rec = module_find(data);
-       if (rec != NULL) module_unload(rec);
+       if (!cmd_get_params(data, &free_arg, 2 , &rootmodule, &submodule))
+               return;
+       if (*rootmodule == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       module = module_find(rootmodule);
+       if (module != NULL) {
+               if (*submodule == '\0')
+                       module_unload(module);
+               else {
+                       file = module_file_find(module, submodule);
+                        if (file != NULL)
+                               module_file_unload(file);
+                       else
+                                module = NULL;
+               }
+       }
+
+       if (module == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                            TXT_MODULE_NOT_LOADED, rootmodule, submodule);
+       }
+
+       cmd_params_free(free_arg);
 }
 
 void fe_modules_init(void)
 }
 
 void fe_modules_init(void)
@@ -159,3 +241,22 @@ void fe_modules_deinit(void)
        command_unbind("load", (SIGNAL_FUNC) cmd_load);
        command_unbind("unload", (SIGNAL_FUNC) cmd_unload);
 }
        command_unbind("load", (SIGNAL_FUNC) cmd_load);
        command_unbind("unload", (SIGNAL_FUNC) cmd_unload);
 }
+
+#else /* !HAVE_GMODULE */
+
+static void cmd_load(const char *data)
+{
+       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                 "Dynamic modules loading not supported");
+}
+
+void fe_modules_init(void)
+{
+       command_bind("load", NULL, (SIGNAL_FUNC) cmd_load);
+}
+
+void fe_modules_deinit(void)
+{
+       command_unbind("load", (SIGNAL_FUNC) cmd_load);
+}
+#endif
index 1924464ac6e087302afcdbffd57ccefa0ca9f081..44d7ef012bdea1da61cdea9539287f4961491e74 100644 (file)
@@ -27,6 +27,7 @@
 #include "settings.h"
 
 #include "chat-protocols.h"
 #include "settings.h"
 
 #include "chat-protocols.h"
+#include "servers.h"
 #include "queries.h"
 
 #include "fe-windows.h"
 #include "queries.h"
 
 #include "fe-windows.h"
@@ -56,14 +57,19 @@ QUERY_REC *privmsg_get_query(SERVER_REC *server, const char *nick,
 
 static void signal_query_created(QUERY_REC *query, gpointer automatic)
 {
 
 static void signal_query_created(QUERY_REC *query, gpointer automatic)
 {
+        TEXT_DEST_REC dest;
+
        g_return_if_fail(IS_QUERY(query));
 
        if (window_item_window(query) == NULL) {
                window_item_create((WI_ITEM_REC *) query,
                                   GPOINTER_TO_INT(automatic));
        g_return_if_fail(IS_QUERY(query));
 
        if (window_item_window(query) == NULL) {
                window_item_create((WI_ITEM_REC *) query,
                                   GPOINTER_TO_INT(automatic));
-               printformat(query->server, query->name, MSGLEVEL_CLIENTNOTICE,
-                           TXT_QUERY_STARTED, query->name);
        }
        }
+
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest, TXT_QUERY_START,
+                        query->name, query->server_tag);
 }
 
 static void signal_query_created_curwin(QUERY_REC *query)
 }
 
 static void signal_query_created_curwin(QUERY_REC *query)
@@ -76,16 +82,22 @@ static void signal_query_created_curwin(QUERY_REC *query)
 static void signal_query_destroyed(QUERY_REC *query)
 {
        WINDOW_REC *window;
 static void signal_query_destroyed(QUERY_REC *query)
 {
        WINDOW_REC *window;
+        TEXT_DEST_REC dest;
 
        g_return_if_fail(IS_QUERY(query));
 
        window = window_item_window((WI_ITEM_REC *) query);
 
        g_return_if_fail(IS_QUERY(query));
 
        window = window_item_window((WI_ITEM_REC *) query);
-       if (window != NULL) {
-               window_item_destroy((WI_ITEM_REC *) query);
+       if (window == NULL)
+               return;
 
 
-               if (!query->unwanted)
-                       window_auto_destroy(window);
-       }
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest, TXT_QUERY_STOP, query->name);
+
+       window_item_destroy((WI_ITEM_REC *) query);
+
+       if (!query->unwanted)
+               window_auto_destroy(window);
 }
 
 static void signal_query_server_changed(QUERY_REC *query)
 }
 
 static void signal_query_server_changed(QUERY_REC *query)
@@ -101,8 +113,15 @@ static void signal_query_server_changed(QUERY_REC *query)
 
 static void signal_query_nick_changed(QUERY_REC *query, const char *oldnick)
 {
 
 static void signal_query_nick_changed(QUERY_REC *query, const char *oldnick)
 {
+        TEXT_DEST_REC dest;
+
        g_return_if_fail(query != NULL);
 
        g_return_if_fail(query != NULL);
 
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest,  TXT_NICK_CHANGED, oldnick,
+                        query->name, query->name);
+
        signal_emit("window item changed", 2,
                    window_item_window((WI_ITEM_REC *) query), query);
 }
        signal_emit("window item changed", 2,
                    window_item_window((WI_ITEM_REC *) query), query);
 }
@@ -117,16 +136,6 @@ static void signal_window_item_server_changed(WINDOW_REC *window,
        }
 }
 
        }
 }
 
-static void signal_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item)
-{
-       QUERY_REC *query;
-
-       g_return_if_fail(window != NULL);
-
-       query = QUERY(item);
-       if (query != NULL) query_destroy(query);
-}
-
 static void sig_server_connected(SERVER_REC *server)
 {
        GSList *tmp;
 static void sig_server_connected(SERVER_REC *server)
 {
        GSList *tmp;
@@ -151,6 +160,7 @@ static void cmd_window_server(const char *data)
 {
        SERVER_REC *server;
         QUERY_REC *query;
 {
        SERVER_REC *server;
         QUERY_REC *query;
+        TEXT_DEST_REC dest;
 
        g_return_if_fail(data != NULL);
 
 
        g_return_if_fail(data != NULL);
 
@@ -160,10 +170,12 @@ static void cmd_window_server(const char *data)
                return;
 
        /* /WINDOW SERVER used in a query window */
                return;
 
        /* /WINDOW SERVER used in a query window */
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest, TXT_QUERY_SERVER_CHANGED,
+                        query->name, server->tag);
+
        query_change_server(query, server);
        query_change_server(query, server);
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   TXT_QUERY_SERVER_CHANGED,
-                   query->name, server->tag);
        signal_stop();
 }
 
        signal_stop();
 }
 
@@ -200,17 +212,17 @@ static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 
        g_return_if_fail(data != NULL);
 
 
        g_return_if_fail(data != NULL);
 
-       if (*data == '\0') {
-               /* remove current query */
-               cmd_unquery("", server, item);
-               return;
-       }
-
        if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
                            PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS,
                            "query", &optlist, &nick, &msg))
                return;
        if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
                            PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS,
                            "query", &optlist, &nick, &msg))
                return;
-       if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       if (*nick == '\0') {
+               /* remove current query */
+               cmd_unquery("", server, item);
+               cmd_params_free(free_arg);
+                return;
+       }
 
        server = cmd_options_get_server("query", optlist, server);
        if (server == NULL) {
 
        server = cmd_options_get_server("query", optlist, server);
        if (server == NULL) {
@@ -228,7 +240,8 @@ static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 
        query = query_find(server, nick);
        if (query == NULL)
 
        query = query_find(server, nick);
        if (query == NULL)
-               CHAT_PROTOCOL(server)->query_create(server->tag, nick, FALSE);
+               query = CHAT_PROTOCOL(server)->
+                       query_create(server->tag, nick, FALSE);
        else {
                /* query already exists */
                WINDOW_REC *window = window_item_window(query);
        else {
                /* query already exists */
                WINDOW_REC *window = window_item_window(query);
@@ -254,13 +267,9 @@ static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
        }
 
        if (*msg != '\0') {
        }
 
        if (*msg != '\0') {
-               /* FIXME: we'll need some function that does both
-                  of these. and separate the , and . target handling
-                  from own_private messagge.. */
-               server->send_message(server, nick, msg);
-
-               signal_emit("message own_private", 4,
-                           server, msg, nick, nick);
+                msg = g_strdup_printf("-nick %s %s", nick, msg);
+               signal_emit("command msg", 3, msg, server, query);
+                g_free(msg);
        }
 
        cmd_params_free(free_arg);
        }
 
        cmd_params_free(free_arg);
@@ -346,7 +355,6 @@ void fe_queries_init(void)
        signal_add("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
        signal_add("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
         signal_add("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
        signal_add("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
        signal_add("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
         signal_add("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
-       signal_add_last("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy);
        signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
        signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
        signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
        signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
        signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
        signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
@@ -368,7 +376,6 @@ void fe_queries_deinit(void)
        signal_remove("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
        signal_remove("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
         signal_remove("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
        signal_remove("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
        signal_remove("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
         signal_remove("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
-       signal_remove("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy);
        signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected);
        signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
        signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
        signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected);
        signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
        signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
index f2327c593959cc32f88947edb0e173422ef4a607..d8f9d13725960836612abdc6a46ca48c2befaaa6 100644 (file)
@@ -146,6 +146,8 @@ static void cmd_server_add(const char *data)
 
        if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
        if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
 
        if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
        if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
+       if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE;
+       if (g_hash_table_lookup(optlist, "noproxy")) rec->no_proxy = TRUE;
 
        if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
        value = g_hash_table_lookup(optlist, "host");
 
        if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
        value = g_hash_table_lookup(optlist, "host");
@@ -175,7 +177,7 @@ static void cmd_server_remove(const char *data)
        if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
 
         if (*port == '\0')
        if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
 
         if (*port == '\0')
-               rec = server_setup_find(addr, -1);
+               rec = server_setup_find(addr, -1, NULL);
        else
                rec = server_setup_find_port(addr, atoi(port));
 
        else
                rec = server_setup_find_port(addr, atoi(port));
 
@@ -189,36 +191,30 @@ static void cmd_server_remove(const char *data)
        cmd_params_free(free_arg);
 }
 
        cmd_params_free(free_arg);
 }
 
-static void cmd_server(const char *data, SERVER_REC *server, void *item)
+static void cmd_server(const char *data)
 {
 {
-       GHashTable *optlist;
-       char *addr;
-       void *free_arg;
-
-       if (*data == '\0') {
-               if (servers == NULL && lookup_servers == NULL &&
-                   reconnects == NULL) {
-                       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                                    TXT_NO_CONNECTED_SERVERS);
-               } else {
-                       print_servers();
-                       print_lookup_servers();
-                       print_reconnects();
-               }
-
-               signal_stop();
+       if (*data != '\0')
                return;
                return;
-       }
 
 
-       if (g_strncasecmp(data, "add ", 4) == 0 ||
-           g_strncasecmp(data, "remove ", 7) == 0 ||
-           g_strcasecmp(data, "list") == 0 ||
-           g_strncasecmp(data, "list ", 5) == 0) {
-               command_runsub("server", data, server, item);
-               signal_stop();
-               return;
+       if (servers == NULL && lookup_servers == NULL &&
+           reconnects == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_NO_CONNECTED_SERVERS);
+       } else {
+               print_servers();
+               print_lookup_servers();
+               print_reconnects();
        }
 
        }
 
+        signal_stop();
+}
+
+static void cmd_server_connect(const char *data)
+{
+       GHashTable *optlist;
+       char *addr;
+       void *free_arg;
+
        if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
                            "connect", &optlist, &addr))
                return;
        if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
                            "connect", &optlist, &addr))
                return;
@@ -264,10 +260,10 @@ static void sig_connect_failed(SERVER_REC *server, gchar *msg)
        if (msg == NULL) {
                /* no message so this wasn't unexpected fail - send
                   connection_lost message instead */
        if (msg == NULL) {
                /* no message so this wasn't unexpected fail - send
                   connection_lost message instead */
-               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+               printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
                            TXT_CONNECTION_LOST, server->connrec->address);
        } else {
                            TXT_CONNECTION_LOST, server->connrec->address);
        } else {
-               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+               printformat(server, NULL, MSGLEVEL_CLIENTERROR,
                            TXT_CANT_CONNECT, server->connrec->address, server->connrec->port, msg);
        }
 }
                            TXT_CANT_CONNECT, server->connrec->address, server->connrec->port, msg);
        }
 }
@@ -276,7 +272,7 @@ static void sig_server_disconnected(SERVER_REC *server)
 {
        g_return_if_fail(server != NULL);
 
 {
        g_return_if_fail(server != NULL);
 
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_CONNECTION_LOST, server->connrec->address);
 }
 
                    TXT_CONNECTION_LOST, server->connrec->address);
 }
 
@@ -284,7 +280,7 @@ static void sig_server_quit(SERVER_REC *server, const char *msg)
 {
        g_return_if_fail(server != NULL);
 
 {
        g_return_if_fail(server != NULL);
 
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_SERVER_QUIT, server->connrec->address, msg);
 }
 
                    TXT_SERVER_QUIT, server->connrec->address, msg);
 }
 
@@ -292,8 +288,9 @@ static void sig_server_lag_disconnected(SERVER_REC *server)
 {
        g_return_if_fail(server != NULL);
 
 {
        g_return_if_fail(server != NULL);
 
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   TXT_LAG_DISCONNECTED, server->connrec->address, time(NULL)-server->lag_sent);
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_LAG_DISCONNECTED, server->connrec->address,
+                   time(NULL)-server->lag_sent.tv_sec);
 }
 
 static void sig_server_reconnect_removed(RECONNECT_REC *reconnect)
 }
 
 static void sig_server_reconnect_removed(RECONNECT_REC *reconnect)
@@ -324,9 +321,10 @@ static void sig_chat_protocol_unknown(const char *protocol)
 void fe_server_init(void)
 {
        command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
 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 remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
        command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add);
        command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
-       command_set_options("server add", "4 6 auto noauto -host -port");
+       command_set_options("server add", "4 6 auto noauto proxy noproxy -host -port");
 
        signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
        signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
 
        signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
        signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
@@ -345,6 +343,7 @@ void fe_server_init(void)
 void fe_server_deinit(void)
 {
        command_unbind("server", (SIGNAL_FUNC) cmd_server);
 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 remove", (SIGNAL_FUNC) cmd_server_remove);
 
        command_unbind("server add", (SIGNAL_FUNC) cmd_server_add);
        command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove);
 
index 0d8aaa933d13db2a87650954270fe097407509b1..382a09f742add475bb5df04d1900d4a2c8f2f2fd 100644 (file)
@@ -50,7 +50,8 @@ static void set_print(SETTINGS_REC *rec)
        default:
                value = "";
        }
        default:
                value = "";
        }
-       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s = %s", rec->key, value);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_SET_ITEM,
+                   rec->key, value);
 }
 
 static void set_boolean(const char *key, const char *value)
 }
 
 static void set_boolean(const char *key, const char *value)
@@ -92,7 +93,8 @@ static void cmd_set(char *data)
 
                if (strcmp(last_section, rec->section) != 0) {
                        /* print section */
 
                if (strcmp(last_section, rec->section) != 0) {
                        /* print section */
-                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%_[ %s ]", rec->section);
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                                   TXT_SET_TITLE, rec->section);
                        last_section = rec->section;
                }
 
                        last_section = rec->section;
                }
 
@@ -123,8 +125,10 @@ static void cmd_set(char *data)
        }
        g_slist_free(sets);
 
        }
        g_slist_free(sets);
 
-        if (!found)
-               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %s", key);
+        if (!found) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_SET_UNKNOWN, key);
+        }
 
         cmd_params_free(free_arg);
 }
 
         cmd_params_free(free_arg);
 }
@@ -143,12 +147,13 @@ static void cmd_toggle(const char *data)
 
        type = settings_get_type(key);
         if (type == -1)
 
        type = settings_get_type(key);
         if (type == -1)
-               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %_%s", key);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key);
        else if (type != SETTING_TYPE_BOOLEAN)
        else if (type != SETTING_TYPE_BOOLEAN)
-               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Setting %_%s%_ isn't boolean, use /SET", key);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_NOT_BOOLEAN, key);
        else {
                set_boolean(key, *value != '\0' ? value : "TOGGLE");
                 set_print(settings_get_record(key));
        else {
                set_boolean(key, *value != '\0' ? value : "TOGGLE");
                 set_print(settings_get_record(key));
+               signal_emit("setup changed", 0);
        }
 
         cmd_params_free(free_arg);
        }
 
         cmd_params_free(free_arg);
@@ -168,12 +173,12 @@ static void show_aliases(const char *alias)
        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_HEADER);
 
        node = iconfig_node_traverse("aliases", FALSE);
        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_HEADER);
 
        node = iconfig_node_traverse("aliases", FALSE);
-       tmp = node == NULL ? NULL : node->value;
+       tmp = node == NULL ? NULL : config_node_first(node->value);
 
        /* first get the list of aliases sorted */
        list = NULL;
        aliaslen = strlen(alias);
 
        /* first get the list of aliases sorted */
        list = NULL;
        aliaslen = strlen(alias);
-       for (; tmp != NULL; tmp = tmp->next) {
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                CONFIG_NODE *node = tmp->data;
 
                if (node->type != NODE_TYPE_KEY)
                CONFIG_NODE *node = tmp->data;
 
                if (node->type != NODE_TYPE_KEY)
@@ -241,20 +246,19 @@ static void cmd_unalias(const char *data)
 /* SYNTAX: RELOAD [<file>] */
 static void cmd_reload(const char *data)
 {
 /* SYNTAX: RELOAD [<file>] */
 static void cmd_reload(const char *data)
 {
-       char *fname;
+       const char *fname;
+
+       fname = *data == '\0' ? get_irssi_config() : data;
 
 
-       fname = *data != '\0' ? g_strdup(data) :
-               g_strdup_printf("%s/.silc/config", g_get_home_dir());
        if (settings_reread(fname)) {
                printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                            TXT_CONFIG_RELOADED, fname);
        }
        if (settings_reread(fname)) {
                printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                            TXT_CONFIG_RELOADED, fname);
        }
-       g_free(fname);
 }
 
 static void settings_save_fe(const char *fname)
 {
 }
 
 static void settings_save_fe(const char *fname)
 {
-       if (settings_save(fname)) {
+       if (settings_save(fname, FALSE /* not autosaved */)) {
                printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                            TXT_CONFIG_SAVED, fname);
        }
                printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                            TXT_CONFIG_SAVED, fname);
        }
@@ -262,7 +266,7 @@ static void settings_save_fe(const char *fname)
 
 static void settings_save_confirm(const char *line, char *fname)
 {
 
 static void settings_save_confirm(const char *line, char *fname)
 {
-       if (line[0] == 'Y')
+       if (i_toupper(line[0]) == 'Y')
                settings_save_fe(fname);
        g_free(fname);
 }
                settings_save_fe(fname);
        g_free(fname);
 }
@@ -270,29 +274,37 @@ static void settings_save_confirm(const char *line, char *fname)
 /* SYNTAX: SAVE [<file>] */
 static void cmd_save(const char *data)
 {
 /* SYNTAX: SAVE [<file>] */
 static void cmd_save(const char *data)
 {
-       char *format;
-
-       if (*data == '\0')
-               data = mainconfig->fname;
+        GHashTable *optlist;
+       char *format, *fname;
+        void *free_arg;
 
 
-       if (!irssi_config_is_changed(data)) {
-               settings_save_fe(data);
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "save", &optlist, &fname))
                return;
                return;
-       }
 
 
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   TXT_CONFIG_MODIFIED, data);
+       if (*fname == '\0')
+               fname = mainconfig->fname;
+
+       if (!irssi_config_is_changed(fname))
+               settings_save_fe(fname);
+       else {
+                /* config file modified outside irssi */
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONFIG_MODIFIED, fname);
+
+               format = format_get_text(MODULE_NAME, NULL, NULL, NULL,
+                                        TXT_OVERWRITE_CONFIG);
+               keyboard_entry_redirect((SIGNAL_FUNC) settings_save_confirm,
+                                       format, 0, g_strdup(fname));
+               g_free(format);
+       }
 
 
-       format = format_get_text(MODULE_NAME, NULL, NULL, NULL,
-                                TXT_OVERWRITE_CONFIG);
-       keyboard_entry_redirect((SIGNAL_FUNC) settings_save_confirm,
-                               format, 0, g_strdup(data));
-        g_free(format);
+       cmd_params_free(free_arg);
 }
 
 static void settings_clean_confirm(const char *line)
 {
 }
 
 static void settings_clean_confirm(const char *line)
 {
-       if (line[0] == 'Y')
+       if (i_toupper(line[0]) == 'Y')
                 settings_clean_invalid();
 }
 
                 settings_clean_invalid();
 }
 
index 3c48261a6aa25df6037cfc05f853b36ff7cf3e0e..fc4766d938a1d77d93379127af8ac4d6756b66e0 100644 (file)
@@ -134,7 +134,8 @@ void window_destroy(WINDOW_REC *window)
 void window_auto_destroy(WINDOW_REC *window)
 {
        if (settings_get_bool("autoclose_windows") && windows->next != NULL &&
 void window_auto_destroy(WINDOW_REC *window)
 {
        if (settings_get_bool("autoclose_windows") && windows->next != NULL &&
-           window->items == NULL && window->level == 0)
+           window->items == NULL && window->bound_items == NULL &&
+           window->level == 0)
                 window_destroy(window);
 }
 
                 window_destroy(window);
 }
 
@@ -194,6 +195,21 @@ void window_set_name(WINDOW_REC *window, const char *name)
        signal_emit("window name changed", 1, window);
 }
 
        signal_emit("window name changed", 1, window);
 }
 
+void window_set_history(WINDOW_REC *window, const char *name)
+{
+       char *oldname;
+       oldname = window->history_name;
+
+       if (name == NULL || *name == '\0')
+               window->history_name = NULL;
+       else
+               window->history_name = g_strdup(name);
+
+       signal_emit("window history changed", 1, window, oldname);
+
+       g_free_not_null(oldname);
+}
+
 void window_set_level(WINDOW_REC *window, int level)
 {
        g_return_if_fail(window != NULL);
 void window_set_level(WINDOW_REC *window, int level)
 {
        g_return_if_fail(window != NULL);
@@ -375,7 +391,7 @@ int windows_refnum_last(void)
        return max;
 }
 
        return max;
 }
 
-static int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
+int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
 {
        return w1->refnum < w2->refnum ? -1 : 1;
 }
 {
        return w1->refnum < w2->refnum ? -1 : 1;
 }
@@ -395,6 +411,7 @@ GSList *windows_get_sorted(void)
         return sorted;
 }
 
         return sorted;
 }
 
+/* Add a new bind to window - if duplicate is found it's returned */
 WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
                                 const char *name)
 {
 WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
                                 const char *name)
 {
@@ -402,7 +419,11 @@ WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
 
         g_return_val_if_fail(window != NULL, NULL);
         g_return_val_if_fail(servertag != NULL, NULL);
 
         g_return_val_if_fail(window != NULL, NULL);
         g_return_val_if_fail(servertag != NULL, NULL);
-        g_return_val_if_fail(name != NULL, NULL);
+       g_return_val_if_fail(name != NULL, NULL);
+
+       rec = window_bind_find(window, servertag, name);
+       if (rec != NULL)
+               return rec;
 
        rec = g_new0(WINDOW_BIND_REC, 1);
         rec->name = g_strdup(name);
 
        rec = g_new0(WINDOW_BIND_REC, 1);
         rec->name = g_strdup(name);
@@ -494,18 +515,31 @@ static void sig_server_disconnected(SERVER_REC *server)
        }
 }
 
        }
 }
 
+static void window_print_daychange(WINDOW_REC *window, struct tm *tm)
+{
+        THEME_REC *theme;
+        TEXT_DEST_REC dest;
+       char *format, str[256];
+
+       theme = active_win->theme != NULL ? active_win->theme : current_theme;
+       format_create_dest(&dest, NULL, NULL, MSGLEVEL_NEVER, window);
+       format = format_get_text_theme(theme, MODULE_NAME, &dest,
+                                      TXT_DAYCHANGE);
+       if (strftime(str, sizeof(str), format, tm) <= 0)
+                str[0] = '\0';
+       g_free(format);
+
+       printtext_string_window(window, MSGLEVEL_NEVER, str);
+}
+
 static void sig_print_text(void)
 {
        GSList *tmp;
 static void sig_print_text(void)
 {
        GSList *tmp;
-       char month[100];
        time_t t;
        struct tm *tm;
 
        t = time(NULL);
        tm = localtime(&t);
        time_t t;
        struct tm *tm;
 
        t = time(NULL);
        tm = localtime(&t);
-       if (strftime(month, sizeof(month), "%b", tm) <= 0)
-               month[0] = '\0';
-
        if (tm->tm_hour != 0 || tm->tm_min != 0)
                return;
 
        if (tm->tm_hour != 0 || tm->tm_min != 0)
                return;
 
@@ -513,11 +547,8 @@ static void sig_print_text(void)
        signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
 
        /* day changed, print notice about it to every window */
        signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
 
        /* day changed, print notice about it to every window */
-       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-               printformat_window(tmp->data, MSGLEVEL_NEVER, TXT_DAYCHANGE,
-                                  tm->tm_mday, tm->tm_mon+1,
-                                  1900+tm->tm_year, month);
-       }
+       for (tmp = windows; tmp != NULL; tmp = tmp->next)
+               window_print_daychange(tmp->data, tm);
 }
 
 static int sig_check_daychange(void)
 }
 
 static int sig_check_daychange(void)
index c9ffe69a39b563632f3261dd2cffa8e38a7d7dc6..320044598372751abfa3ab92868ae117b58c6386 100644 (file)
@@ -1,10 +1,8 @@
 #ifndef __WINDOWS_H
 #define __WINDOWS_H
 
 #ifndef __WINDOWS_H
 #define __WINDOWS_H
 
-#include "servers.h"
-
-#define STRUCT_SERVER_REC SERVER_REC
 #include "window-item-def.h"
 #include "window-item-def.h"
+#include "command-history.h"
 
 enum {
         DATA_LEVEL_NONE = 0,
 
 enum {
         DATA_LEVEL_NONE = 0,
@@ -19,7 +17,7 @@ typedef struct {
        unsigned int sticky:1;
 } WINDOW_BIND_REC;
 
        unsigned int sticky:1;
 } WINDOW_BIND_REC;
 
-typedef struct {
+struct _WINDOW_REC {
        int refnum;
        char *name;
 
        int refnum;
        char *name;
 
@@ -37,8 +35,8 @@ typedef struct {
        unsigned int destroying:1;
 
        /* window-specific command line history */
        unsigned int destroying:1;
 
        /* window-specific command line history */
-       GList *history, *history_pos;
-       int history_lines, history_over_counter;
+       HISTORY_REC *history;
+       char *history_name;
 
        int data_level; /* current data level */
        char *hilight_color; /* current hilight color in %format */
 
        int data_level; /* current data level */
        char *hilight_color; /* current hilight color in %format */
@@ -50,7 +48,7 @@ typedef struct {
        void *theme; /* THEME_REC */
 
        void *gui_data;
        void *theme; /* THEME_REC */
 
        void *gui_data;
-} WINDOW_REC;
+};
 
 extern GSList *windows;
 extern WINDOW_REC *active_win;
 
 extern GSList *windows;
 extern WINDOW_REC *active_win;
@@ -65,6 +63,7 @@ void window_change_server(WINDOW_REC *window, void *server);
 
 void window_set_refnum(WINDOW_REC *window, int refnum);
 void window_set_name(WINDOW_REC *window, const char *name);
 
 void window_set_refnum(WINDOW_REC *window, int refnum);
 void window_set_name(WINDOW_REC *window, const char *name);
+void window_set_history(WINDOW_REC *window, const char *name);
 void window_set_level(WINDOW_REC *window, int level);
 
 /* return active item's name, or if none is active, window's name */
 void window_set_level(WINDOW_REC *window, int level);
 
 /* return active item's name, or if none is active, window's name */
@@ -80,8 +79,10 @@ int window_refnum_prev(int refnum, int wrap);
 int window_refnum_next(int refnum, int wrap);
 int windows_refnum_last(void);
 
 int window_refnum_next(int refnum, int wrap);
 int windows_refnum_last(void);
 
+int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2);
 GSList *windows_get_sorted(void);
 
 GSList *windows_get_sorted(void);
 
+/* Add a new bind to window - if duplicate is found it's returned */
 WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
                                 const char *name);
 void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec);
 WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
                                 const char *name);
 void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec);
index b933c068a139c5f9a7e9735625bb8532adabdf28..b4d2e7edade7ab69d3685166f994c7d994a3dae1 100644 (file)
 #include "settings.h"
 
 #include "levels.h"
 #include "settings.h"
 
 #include "levels.h"
+#include "servers.h"
 
 #include "fe-windows.h"
 
 #include "fe-windows.h"
+#include "window-items.h"
 #include "formats.h"
 #include "themes.h"
 #include "translation.h"
 #include "formats.h"
 #include "themes.h"
 #include "translation.h"
@@ -36,9 +38,9 @@ static const char *format_fores = "kbgcrmyw";
 static const char *format_boldfores = "KBGCRMYW";
 
 static int signal_gui_print_text;
 static const char *format_boldfores = "KBGCRMYW";
 
 static int signal_gui_print_text;
-static int hide_text_style, hide_server_tags;
+static int hide_text_style, hide_server_tags, hide_mirc_colors;
 
 
-static int timestamps, msgs_timestamps;
+static int timestamp_level;
 static int timestamp_timeout;
 
 int format_find_tag(const char *module, const char *tag)
 static int timestamp_timeout;
 
 int format_find_tag(const char *module, const char *tag)
@@ -59,11 +61,73 @@ int format_find_tag(const char *module, const char *tag)
        return -1;
 }
 
        return -1;
 }
 
-int format_expand_styles(GString *out, char format)
+static void format_expand_code(const char **format, GString *out, int *flags)
 {
 {
-       char *p;
+       int set;
 
 
-       switch (format) {
+       if (flags == NULL) {
+                /* flags are being ignored - skip the code */
+               while (**format != ']')
+                       (*format)++;
+                return;
+       }
+
+        set = TRUE;
+       (*format)++;
+       while (**format != ']' && **format != '\0') {
+               if (**format == '+')
+                       set = TRUE;
+               else if (**format == '-')
+                       set = FALSE;
+               else switch (**format) {
+               case 'i':
+                       /* indent function */
+                       (*format)++;
+                       if (**format == '=')
+                               (*format)++;
+
+                       g_string_append_c(out, 4);
+                       g_string_append_c(out, FORMAT_STYLE_INDENT_FUNC);
+                       while (**format != ']' && **format != '\0' &&
+                              **format != ',') {
+                               g_string_append_c(out, **format);
+                               (*format)++;
+                       }
+                       g_string_append_c(out, ',');
+                        (*format)--;
+                       break;
+               case 's':
+               case 'S':
+                       *flags |= !set ? PRINT_FLAG_UNSET_LINE_START :
+                               **format == 's' ? PRINT_FLAG_SET_LINE_START :
+                               PRINT_FLAG_SET_LINE_START_IRSSI;
+                       break;
+               case 't':
+                       *flags |= set ? PRINT_FLAG_SET_TIMESTAMP :
+                                PRINT_FLAG_UNSET_TIMESTAMP;
+                        break;
+               case 'T':
+                       *flags |= set ? PRINT_FLAG_SET_SERVERTAG :
+                                PRINT_FLAG_UNSET_SERVERTAG;
+                        break;
+               }
+
+               (*format)++;
+       }
+}
+
+int format_expand_styles(GString *out, const char **format, int *flags)
+{
+       char *p, fmt;
+
+        fmt = **format;
+       switch (fmt) {
+       case '{':
+       case '}':
+       case '%':
+                /* escaped char */
+               g_string_append_c(out, fmt);
+               break;
        case 'U':
                /* Underline on/off */
                g_string_append_c(out, 4);
        case 'U':
                /* Underline on/off */
                g_string_append_c(out, 4);
@@ -80,9 +144,6 @@ int format_expand_styles(GString *out, char format)
                g_string_append_c(out, 4);
                g_string_append_c(out, FORMAT_STYLE_REVERSE);
                break;
                g_string_append_c(out, 4);
                g_string_append_c(out, FORMAT_STYLE_REVERSE);
                break;
-       case '%':
-               g_string_append_c(out, '%');
-               break;
        case ':':
                /* Newline */
                g_string_append_c(out, '\n');
        case ':':
                /* Newline */
                g_string_append_c(out, '\n');
@@ -103,9 +164,18 @@ int format_expand_styles(GString *out, char format)
                g_string_append_c(out, 4);
                g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
                break;
                g_string_append_c(out, 4);
                g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
                break;
+       case '>':
+                /* clear to end of line */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_CLRTOEOL);
+               break;
+       case '[':
+               /* code */
+                format_expand_code(format, out, flags);
+               break;
        default:
                /* check if it's a background color */
        default:
                /* check if it's a background color */
-               p = strchr(format_backs, format);
+               p = strchr(format_backs, fmt);
                if (p != NULL) {
                        g_string_append_c(out, 4);
                        g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
                if (p != NULL) {
                        g_string_append_c(out, 4);
                        g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
@@ -114,8 +184,8 @@ int format_expand_styles(GString *out, char format)
                }
 
                /* check if it's a foreground color */
                }
 
                /* check if it's a foreground color */
-               if (format == 'p') format = 'm';
-               p = strchr(format_fores, format);
+               if (fmt == 'p') fmt = 'm';
+               p = strchr(format_fores, fmt);
                if (p != NULL) {
                        g_string_append_c(out, 4);
                        g_string_append_c(out, (char) ((int) (p-format_fores)+'0'));
                if (p != NULL) {
                        g_string_append_c(out, 4);
                        g_string_append_c(out, (char) ((int) (p-format_fores)+'0'));
@@ -124,8 +194,8 @@ int format_expand_styles(GString *out, char format)
                }
 
                /* check if it's a bold foreground color */
                }
 
                /* check if it's a bold foreground color */
-               if (format == 'P') format = 'M';
-               p = strchr(format_boldfores, format);
+               if (fmt == 'P') fmt = 'M';
+               p = strchr(format_boldfores, fmt);
                if (p != NULL) {
                        g_string_append_c(out, 4);
                        g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0'));
                if (p != NULL) {
                        g_string_append_c(out, 4);
                        g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0'));
@@ -208,14 +278,21 @@ void format_create_dest(TEXT_DEST_REC *dest,
                        void *server, const char *target,
                        int level, WINDOW_REC *window)
 {
                        void *server, const char *target,
                        int level, WINDOW_REC *window)
 {
+        format_create_dest_tag(dest, server, NULL, target, level, window);
+}
+
+void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
+                           const char *server_tag, const char *target,
+                           int level, WINDOW_REC *window)
+{
+        memset(dest, 0, sizeof(TEXT_DEST_REC));
+
        dest->server = server;
        dest->server = server;
+       dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag;
        dest->target = target;
        dest->level = level;
        dest->window = window != NULL ? window :
                window_find_closest(server, target, level);
        dest->target = target;
        dest->level = level;
        dest->window = window != NULL ? window :
                window_find_closest(server, target, level);
-
-       dest->hilight_priority = 0;
-        dest->hilight_color = NULL;
 }
 
 /* Return length of text part in string (ie. without % codes) */
 }
 
 /* Return length of text part in string (ie. without % codes) */
@@ -231,7 +308,8 @@ int format_get_length(const char *str)
        while (*str != '\0') {
                if (*str == '%' && str[1] != '\0') {
                        str++;
        while (*str != '\0') {
                if (*str == '%' && str[1] != '\0') {
                        str++;
-                       if (*str != '%' && format_expand_styles(tmp, *str)) {
+                       if (*str != '%' &&
+                           format_expand_styles(tmp, &str, NULL)) {
                                 str++;
                                continue;
                        }
                                 str++;
                                continue;
                        }
@@ -265,7 +343,8 @@ int format_real_length(const char *str, int len)
        while (*str != '\0' && len > 0) {
                if (*str == '%' && str[1] != '\0') {
                        str++;
        while (*str != '\0' && len > 0) {
                if (*str == '%' && str[1] != '\0') {
                        str++;
-                       if (*str != '%' && format_expand_styles(tmp, *str)) {
+                       if (*str != '%' &&
+                           format_expand_styles(tmp, &str, NULL)) {
                                 str++;
                                continue;
                        }
                                 str++;
                                continue;
                        }
@@ -285,7 +364,7 @@ int format_real_length(const char *str, int len)
         return (int) (str-start);
 }
 
         return (int) (str-start);
 }
 
-char *format_string_expand(const char *text)
+char *format_string_expand(const char *text, int *flags)
 {
        GString *out;
        char code, *ret;
 {
        GString *out;
        char code, *ret;
@@ -294,11 +373,12 @@ char *format_string_expand(const char *text)
 
        out = g_string_new(NULL);
 
 
        out = g_string_new(NULL);
 
+       if (flags != NULL) *flags = 0;
        code = 0;
        while (*text != '\0') {
                if (code == '%') {
                        /* color code */
        code = 0;
        while (*text != '\0') {
                if (code == '%') {
                        /* color code */
-                       if (!format_expand_styles(out, *text)) {
+                       if (!format_expand_styles(out, &text, flags)) {
                                g_string_append_c(out, '%');
                                g_string_append_c(out, '%');
                                g_string_append_c(out, *text);
                                g_string_append_c(out, '%');
                                g_string_append_c(out, '%');
                                g_string_append_c(out, *text);
@@ -332,7 +412,7 @@ static char *format_get_text_args(TEXT_DEST_REC *dest,
        while (*text != '\0') {
                if (code == '%') {
                        /* color code */
        while (*text != '\0') {
                if (code == '%') {
                        /* color code */
-                       if (!format_expand_styles(out, *text)) {
+                       if (!format_expand_styles(out, &text, &dest->flags)) {
                                g_string_append_c(out, '%');
                                g_string_append_c(out, '%');
                                g_string_append_c(out, *text);
                                g_string_append_c(out, '%');
                                g_string_append_c(out, '%');
                                g_string_append_c(out, *text);
@@ -342,12 +422,10 @@ static char *format_get_text_args(TEXT_DEST_REC *dest,
                        /* argument */
                        char *ret;
 
                        /* argument */
                        char *ret;
 
-                       ret = parse_special((char **) &text,
-                                           active_win == NULL ? NULL :
-                                           active_win->active_server,
-                                           active_win == NULL ? NULL :
-                                           active_win->active, arglist,
-                                           &need_free, NULL, 0);
+                       ret = parse_special((char **) &text, dest->server,
+                                           dest->target == NULL ? NULL :
+                                           window_item_find(dest->server, dest->target),
+                                           arglist, &need_free, NULL, 0);
 
                        if (ret != NULL) {
                                /* string shouldn't end with \003 or it could
 
                        if (ret != NULL) {
                                /* string shouldn't end with \003 or it could
@@ -384,10 +462,8 @@ char *format_get_text_theme(THEME_REC *theme, const char *module,
        va_list va;
        char *str;
 
        va_list va;
        char *str;
 
-       if (theme == NULL) {
-               theme = dest->window->theme == NULL ? current_theme :
-                       dest->window->theme;
-       }
+       if (theme == NULL)
+               theme = window_get_theme(dest->window);
 
        va_start(va, formatnum);
        str = format_get_text_theme_args(theme, module, dest, formatnum, va);
 
        va_start(va, formatnum);
        str = format_get_text_theme_args(theme, module, dest, formatnum, va);
@@ -438,8 +514,7 @@ char *format_get_text(const char *module, WINDOW_REC *window,
        char *str;
 
        format_create_dest(&dest, server, target, 0, window);
        char *str;
 
        format_create_dest(&dest, server, target, 0, window);
-       theme = dest.window->theme == NULL ? current_theme :
-               dest.window->theme;
+       theme = window_get_theme(dest.window);
 
        va_start(va, formatnum);
        str = format_get_text_theme_args(theme, module, &dest, formatnum, va);
 
        va_start(va, formatnum);
        str = format_get_text_theme_args(theme, module, &dest, formatnum, va);
@@ -487,28 +562,45 @@ char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
 {
        int format;
 
 {
        int format;
 
-       if (dest->level & LINE_START_IRSSI_LEVEL)
-               format = TXT_LINE_START_IRSSI;
-       else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
-               format = TXT_LINE_START;
-       else
+        /* check for flags if we want to override defaults */
+       if (dest->flags & PRINT_FLAG_UNSET_LINE_START)
                return NULL;
 
                return NULL;
 
+       if (dest->flags & PRINT_FLAG_SET_LINE_START)
+               format = TXT_LINE_START;
+        else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
+               format = TXT_LINE_START_IRSSI;
+       else {
+                /* use defaults */
+               if (dest->level & LINE_START_IRSSI_LEVEL)
+                       format = TXT_LINE_START_IRSSI;
+               else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
+                       format = TXT_LINE_START;
+               else
+                       return NULL;
+       }
+
        return format_get_text_theme(theme, MODULE_NAME, dest, format);
 }
 
        return format_get_text_theme(theme, MODULE_NAME, dest, format);
 }
 
-#define show_timestamp(level) \
-       ((level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) == 0 && \
-       (timestamps || (msgs_timestamps && ((level) & MSGLEVEL_MSGS))))
-
 static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
 {
 static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
 {
+        char *format, str[256];
        struct tm *tm;
        int diff;
 
        struct tm *tm;
        int diff;
 
-       if (!show_timestamp(dest->level))
+       if ((timestamp_level & dest->level) == 0)
+               return NULL;
+
+        /* check for flags if we want to override defaults */
+       if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP)
                return NULL;
 
                return NULL;
 
+       if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 &&
+           (dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0)
+                return NULL;
+
+
        if (timestamp_timeout > 0) {
                diff = t - dest->window->last_timestamp;
                dest->window->last_timestamp = t;
        if (timestamp_timeout > 0) {
                diff = t - dest->window->last_timestamp;
                dest->window->last_timestamp = t;
@@ -517,38 +609,47 @@ static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
        }
 
        tm = localtime(&t);
        }
 
        tm = localtime(&t);
-       return format_get_text_theme(theme, MODULE_NAME, dest, TXT_TIMESTAMP,
-                                    tm->tm_year+1900,
-                                    tm->tm_mon+1, tm->tm_mday,
-                                    tm->tm_hour, tm->tm_min, tm->tm_sec);
+       format = format_get_text_theme(theme, MODULE_NAME, dest,
+                                      TXT_TIMESTAMP);
+       if (strftime(str, sizeof(str), format, tm) <= 0)
+                str[0] = '\0';
+       g_free(format);
+       return g_strdup(str);
 }
 
 static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
 {
 }
 
 static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
 {
-       SERVER_REC *server;
        int count = 0;
 
        int count = 0;
 
-       server = dest->server;
+       if (dest->server_tag == NULL || hide_server_tags)
+                return NULL;
 
 
-       if (server == NULL || hide_server_tags ||
-           (dest->window->active != NULL &&
-            dest->window->active->server == server))
+        /* check for flags if we want to override defaults */
+       if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG)
                return NULL;
 
                return NULL;
 
-       if (servers != NULL) {
-               count++;
-               if (servers->next != NULL)
+       if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) {
+               if (dest->window->active != NULL &&
+                   dest->window->active->server == dest->server)
+                       return NULL;
+
+               if (servers != NULL) {
                        count++;
                        count++;
-       }
-       if (count < 2 && lookup_servers != NULL) {
-                count++;
-               if (lookup_servers->next != NULL)
+                       if (servers->next != NULL)
+                               count++;
+               }
+               if (count < 2 && lookup_servers != NULL) {
                        count++;
                        count++;
+                       if (lookup_servers->next != NULL)
+                               count++;
+               }
+
+               if (count < 2)
+                        return NULL;
        }
 
        }
 
-       return count < 2 ? NULL :
-               format_get_text_theme(theme, MODULE_NAME, dest,
-                                     TXT_SERVERTAG, server->tag);
+       return format_get_text_theme(theme, MODULE_NAME, dest,
+                                    TXT_SERVERTAG, dest->server_tag);
 }
 
 char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
 }
 
 char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
@@ -576,16 +677,16 @@ void format_newline(WINDOW_REC *window)
 
        signal_emit_id(signal_gui_print_text, 6, window,
                       GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
 
        signal_emit_id(signal_gui_print_text, 6, window,
                       GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
-                      GINT_TO_POINTER(PRINTFLAG_NEWLINE),
+                      GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE),
                       "", GINT_TO_POINTER(-1));
 }
 
 /* parse ANSI color string */
                       "", GINT_TO_POINTER(-1));
 }
 
 /* parse ANSI color string */
-static char *get_ansi_color(THEME_REC *theme, char *str,
-                           int *fg_ret, int *bg_ret, int *flags_ret)
+static const char *get_ansi_color(THEME_REC *theme, const char *str,
+                                 int *fg_ret, int *bg_ret, int *flags_ret)
 {
        static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
 {
        static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
-       char *start;
+       const char *start;
        int fg, bg, flags, num;
 
        if (*str != '[')
        int fg, bg, flags, num;
 
        if (*str != '[')
@@ -600,7 +701,7 @@ static char *get_ansi_color(THEME_REC *theme, char *str,
        for (;; str++) {
                if (*str == '\0') return start;
 
        for (;; str++) {
                if (*str == '\0') return start;
 
-               if (isdigit((int) *str)) {
+               if (i_isdigit(*str)) {
                        num = num*10 + (*str-'0');
                        continue;
                }
                        num = num*10 + (*str-'0');
                        continue;
                }
@@ -613,19 +714,19 @@ static char *get_ansi_color(THEME_REC *theme, char *str,
                        /* reset colors back to default */
                        fg = theme->default_color;
                        bg = -1;
                        /* reset colors back to default */
                        fg = theme->default_color;
                        bg = -1;
-                       flags &= ~PRINTFLAG_INDENT;
+                       flags &= ~GUI_PRINT_FLAG_INDENT;
                        break;
                case 1:
                        /* hilight */
                        break;
                case 1:
                        /* hilight */
-                       flags |= PRINTFLAG_BOLD;
+                       flags |= GUI_PRINT_FLAG_BOLD;
                        break;
                case 5:
                        /* blink */
                        break;
                case 5:
                        /* blink */
-                       flags |= PRINTFLAG_BLINK;
+                       flags |= GUI_PRINT_FLAG_BLINK;
                        break;
                case 7:
                        /* reverse */
                        break;
                case 7:
                        /* reverse */
-                       flags |= PRINTFLAG_REVERSE;
+                       flags |= GUI_PRINT_FLAG_REVERSE;
                        break;
                default:
                        if (num >= 30 && num <= 37)
                        break;
                default:
                        if (num >= 30 && num <= 37)
@@ -659,7 +760,7 @@ static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
        fg = fg_ret == NULL ? -1 : *fg_ret;
        bg = bg_ret == NULL ? -1 : *bg_ret;
 
        fg = fg_ret == NULL ? -1 : *fg_ret;
        bg = bg_ret == NULL ? -1 : *bg_ret;
 
-       if (!isdigit((int) **str) && **str != ',') {
+       if (!i_isdigit(**str) && **str != ',') {
                fg = -1;
                bg = -1;
        } else {
                fg = -1;
                bg = -1;
        } else {
@@ -667,7 +768,7 @@ static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
                if (**str != ',') {
                        fg = **str-'0';
                         (*str)++;
                if (**str != ',') {
                        fg = **str-'0';
                         (*str)++;
-                       if (isdigit((int) **str)) {
+                       if (i_isdigit(**str)) {
                                fg = fg*10 + (**str-'0');
                                (*str)++;
                        }
                                fg = fg*10 + (**str-'0');
                                (*str)++;
                        }
@@ -675,12 +776,12 @@ static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
                if (**str == ',') {
                        /* background color */
                        (*str)++;
                if (**str == ',') {
                        /* background color */
                        (*str)++;
-                       if (!isdigit((int) **str))
+                       if (!i_isdigit(**str))
                                bg = -1;
                        else {
                                bg = **str-'0';
                                (*str)++;
                                bg = -1;
                        else {
                                bg = **str-'0';
                                (*str)++;
-                               if (isdigit((int) **str)) {
+                               if (i_isdigit(**str)) {
                                        bg = bg*10 + (**str-'0');
                                        (*str)++;
                                }
                                        bg = bg*10 + (**str-'0');
                                        (*str)++;
                                }
@@ -772,7 +873,10 @@ char *strip_codes(const char *input)
                                 p += 2;
                                 continue;
                         }
                                 p += 2;
                                 continue;
                         }
-                }
+               }
+
+               if (*p == 27 && p[1] != '\0')
+                       p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
 
                 if (!IS_COLOR_CODE(*p))
                         *out++ = *p;   
 
                 if (!IS_COLOR_CODE(*p))
                         *out++ = *p;   
@@ -808,16 +912,19 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
                        /* bell */
                        if (settings_get_bool("bell_beeps"))
                                 signal_emit("beep", 0);
                        /* bell */
                        if (settings_get_bool("bell_beeps"))
                                 signal_emit("beep", 0);
+               } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
+                        /* clear to end of line */
+                       flags |= GUI_PRINT_FLAG_CLRTOEOL;
                }
 
                }
 
-               if (*str != '\0') {
+               if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) {
                         /* send the text to gui handler */
                        signal_emit_id(signal_gui_print_text, 6, dest->window,
                                       GINT_TO_POINTER(fgcolor),
                                       GINT_TO_POINTER(bgcolor),
                                       GINT_TO_POINTER(flags), str,
                                       dest->level);
                         /* send the text to gui handler */
                        signal_emit_id(signal_gui_print_text, 6, dest->window,
                                       GINT_TO_POINTER(fgcolor),
                                       GINT_TO_POINTER(bgcolor),
                                       GINT_TO_POINTER(flags), str,
                                       dest->level);
-                       flags &= ~PRINTFLAG_INDENT;
+                       flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL);
                }
 
                if (type == '\n')
                }
 
                if (type == '\n')
@@ -831,84 +938,106 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
                case 2:
                        /* bold */
                        if (!hide_text_style)
                case 2:
                        /* bold */
                        if (!hide_text_style)
-                               flags ^= PRINTFLAG_BOLD;
+                               flags ^= GUI_PRINT_FLAG_BOLD;
                        break;
                case 3:
                        /* MIRC color */
                        get_mirc_color((const char **) &ptr,
                        break;
                case 3:
                        /* MIRC color */
                        get_mirc_color((const char **) &ptr,
-                                      hide_text_style ? NULL : &fgcolor,
-                                      hide_text_style ? NULL : &bgcolor);
-                       if (!hide_text_style)
-                               flags |= PRINTFLAG_MIRC_COLOR;
+                                       hide_mirc_colors || hide_text_style ? NULL : &fgcolor,
+                                       hide_mirc_colors || hide_text_style ? NULL : &bgcolor);
+                       if (!hide_mirc_colors && !hide_text_style)
+                               flags |= GUI_PRINT_FLAG_MIRC_COLOR;
                        break;
                case 4:
                        /* user specific colors */
                        break;
                case 4:
                        /* user specific colors */
-                       flags &= ~PRINTFLAG_MIRC_COLOR;
+                       flags &= ~GUI_PRINT_FLAG_MIRC_COLOR;
                        switch (*ptr) {
                        case FORMAT_STYLE_BLINK:
                        switch (*ptr) {
                        case FORMAT_STYLE_BLINK:
-                               flags ^= PRINTFLAG_BLINK;
+                               flags ^= GUI_PRINT_FLAG_BLINK;
                                break;
                        case FORMAT_STYLE_UNDERLINE:
                                break;
                        case FORMAT_STYLE_UNDERLINE:
-                               flags ^= PRINTFLAG_UNDERLINE;
+                               flags ^= GUI_PRINT_FLAG_UNDERLINE;
                                break;
                        case FORMAT_STYLE_BOLD:
                                break;
                        case FORMAT_STYLE_BOLD:
-                               flags ^= PRINTFLAG_BOLD;
+                               flags ^= GUI_PRINT_FLAG_BOLD;
                                break;
                        case FORMAT_STYLE_REVERSE:
                                break;
                        case FORMAT_STYLE_REVERSE:
-                               flags ^= PRINTFLAG_REVERSE;
+                               flags ^= GUI_PRINT_FLAG_REVERSE;
                                break;
                        case FORMAT_STYLE_INDENT:
                                break;
                        case FORMAT_STYLE_INDENT:
-                               flags |= PRINTFLAG_INDENT;
+                               flags |= GUI_PRINT_FLAG_INDENT;
+                               break;
+                       case FORMAT_STYLE_INDENT_FUNC: {
+                               const char *start = ptr;
+                               while (*ptr != ',' && *ptr != '\0')
+                                       ptr++;
+                               if (*ptr != '\0') *ptr++ = '\0';
+                               signal_emit_id(signal_gui_print_text, 6,
+                                              dest->window, NULL, NULL,
+                                              GINT_TO_POINTER(GUI_PRINT_FLAG_INDENT_FUNC),
+                                              str, start, dest->level);
                                break;
                                break;
+                       }
                        case FORMAT_STYLE_DEFAULTS:
                                fgcolor = bgcolor = -1;
                        case FORMAT_STYLE_DEFAULTS:
                                fgcolor = bgcolor = -1;
-                               flags &= PRINTFLAG_INDENT;
+                               flags &= GUI_PRINT_FLAG_INDENT;
                                break;
                                break;
+                       case FORMAT_STYLE_CLRTOEOL:
+                                break;
                        default:
                                if (*ptr != FORMAT_COLOR_NOCHANGE) {
                                        fgcolor = (unsigned char) *ptr-'0';
                                        if (fgcolor <= 7)
                        default:
                                if (*ptr != FORMAT_COLOR_NOCHANGE) {
                                        fgcolor = (unsigned char) *ptr-'0';
                                        if (fgcolor <= 7)
-                                               flags &= ~PRINTFLAG_BOLD;
+                                               flags &= ~GUI_PRINT_FLAG_BOLD;
                                        else {
                                                /* bold */
                                                if (fgcolor != 8) fgcolor -= 8;
                                        else {
                                                /* bold */
                                                if (fgcolor != 8) fgcolor -= 8;
-                                               flags |= PRINTFLAG_BOLD;
+                                               flags |= GUI_PRINT_FLAG_BOLD;
                                        }
                                }
                                ptr++;
                                        }
                                }
                                ptr++;
-                               if (*ptr != FORMAT_COLOR_NOCHANGE)
+                               if (*ptr != FORMAT_COLOR_NOCHANGE) {
                                        bgcolor = *ptr-'0';
                                        bgcolor = *ptr-'0';
+                                       if (bgcolor <= 7)
+                                               flags &= ~GUI_PRINT_FLAG_BLINK;
+                                       else {
+                                               /* blink */
+                                                bgcolor -= 8;
+                                                flags |= GUI_PRINT_FLAG_BLINK;
+                                       }
+                               }
                        }
                        ptr++;
                        break;
                case 6:
                        /* blink */
                        if (!hide_text_style)
                        }
                        ptr++;
                        break;
                case 6:
                        /* blink */
                        if (!hide_text_style)
-                               flags ^= PRINTFLAG_BLINK;
+                               flags ^= GUI_PRINT_FLAG_BLINK;
                        break;
                case 15:
                        /* remove all styling */
                        fgcolor = bgcolor = -1;
                        break;
                case 15:
                        /* remove all styling */
                        fgcolor = bgcolor = -1;
-                       flags &= PRINTFLAG_INDENT;
+                       flags &= GUI_PRINT_FLAG_INDENT;
                        break;
                case 22:
                        /* reverse */
                        if (!hide_text_style)
                        break;
                case 22:
                        /* reverse */
                        if (!hide_text_style)
-                               flags ^= PRINTFLAG_REVERSE;
+                               flags ^= GUI_PRINT_FLAG_REVERSE;
                        break;
                case 31:
                        /* underline */
                        if (!hide_text_style)
                        break;
                case 31:
                        /* underline */
                        if (!hide_text_style)
-                               flags ^= PRINTFLAG_UNDERLINE;
+                               flags ^= GUI_PRINT_FLAG_UNDERLINE;
                        break;
                case 27:
                        /* ansi color code */
                        break;
                case 27:
                        /* ansi color code */
-                       ptr = get_ansi_color(dest->window == NULL || dest->window->theme == NULL ?
-                                            current_theme : dest->window->theme,
-                                            ptr,
-                                            hide_text_style ? NULL : &fgcolor,
-                                            hide_text_style ? NULL : &bgcolor,
-                                            hide_text_style ? NULL : &flags);
+                       ptr = (char *)
+                               get_ansi_color(dest->window == NULL || dest->window->theme == NULL ?
+                                              current_theme : dest->window->theme,
+                                              ptr,
+                                              hide_text_style ? NULL : &fgcolor,
+                                              hide_text_style ? NULL : &bgcolor,
+                                              hide_text_style ? NULL : &flags);
                        break;
                }
 
                        break;
                }
 
@@ -920,11 +1049,16 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
 
 static void read_settings(void)
 {
 
 static void read_settings(void)
 {
+       timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0;
+       if (timestamp_level > 0) {
+               timestamp_level =
+                       level2bits(settings_get_str("timestamp_level"));
+       }
+       timestamp_timeout = settings_get_int("timestamp_timeout");
+
        hide_server_tags = settings_get_bool("hide_server_tags");
        hide_text_style = settings_get_bool("hide_text_style");
        hide_server_tags = settings_get_bool("hide_server_tags");
        hide_text_style = settings_get_bool("hide_text_style");
-       timestamps = settings_get_bool("timestamps");
-       timestamp_timeout = settings_get_int("timestamp_timeout");
-       msgs_timestamps = settings_get_bool("msgs_timestamps");
+       hide_mirc_colors = settings_get_bool("hide_mirc_colors");
 }
 
 void formats_init(void)
 }
 
 void formats_init(void)
index 86c53e69f28921ca167dfe920346cb24fdbf2cf1..b98d7be75787e927fa573c58e74df9ae42e4695f 100644 (file)
@@ -4,13 +4,15 @@
 #include "themes.h"
 #include "fe-windows.h"
 
 #include "themes.h"
 #include "fe-windows.h"
 
-#define PRINTFLAG_BOLD          0x01
-#define PRINTFLAG_REVERSE       0x02
-#define PRINTFLAG_UNDERLINE     0x04
-#define PRINTFLAG_BLINK         0x08
-#define PRINTFLAG_MIRC_COLOR    0x10
-#define PRINTFLAG_INDENT        0x20
-#define PRINTFLAG_NEWLINE       0x40
+#define GUI_PRINT_FLAG_BOLD          0x0001
+#define GUI_PRINT_FLAG_REVERSE       0x0002
+#define GUI_PRINT_FLAG_UNDERLINE     0x0004
+#define GUI_PRINT_FLAG_BLINK         0x0008
+#define GUI_PRINT_FLAG_MIRC_COLOR    0x0010
+#define GUI_PRINT_FLAG_INDENT        0x0020
+#define GUI_PRINT_FLAG_INDENT_FUNC   0x0040
+#define GUI_PRINT_FLAG_NEWLINE       0x0080
+#define GUI_PRINT_FLAG_CLRTOEOL      0x0100
 
 #define MAX_FORMAT_PARAMS 10
 #define DEFAULT_FORMAT_ARGLIST_SIZE 200
 
 #define MAX_FORMAT_PARAMS 10
 #define DEFAULT_FORMAT_ARGLIST_SIZE 200
@@ -30,16 +32,32 @@ struct _FORMAT_REC {
        int paramtypes[MAX_FORMAT_PARAMS];
 };
 
        int paramtypes[MAX_FORMAT_PARAMS];
 };
 
+#define PRINT_FLAG_SET_LINE_START      0x0001
+#define PRINT_FLAG_SET_LINE_START_IRSSI        0x0002
+#define PRINT_FLAG_UNSET_LINE_START    0x0003
+
+#define PRINT_FLAG_SET_TIMESTAMP       0x0004
+#define PRINT_FLAG_UNSET_TIMESTAMP     0x0008
+
+#define PRINT_FLAG_SET_SERVERTAG       0x0010
+#define PRINT_FLAG_UNSET_SERVERTAG     0x0020
+
 typedef struct {
        WINDOW_REC *window;
        SERVER_REC *server;
 typedef struct {
        WINDOW_REC *window;
        SERVER_REC *server;
+        const char *server_tag; /* if server is non-NULL, must be server->tag */
        const char *target;
        int level;
 
        int hilight_priority;
        char *hilight_color;
        const char *target;
        int level;
 
        int hilight_priority;
        char *hilight_color;
+        int flags;
 } TEXT_DEST_REC;
 
 } TEXT_DEST_REC;
 
+#define window_get_theme(window) \
+       (window != NULL && (window)->theme != NULL ? \
+       (window)->theme : current_theme)
+
 int format_find_tag(const char *module, const char *tag);
 
 /* Return length of text part in string (ie. without % codes) */
 int format_find_tag(const char *module, const char *tag);
 
 /* Return length of text part in string (ie. without % codes) */
@@ -49,7 +67,7 @@ int format_get_length(const char *str);
    handles %codes. */
 int format_real_length(const char *str, int len);
 
    handles %codes. */
 int format_real_length(const char *str, int len);
 
-char *format_string_expand(const char *text);
+char *format_string_expand(const char *text, int *flags);
 
 char *format_get_text(const char *module, WINDOW_REC *window,
                      void *server, const char *target,
 
 char *format_get_text(const char *module, WINDOW_REC *window,
                      void *server, const char *target,
@@ -83,6 +101,9 @@ char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t);
 void format_create_dest(TEXT_DEST_REC *dest,
                        void *server, const char *target,
                        int level, WINDOW_REC *window);
 void format_create_dest(TEXT_DEST_REC *dest,
                        void *server, const char *target,
                        int level, WINDOW_REC *window);
+void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
+                           const char *server_tag, const char *target,
+                           int level, WINDOW_REC *window);
 
 void format_newline(WINDOW_REC *window);
 
 
 void format_newline(WINDOW_REC *window);
 
@@ -106,8 +127,10 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text);
 #define FORMAT_STYLE_BOLD      (0x03 + FORMAT_STYLE_SPECIAL)
 #define FORMAT_STYLE_REVERSE   (0x04 + FORMAT_STYLE_SPECIAL)
 #define FORMAT_STYLE_INDENT    (0x05 + FORMAT_STYLE_SPECIAL)
 #define FORMAT_STYLE_BOLD      (0x03 + FORMAT_STYLE_SPECIAL)
 #define FORMAT_STYLE_REVERSE   (0x04 + FORMAT_STYLE_SPECIAL)
 #define FORMAT_STYLE_INDENT    (0x05 + FORMAT_STYLE_SPECIAL)
-#define FORMAT_STYLE_DEFAULTS  (0x06 + FORMAT_STYLE_SPECIAL)
-int format_expand_styles(GString *out, char format);
+#define FORMAT_STYLE_INDENT_FUNC (0x06 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_DEFAULTS  (0x07 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_CLRTOEOL  (0x08 + FORMAT_STYLE_SPECIAL)
+int format_expand_styles(GString *out, const char **format, int *flags);
 
 void formats_init(void);
 void formats_deinit(void);
 
 void formats_init(void);
 void formats_deinit(void);
index 197957f6cf8b3f89acebc22aa8442c8555a5c79a..0bdda55ff2a488c7ee27a1918c1dd0ccf367f3b1 100644 (file)
@@ -37,8 +37,6 @@
 #include "formats.h"
 
 static NICKMATCH_REC *nickmatch;
 #include "formats.h"
 
 static NICKMATCH_REC *nickmatch;
-static HILIGHT_REC *next_nick_hilight, *next_line_hilight;
-static int next_hilight_start, next_hilight_end;
 static int never_hilight_level, default_hilight_level;
 GSList *hilights;
 
 static int never_hilight_level, default_hilight_level;
 GSList *hilights;
 
@@ -264,7 +262,7 @@ static char *hilight_get_color(HILIGHT_REC *rec)
        color = rec->color != NULL ? rec->color :
                settings_get_str("hilight_color");
 
        color = rec->color != NULL ? rec->color :
                settings_get_str("hilight_color");
 
-       return format_string_expand(color);
+       return format_string_expand(color, NULL);
 }
 
 static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
 }
 
 static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
@@ -274,64 +272,51 @@ static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
        if (rec->priority > 0)
                dest->hilight_priority = rec->priority;
 
        if (rec->priority > 0)
                dest->hilight_priority = rec->priority;
 
+        g_free_not_null(dest->hilight_color);
         dest->hilight_color = hilight_get_act_color(rec);
 }
 
         dest->hilight_color = hilight_get_act_color(rec);
 }
 
-static void sig_print_text_stripped(TEXT_DEST_REC *dest, const char *str)
+static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
+                          const char *stripped)
 {
 {
-        HILIGHT_REC *hilight;
-
-       g_return_if_fail(str != NULL);
-
-       if (next_nick_hilight != NULL) {
-               if (!next_nick_hilight->nick) {
-                        /* non-nick hilight wanted */
-                       hilight = next_nick_hilight;
-                       next_nick_hilight = NULL;
-                       if (!hilight_match_text(hilight, str,
-                                               &next_hilight_start,
-                                               &next_hilight_end)) {
-                                next_hilight_start = 0;
-                                next_hilight_end = strlen(str);
-                       }
-               } else {
-                       /* nick is highlighted, just set priority */
-                       hilight_update_text_dest(dest, next_nick_hilight);
-                       next_nick_hilight = NULL;
-                       return;
-               }
-       } else {
-               if (dest->level & (MSGLEVEL_NOHILIGHT|MSGLEVEL_HILIGHT))
-                       return;
+       HILIGHT_REC *hilight;
+       char *color, *newstr;
+       int old_level, hilight_start, hilight_end, hilight_len;
+       int nick_match;
 
 
-               hilight = hilight_match(dest->server, dest->target,
-                                       NULL, NULL, dest->level, str,
-                                       &next_hilight_start,
-                                       &next_hilight_end);
-       }
+       if (dest->level & MSGLEVEL_NOHILIGHT)
+               return;
+
+        hilight_start = hilight_end = 0;
+       hilight = hilight_match(dest->server, dest->target,
+                               NULL, NULL, dest->level, stripped,
+                               &hilight_start,
+                               &hilight_end);
+       if (hilight == NULL)
+               return;
 
 
-       if (hilight != NULL) {
+       nick_match = hilight->nick && (dest->level & (MSGLEVEL_PUBLIC|MSGLEVEL_ACTIONS)) == MSGLEVEL_PUBLIC;
+
+       old_level = dest->level;
+       if (!nick_match || (dest->level & MSGLEVEL_HILIGHT)) {
                /* update the level / hilight info */
                hilight_update_text_dest(dest, hilight);
                /* update the level / hilight info */
                hilight_update_text_dest(dest, hilight);
-
-               next_line_hilight = hilight;
        }
        }
-}
 
 
-static void sig_print_text(TEXT_DEST_REC *dest, const char *str)
-{
-       char *color, *newstr;
-        int next_hilight_len;
+       if (nick_match)
+               return; /* fe-messages.c should have taken care of this */
 
 
-       if (next_line_hilight == NULL)
-                return;
+       if (old_level & MSGLEVEL_HILIGHT) {
+               /* nick is highlighted, just set priority */
+               return;
+       }
 
 
-       color = hilight_get_color(next_line_hilight);
-       next_hilight_len = next_hilight_end-next_hilight_start;
+       color = hilight_get_color(hilight);
+       hilight_len = hilight_end-hilight_start;
 
 
-       if (!next_line_hilight->word) {
+       if (!hilight->word) {
                /* hilight whole line */
                /* hilight whole line */
-               char *tmp = strip_codes(str);
+               char *tmp = strip_codes(text);
                newstr = g_strconcat(color, tmp, NULL);
                 g_free(tmp);
        } else {
                newstr = g_strconcat(color, tmp, NULL);
                 g_free(tmp);
        } else {
@@ -343,25 +328,25 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *str)
                 tmp = g_string_new(NULL);
 
                 /* start of the line */
                 tmp = g_string_new(NULL);
 
                 /* start of the line */
-               pos = strip_real_length(str, next_hilight_start, NULL, NULL);
-               g_string_append(tmp, str);
+               pos = strip_real_length(text, hilight_start, NULL, NULL);
+               g_string_append(tmp, text);
                 g_string_truncate(tmp, pos);
 
                /* color */
                 g_string_append(tmp, color);
 
                /* middle of the line, stripped */
                 g_string_truncate(tmp, pos);
 
                /* color */
                 g_string_append(tmp, color);
 
                /* middle of the line, stripped */
-               middle = strip_codes(str+pos);
+               middle = strip_codes(text+pos);
                 pos = tmp->len;
                g_string_append(tmp, middle);
                 pos = tmp->len;
                g_string_append(tmp, middle);
-                g_string_truncate(tmp, pos+next_hilight_len);
+                g_string_truncate(tmp, pos+hilight_len);
                 g_free(middle);
 
                /* end of the line */
                 g_free(middle);
 
                /* end of the line */
-               pos = strip_real_length(str, next_hilight_end,
+               pos = strip_real_length(text, hilight_end,
                                        &color_pos, &color_len);
                if (color_pos > 0)
                                        &color_pos, &color_len);
                if (color_pos > 0)
-                       lastcolor = g_strndup(str+color_pos, color_len);
+                       lastcolor = g_strndup(text+color_pos, color_len);
                 else {
                         /* no colors in line, change back to default */
                        lastcolor = g_malloc0(3);
                 else {
                         /* no colors in line, change back to default */
                        lastcolor = g_malloc0(3);
@@ -369,15 +354,14 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *str)
                         lastcolor[1] = FORMAT_STYLE_DEFAULTS;
                }
                g_string_append(tmp, lastcolor);
                         lastcolor[1] = FORMAT_STYLE_DEFAULTS;
                }
                g_string_append(tmp, lastcolor);
-               g_string_append(tmp, str+pos);
+               g_string_append(tmp, text+pos);
                g_free(lastcolor);
 
                 newstr = tmp->str;
                 g_string_free(tmp, FALSE);
        }
 
                g_free(lastcolor);
 
                 newstr = tmp->str;
                 g_string_free(tmp, FALSE);
        }
 
-       next_line_hilight = NULL;
-       signal_emit("print text", 2, dest, newstr);
+       signal_emit("print text", 3, dest, newstr, stripped);
 
        g_free(color);
        g_free(newstr);
 
        g_free(color);
        g_free(newstr);
@@ -397,7 +381,6 @@ char *hilight_match_nick(SERVER_REC *server, const char *channel,
        color = rec == NULL || !rec->nick ? NULL :
                hilight_get_color(rec);
 
        color = rec == NULL || !rec->nick ? NULL :
                hilight_get_color(rec);
 
-        next_nick_hilight = rec;
        return color;
 }
 
        return color;
 }
 
@@ -416,7 +399,8 @@ static void read_hilight_config(void)
                return;
        }
 
                return;
        }
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
                node = tmp->data;
 
                if (node->type != NODE_TYPE_BLOCK)
@@ -567,16 +551,14 @@ static void cmd_hilight(const char *data)
        rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
 
        if (colorarg != NULL) {
        rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
 
        if (colorarg != NULL) {
+               g_free_and_null(rec->color);
                if (*colorarg != '\0')
                        rec->color = g_strdup(colorarg);
                if (*colorarg != '\0')
                        rec->color = g_strdup(colorarg);
-               else
-                       g_free_and_null(rec->color);
        }
        if (actcolorarg != NULL) {
        }
        if (actcolorarg != NULL) {
+               g_free_and_null(rec->act_color);
                if (*actcolorarg != '\0')
                        rec->act_color = g_strdup(actcolorarg);
                if (*actcolorarg != '\0')
                        rec->act_color = g_strdup(actcolorarg);
-               else
-                       g_free_and_null(rec->act_color);
        }
 
 #ifdef HAVE_REGEX_H
        }
 
 #ifdef HAVE_REGEX_H
@@ -664,15 +646,11 @@ void hilight_text_init(void)
        settings_add_str("lookandfeel", "hilight_act_color", "%M");
        settings_add_str("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
 
        settings_add_str("lookandfeel", "hilight_act_color", "%M");
        settings_add_str("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
 
-       next_nick_hilight = NULL;
-       next_line_hilight = NULL;
-
         read_settings();
 
        nickmatch = nickmatch_init(hilight_nick_cache);
        read_hilight_config();
 
         read_settings();
 
        nickmatch = nickmatch_init(hilight_nick_cache);
        read_hilight_config();
 
-       signal_add_first("print text stripped", (SIGNAL_FUNC) sig_print_text_stripped);
        signal_add_first("print text", (SIGNAL_FUNC) sig_print_text);
         signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
        signal_add_first("print text", (SIGNAL_FUNC) sig_print_text);
         signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
@@ -687,7 +665,6 @@ void hilight_text_deinit(void)
        hilights_destroy_all();
         nickmatch_deinit(nickmatch);
 
        hilights_destroy_all();
         nickmatch_deinit(nickmatch);
 
-       signal_remove("print text stripped", (SIGNAL_FUNC) sig_print_text_stripped);
        signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
         signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
        signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
         signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
index 0b7ac714c0572563ca79cb7f9558c515561b6476..ac68200196baec1863f5a8dd99f239613724a528 100644 (file)
@@ -38,44 +38,15 @@ static GHashTable *keys, *default_keys;
    If the key isn't used, used_keys[key] is zero. */
 static char used_keys[256];
 
    If the key isn't used, used_keys[key] is zero. */
 static char used_keys[256];
 
-/* contains list of all key bindings of which command is "key" -
-   this can be used to check fast if some command queue exists or not.
-   Format is _always_ in key1-key2-key3 format (like ^W-^N,
-   not ^W^N) */
+/* Contains a list of all possible executable key bindings (not "key" keys).
+   Format is _always_ in key1-key2-key3 format and fully extracted, like
+   ^[-[-A, not meta-A */
 static GTree *key_states;
 static GTree *key_states;
-/* List of all key combo names */
-static GSList *key_combos;
 static int key_config_frozen;
 
 struct KEYBOARD_REC {
 static int key_config_frozen;
 
 struct KEYBOARD_REC {
-       /* example:
-          /BIND ^[ key meta
-          /BIND meta-O key meta2
-          /BIND meta-[ key meta2
-
-          /BIND meta2-C key right
-          /BIND ^W-meta-right /echo ^W Meta-right key pressed
-
-          When ^W Meta-Right is pressed, the full char combination
-          is "^W^[^[[C".
-
-          We'll get there with key states:
-            ^W - key_prev_state = NULL, key_state = NULL -> ^W
-            ^[ - key_prev_state = NULL, key_state = ^W -> meta
-            ^[ - key_prev_state = ^W, key_state = meta -> meta
-            [ - key_prev_state = ^W-meta, key_state = meta -> meta2
-            C - key_prev_state = ^W-meta, key_state = meta2 -> right
-            key_prev_state = ^W-meta, key_state = right -> ^W-meta-right
-
-          key_state is moved to key_prev_state if there's nothing else in
-          /BINDs matching for key_state-newkey.
-
-          ^X^Y equals to ^X-^Y, ABC equals to A-B-C unless there's ABC
-          named key. ^ can be used with ^^ and - with -- */
-       char *key_state, *key_prev_state;
-
-        /* GUI specific data sent in "key pressed" signal */
-        void *gui_data;
+       char *key_state; /* the ongoing key combo */
+        void *gui_data; /* GUI specific data sent in "key pressed" signal */
 };
 
 /* Creates a new "keyboard" - this is used only for keeping track of
 };
 
 /* Creates a new "keyboard" - this is used only for keeping track of
@@ -98,7 +69,6 @@ void keyboard_destroy(KEYBOARD_REC *keyboard)
        signal_emit("keyboard destroyed", 1, keyboard);
 
         g_free_not_null(keyboard->key_state);
        signal_emit("keyboard destroyed", 1, keyboard);
 
         g_free_not_null(keyboard->key_state);
-        g_free_not_null(keyboard->key_prev_state);
         g_free(keyboard);
 }
 
         g_free(keyboard);
 }
 
@@ -144,7 +114,8 @@ static CONFIG_NODE *key_config_find(const char *key)
        /* remove old keyboard settings */
        node = iconfig_node_traverse("(keyboard", TRUE);
 
        /* remove old keyboard settings */
        node = iconfig_node_traverse("(keyboard", TRUE);
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                node = tmp->data;
 
                if (strcmp(config_node_get_str(node, "key", ""), key) == 0)
                node = tmp->data;
 
                if (strcmp(config_node_get_str(node, "key", ""), key) == 0)
@@ -180,8 +151,10 @@ static void keyconfig_clear(const char *key)
 
        /* remove old keyboard settings */
        node = key_config_find(key);
 
        /* remove old keyboard settings */
        node = key_config_find(key);
-        if (node != NULL)
-               iconfig_node_clear(node);
+       if (node != NULL) {
+               iconfig_node_remove(iconfig_node_traverse("(keyboard", FALSE),
+                                   node);
+       }
 }
 
 KEYINFO_REC *key_info_find(const char *id)
 }
 
 KEYINFO_REC *key_info_find(const char *id)
@@ -198,69 +171,170 @@ KEYINFO_REC *key_info_find(const char *id)
        return NULL;
 }
 
        return NULL;
 }
 
-static KEY_REC *key_combo_find(const char *key)
+static int expand_key(const char *key, GSList **out);
+
+#define expand_out_char(out, c) \
+       { \
+         GSList *tmp; \
+         for (tmp = out; tmp != NULL; tmp = tmp->next) \
+            g_string_append_c(tmp->data, c); \
+       }
+
+#define expand_out_free(out) \
+       { \
+         GSList *tmp; \
+         for (tmp = out; tmp != NULL; tmp = tmp->next) \
+            g_string_free(tmp->data, TRUE); \
+         g_slist_free(out); out = NULL; \
+       }
+
+static int expand_combo(const char *start, const char *end, GSList **out)
 {
 {
+        KEY_REC *rec;
        KEYINFO_REC *info;
        KEYINFO_REC *info;
-        GSList *tmp;
+        GSList *tmp, *tmp2, *list, *copy, *newout;
+       char *str;
+
+       if (start == end) {
+               /* single key */
+               expand_out_char(*out, *start);
+                return TRUE;
+       }
 
        info = key_info_find("key");
        if (info == NULL)
 
        info = key_info_find("key");
        if (info == NULL)
-               return NULL;
+               return FALSE;
 
 
+        /* get list of all key combos that generate the named combo.. */
+        list = NULL;
+       str = g_strndup(start, (int) (end-start)+1);
        for (tmp = info->keys; tmp != NULL; tmp = tmp->next) {
                KEY_REC *rec = tmp->data;
 
        for (tmp = info->keys; tmp != NULL; tmp = tmp->next) {
                KEY_REC *rec = tmp->data;
 
-               if (strcmp(rec->data, key) == 0)
-                        return rec;
+               if (strcmp(rec->data, str) == 0)
+                        list = g_slist_append(list, rec);
        }
        }
+       g_free(str);
 
 
-        return NULL;
-}
+       if (list == NULL)
+               return FALSE;
 
 
-static void key_states_scan_key(const char *key, KEY_REC *rec, GString *temp)
-{
-       char **keys, **tmp, *p;
+       if (list->next == NULL) {
+                /* only one way to generate the combo, good */
+                rec = list->data;
+               return expand_key(rec->key, out);
+       }
 
 
-       g_string_truncate(temp, 0);
+       /* multiple ways to generate the combo -
+          we'll need to include all of them in output */
+        newout = NULL;
+       for (tmp = list->next; tmp != NULL; tmp = tmp->next) {
+               KEY_REC *rec = tmp->data;
 
 
-       /* meta-^W^Gfoo -> meta-^W-^G-f-o-o */
-       keys = g_strsplit(key, "-", -1);
-       for (tmp = keys; *tmp != NULL; tmp++) {
-               if (key_combo_find(*tmp)) {
-                        /* key combo */
-                       g_string_append(temp, *tmp);
-                        g_string_append_c(temp, '-');
-                        continue;
+               copy = NULL;
+               for (tmp2 = *out; tmp2 != NULL; tmp2 = tmp2->next) {
+                       GString *str = tmp2->data;
+                        copy = g_slist_append(copy, g_string_new(str->str));
                }
 
                }
 
-               if (**tmp == '\0') {
-                        /* '-' */
-                       g_string_append(temp, "--");
-                        continue;
+               if (!expand_key(rec->key, &copy)) {
+                       /* illegal key combo, remove from list */
+                        expand_out_free(copy);
+               } else {
+                        newout = g_slist_concat(newout, copy);
                }
                }
+       }
 
 
-               for (p = *tmp; *p != '\0'; p++) {
-                       g_string_append_c(temp, *p);
+        rec = list->data;
+       if (!expand_key(rec->key, out)) {
+               /* illegal key combo, remove from list */
+               expand_out_free(*out);
+       }
 
 
-                       if (*p == '^') {
-                                /* ctrl-code */
-                               if (p[1] != '\0')
-                                       p++;
-                               g_string_append_c(temp, *p);
+       *out = g_slist_concat(*out, newout);
+        return *out != NULL;
+}
+
+/* Expand key code - returns TRUE if successful. */
+static int expand_key(const char *key, GSList **out)
+{
+       GSList *tmp;
+       const char *start;
+       int last_hyphen;
+
+       /* meta-^W^Gf -> ^[-^W-^G-f */
+        start = NULL; last_hyphen = TRUE;
+       for (; *key != '\0'; key++) {
+               if (start != NULL) {
+                       if (i_isalnum(*key) || *key == '_') {
+                                /* key combo continues */
+                               continue;
                        }
 
                        }
 
-                       g_string_append_c(temp, '-');
+                       if (!expand_combo(start, key-1, out))
+                                return FALSE;
+                       expand_out_char(*out, '-');
+                        start = NULL;
+               }
+
+               if (*key == '-') {
+                       if (last_hyphen) {
+                                expand_out_char(*out, '-');
+                                expand_out_char(*out, '-');
+                       }
+                       last_hyphen = !last_hyphen;
+               } else if (*key == '^') {
+                        /* ctrl-code */
+                       if (key[1] != '\0')
+                               key++;
+
+                       expand_out_char(*out, '^');
+                       expand_out_char(*out, *key);
+                       expand_out_char(*out, '-');
+                        last_hyphen = FALSE; /* optional */
+               } else if (last_hyphen && i_isalnum(*key) && !i_isdigit(*key)) {
+                        /* possibly beginning of keycombo */
+                       start = key;
+                        last_hyphen = FALSE;
+               } else {
+                       expand_out_char(*out, *key);
+                       expand_out_char(*out, '-');
+                        last_hyphen = FALSE; /* optional */
                }
        }
                }
        }
-       g_strfreev(keys);
 
 
-       if (temp->len > 0) {
-               g_string_truncate(temp, temp->len-1);
+       if (start != NULL)
+               return expand_combo(start, key-1, out);
 
 
-               if (temp->str[1] == '-' || temp->str[1] == '\0')
-                        used_keys[(int) (unsigned char) temp->str[0]] = 1;
-               g_tree_insert(key_states, g_strdup(temp->str), rec);
+       for (tmp = *out; tmp != NULL; tmp = tmp->next) {
+               GString *str = tmp->data;
+
+               g_string_truncate(str, str->len-1);
        }
        }
+
+        return TRUE;
+}
+
+static void key_states_scan_key(const char *key, KEY_REC *rec)
+{
+       GSList *tmp, *out;
+
+       if (strcmp(rec->info->id, "key") == 0)
+               return;
+
+        out = g_slist_append(NULL, g_string_new(NULL));
+       if (expand_key(key, &out)) {
+               for (tmp = out; tmp != NULL; tmp = tmp->next) {
+                       GString *str = tmp->data;
+
+                       if (str->str[1] == '-' || str->str[1] == '\0')
+                               used_keys[(int)(unsigned char)str->str[0]] = 1;
+
+                       g_tree_insert(key_states, g_strdup(str->str), rec);
+               }
+       }
+
+       expand_out_free(out);
 }
 
 static int key_state_destroy(char *key)
 }
 
 static int key_state_destroy(char *key)
@@ -455,7 +529,8 @@ static int key_emit_signal(KEYBOARD_REC *keyboard, KEY_REC *key)
         return consumed;
 }
 
         return consumed;
 }
 
-int key_states_search(const char *combo, const char *search)
+static int key_states_search(const unsigned char *combo,
+                            const unsigned char *search)
 {
        while (*search != '\0') {
                if (*combo != *search)
 {
        while (*search != '\0') {
                if (*combo != *search)
@@ -463,7 +538,7 @@ int key_states_search(const char *combo, const char *search)
                 search++; combo++;
        }
 
                 search++; combo++;
        }
 
-       return *combo == '\0' || *combo == '-' ? 0 : -1;
+        return 0;
 }
 
 /* Returns TRUE if key press was consumed. Control characters should be sent
 }
 
 /* Returns TRUE if key press was consumed. Control characters should be sent
@@ -471,96 +546,45 @@ int key_states_search(const char *combo, const char *search)
 int key_pressed(KEYBOARD_REC *keyboard, const char *key)
 {
        KEY_REC *rec;
 int key_pressed(KEYBOARD_REC *keyboard, const char *key)
 {
        KEY_REC *rec;
-       char *str;
-        int consumed;
+        char *combo;
+        int first_key, consumed;
 
        g_return_val_if_fail(keyboard != NULL, FALSE);
        g_return_val_if_fail(key != NULL && *key != '\0', FALSE);
 
 
        g_return_val_if_fail(keyboard != NULL, FALSE);
        g_return_val_if_fail(key != NULL && *key != '\0', FALSE);
 
-       if (keyboard->key_state == NULL) {
-               if (key[1] == '\0' &&
-                   !used_keys[(int) (unsigned char) key[0]]) {
-                        /* fast check - key not used */
-                       return FALSE;
-               }
-
-               rec = g_tree_search(key_states,
-                                   (GSearchFunc) key_states_search,
-                                   (void *) key);
-               if (rec == NULL ||
-                   (g_tree_lookup(key_states, (void *) key) != NULL &&
-                    strcmp(rec->info->id, "key") != 0)) {
-                       /* a single non-combo key was pressed */
-                       rec = g_hash_table_lookup(keys, key);
-                       if (rec == NULL)
-                               return FALSE;
-                       consumed = key_emit_signal(keyboard, rec);
-
-                       /* never consume non-control characters */
-                       return consumed && key[1] != '\0';
-               }
-       }
-
-       if (keyboard->key_state == NULL) {
-                /* first key in combo */
-               rec = g_tree_lookup(key_states, (void *) key);
-       } else {
-               /* continuing key combination */
-               str = g_strconcat(keyboard->key_state, "-", key, NULL);
-               rec = g_tree_lookup(key_states, str);
-               g_free(str);
+       if (keyboard->key_state == NULL && key[1] == '\0' &&
+           !used_keys[(int) (unsigned char) key[0]]) {
+               /* fast check - key not used */
+               return FALSE;
        }
 
        }
 
-       if (rec != NULL && strcmp(rec->info->id, "key") == 0) {
-               /* combo has a specified name, use it */
-               g_free_not_null(keyboard->key_state);
-               keyboard->key_state = g_strdup(rec->data);
-       } else {
-               /* some unnamed key - move key_state after key_prev_state
-                  and replace key_state with this new key */
-               if (keyboard->key_prev_state == NULL)
-                       keyboard->key_prev_state = keyboard->key_state;
-               else {
-                       str = g_strconcat(keyboard->key_prev_state, "-",
-                                         keyboard->key_state, NULL);
-                       g_free(keyboard->key_prev_state);
-                       g_free(keyboard->key_state);
-                       keyboard->key_prev_state = str;
-               }
+        first_key = keyboard->key_state == NULL;
+       combo = keyboard->key_state == NULL ? g_strdup(key) :
+                g_strconcat(keyboard->key_state, "-", key, NULL);
+       g_free_and_null(keyboard->key_state);
 
 
-               keyboard->key_state = g_strdup(key);
+       rec = g_tree_search(key_states,
+                           (GSearchFunc) key_states_search,
+                           combo);
+       if (rec == NULL) {
+               /* unknown key combo, eat the invalid key
+                  unless it was the first key pressed */
+                g_free(combo);
+               return !first_key;
        }
 
        }
 
-        /* what to do with the key combo? */
-       str = keyboard->key_prev_state == NULL ?
-               g_strdup(keyboard->key_state) :
-               g_strconcat(keyboard->key_prev_state, "-",
-                           keyboard->key_state, NULL);
-
-       rec = g_tree_lookup(key_states, str);
-       if (rec != NULL) {
-               if (strcmp(rec->info->id, "key") == 0)
-                       rec = g_tree_lookup(key_states, rec->data);
-
-               if (rec != NULL) {
-                       /* full key combo */
-                       key_emit_signal(keyboard, rec);
-                       rec = NULL;
-               }
-       } else {
-                /* check that combo is possible */
-               rec = g_tree_search(key_states,
-                                   (GSearchFunc) key_states_search, str);
+       if (g_tree_lookup(key_states, combo) != rec) {
+               /* key combo continues.. */
+               keyboard->key_state = combo;
+                return TRUE;
        }
 
        }
 
-       if (rec == NULL) {
-               /* a) key combo finished, b) unknown key combo, abort */
-               g_free_and_null(keyboard->key_prev_state);
-               g_free_and_null(keyboard->key_state);
-       }
+        /* finished key combo, execute */
+        g_free(combo);
+       consumed = key_emit_signal(keyboard, rec);
 
 
-       g_free(str);
-        return TRUE;
+       /* never consume non-control characters */
+       return consumed;
 }
 
 void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry,
 }
 
 void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry,
@@ -771,7 +795,8 @@ static void read_keyboard_config(void)
                return;
        }
 
                return;
        }
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp))
                key_config_read(tmp->data);
 
         key_configure_thaw();
                key_config_read(tmp->data);
 
         key_configure_thaw();
@@ -785,7 +810,6 @@ void keyboard_init(void)
                                        (GCompareFunc) g_str_equal);
        keyinfos = NULL;
        key_states = g_tree_new((GCompareFunc) strcmp);
                                        (GCompareFunc) g_str_equal);
        keyinfos = NULL;
        key_states = g_tree_new((GCompareFunc) strcmp);
-       key_combos = NULL;
         key_config_frozen = 0;
        memset(used_keys, 0, sizeof(used_keys));
 
         key_config_frozen = 0;
        memset(used_keys, 0, sizeof(used_keys));
 
index 0f37ccfd649173b1e3cd28647f4a3a7312711290..2a5c8f93e63697070a71cc500a29050e9816948a 100644 (file)
@@ -28,21 +28,39 @@ FORMAT_REC fecommon_core_formats[] = {
        { NULL, "Windows", 0 },
 
        { "line_start", "{line_start}", 0 },
        { NULL, "Windows", 0 },
 
        { "line_start", "{line_start}", 0 },
-       { "line_start_irssi", "{line_start}{hilight Irssi:} ", 0 }, 
-        { "timestamp", "{timestamp $Z} ", 6, { 1, 1, 1, 1, 1, 1 } },
+       { "line_start_irssi", "{line_start}{hilight Irssi:} ", 0 },
+        { "timestamp", "{timestamp $Z} ", 0 },
        { "servertag", "[$0] ", 1, { 0 } },
        { "servertag", "[$0] ", 1, { 0 } },
-       { "daychange", "Day changed to $[-2.0]{0} $3 $2", 4, { 1, 1, 1, 0 } },
+       { "daychange", "Day changed to %%d %%b %%Y", 0 },
        { "talking_with", "You are now talking with {nick $0}", 1, { 0 } },
        { "refnum_too_low", "Window number must be greater than 1", 0 },
        { "error_server_sticky", "Window's server is sticky and it cannot be changed without -unsticky option", 0 },
        { "set_server_sticky", "Window's server set sticky", 1, { 0 } },
        { "talking_with", "You are now talking with {nick $0}", 1, { 0 } },
        { "refnum_too_low", "Window number must be greater than 1", 0 },
        { "error_server_sticky", "Window's server is sticky and it cannot be changed without -unsticky option", 0 },
        { "set_server_sticky", "Window's server set sticky", 1, { 0 } },
-       { "unset_server_sticky", "Window's server isn't sticky anymore", 1, { 0 } },
+       { "unset_server_sticky", "Window's server isn't sticky anymore", 0 },
+       { "window_name_not_unique", "Window names must be unique", 1, { 0 } },
        { "window_level", "Window level is now $0", 1, { 0 } },
        { "windowlist_header", "Ref Name                 Active item     Server          Level", 0 },
        { "windowlist_line", "$[3]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } },
        { "windowlist_footer", "", 0 },
        { "window_level", "Window level is now $0", 1, { 0 } },
        { "windowlist_header", "Ref Name                 Active item     Server          Level", 0 },
        { "windowlist_line", "$[3]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } },
        { "windowlist_footer", "", 0 },
-       { "windows_layout_saved", "Layout of windows is now remembered next time you start silc", 0 },
+       { "windows_layout_saved", "Layout of windows is now remembered next time you start irssi", 0 },
        { "windows_layout_reset", "Layout of windows reset to defaults", 0 },
        { "windows_layout_reset", "Layout of windows reset to defaults", 0 },
+       { "window_info_header", "", 0 },
+       { "window_info_footer", "", 0 },
+       { "window_info_refnum", "Window  : {hilight #$0}", 1, { 1 } },
+       { "window_info_refnum_sticky", "Window  : {hilight #$0 (sticky)}", 1, { 1 } },
+       { "window_info_name", "Name    : $0", 1, { 0 } },
+       { "window_info_history", "History : $0", 1, { 0 } },
+       { "window_info_size", "Size    : $0x$1", 2, { 1, 1 } },
+       { "window_info_level", "Level   : $0", 1, { 0 } },
+       { "window_info_server", "Server  : $0", 1, { 0 } },
+       { "window_info_server_sticky", "Server  : $0 (sticky)", 1, { 0 } },
+       { "window_info_theme", "Theme   : $0$1", 2, { 0, 0 } },
+       { "window_info_bound_items_header", "Bounds  : {hilight Name                           Server tag}", 0 },
+       { "window_info_bound_item", "        : $[!30]0 $[!15]1 $2", 3, { 0, 0, 0 } },
+       { "window_info_bound_items_footer", "", 0 },
+       { "window_info_items_header", "Items   : {hilight Name                           Server tag}", 0 },
+       { "window_info_item", " $[7]0: $[!30]1 $2", 3, { 0, 0, 0 } },
+       { "window_info_items_footer", "", 0 },
 
        /* ---- */
        { NULL, "Server", 0 },
 
        /* ---- */
        { NULL, "Server", 0 },
@@ -66,6 +84,7 @@ FORMAT_REC fecommon_core_formats[] = {
        { "setupserver_added", "Server {server $0} saved", 2, { 0, 1 } },
        { "setupserver_removed", "Server {server $0} removed", 2, { 0, 1 } },
        { "setupserver_not_found", "Server {server $0} not found", 2, { 0, 1 } },
        { "setupserver_added", "Server {server $0} saved", 2, { 0, 1 } },
        { "setupserver_removed", "Server {server $0} removed", 2, { 0, 1 } },
        { "setupserver_not_found", "Server {server $0} not found", 2, { 0, 1 } },
+       { "your_nick", "Your nickname is {nick $0}", 1, { 0 } },
 
        /* ---- */
        { NULL, "Channels", 0 },
 
        /* ---- */
        { NULL, "Channels", 0 },
@@ -76,6 +95,7 @@ FORMAT_REC fecommon_core_formats[] = {
        { "quit", "{channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } },
        { "quit_once", "{channel $3} {channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } },
        { "invite", "{nick $0} invites you to {channel $1}", 2, { 0, 0 } },
        { "quit", "{channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } },
        { "quit_once", "{channel $3} {channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } },
        { "invite", "{nick $0} invites you to {channel $1}", 2, { 0, 0 } },
+       { "not_invited", "You have not been invited to a channel!", 0 },
        { "new_topic", "{nick $0} changed the topic of {channel $1} to: $2", 3, { 0, 0, 0 } },
        { "topic_unset", "Topic unset by {nick $0} on {channel $1}", 2, { 0, 0 } },
        { "your_nick_changed", "You're now known as {nick $1}", 3, { 0, 0, 0 } },
        { "new_topic", "{nick $0} changed the topic of {channel $1} to: $2", 3, { 0, 0, 0 } },
        { "topic_unset", "Topic unset by {nick $0} on {channel $1}", 2, { 0, 0 } },
        { "your_nick_changed", "You're now known as {nick $1}", 3, { 0, 0, 0 } },
@@ -84,14 +104,18 @@ FORMAT_REC fecommon_core_formats[] = {
        { "not_in_channels", "You are not on any channels", 0 },
        { "current_channel", "Current channel {channel $0}", 1, { 0 } },
        { "names", "{names_users Users {names_channel $0}} $1", 2, { 0, 0 } },
        { "not_in_channels", "You are not on any channels", 0 },
        { "current_channel", "Current channel {channel $0}", 1, { 0 } },
        { "names", "{names_users Users {names_channel $0}} $1", 2, { 0, 0 } },
+       { "names_prefix", "{names_prefix $0}", 1, { 0 } },
+        { "names_nick_op", "{names_nick_op $0 $1}", 2, { 0, 0 } },
+        { "names_nick_halfop", "{names_nick_halfop $0 $1}", 2, { 0, 0 } },
+        { "names_nick_voice", "{names_nick_voice $0 $1}", 2, { 0, 0 } },
         { "names_nick", "{names_nick $0 $1}", 2, { 0, 0 } },
         { "names_nick", "{names_nick $0 $1}", 2, { 0, 0 } },
-       { "endofnames", "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} voices, {hilight $4} normal}", 5, { 0, 1, 1, 1, 1 } },
+        { "endofnames", "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal}", 6, { 0, 1, 1, 1, 1, 1 } },
        { "chanlist_header", "You are on the following channels:", 0 },
        { "chanlist_line", "{channel $[-10]0} %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } },
        { "chansetup_not_found", "Channel {channel $0} not found", 2, { 0, 0 } },
        { "chansetup_added", "Channel {channel $0} saved", 2, { 0, 0 } },
        { "chansetup_removed", "Channel {channel $0} removed", 2, { 0, 0 } },
        { "chanlist_header", "You are on the following channels:", 0 },
        { "chanlist_line", "{channel $[-10]0} %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } },
        { "chansetup_not_found", "Channel {channel $0} not found", 2, { 0, 0 } },
        { "chansetup_added", "Channel {channel $0} saved", 2, { 0, 0 } },
        { "chansetup_removed", "Channel {channel $0} removed", 2, { 0, 0 } },
-       { "chansetup_header", "Channel         IRC net    Password   Settings", 0 },
+       { "chansetup_header", "Channel         Network    Password   Settings", 0 },
        { "chansetup_line", "{channel $[15]0} %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } },
        { "chansetup_footer", "", 0 },
        { "channel_move_notify", "{channel $0} is already joined in window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } },
        { "chansetup_line", "{channel $[15]0} %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } },
        { "chansetup_footer", "", 0 },
        { "channel_move_notify", "{channel $0} is already joined in window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } },
@@ -117,7 +141,8 @@ FORMAT_REC fecommon_core_formats[] = {
        /* ---- */
        { NULL, "Queries", 0 },
 
        /* ---- */
        { NULL, "Queries", 0 },
 
-       { "query_start", "Starting query with {nick $0}", 1, { 0 } },
+       { "query_start", "Starting query in {server $1} with {nick $0}", 2, { 0, 0 } },
+       { "query_stop", "Closing query with {nick $0}", 1, { 0 } },
        { "no_query", "No query with {nick $0}", 1, { 0 } },
        { "query_server_changed", "Query with {nick $0} changed to server {server $1}", 2, { 0, 0 } },
        { "query_move_notify", "Query with {nick $0} is already created to window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } },
        { "no_query", "No query with {nick $0}", 1, { 0 } },
        { "query_server_changed", "Query with {nick $0} changed to server {server $1}", 2, { 0, 0 } },
        { "query_move_notify", "Query with {nick $0} is already created to window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } },
@@ -147,7 +172,7 @@ FORMAT_REC fecommon_core_formats[] = {
        { "log_opened", "Log file {hilight $0} opened", 1, { 0 } },
        { "log_closed", "Log file {hilight $0} closed", 1, { 0 } },
        { "log_create_failed", "Couldn't create log file {hilight $0}: $1", 2, { 0, 0 } },
        { "log_opened", "Log file {hilight $0} opened", 1, { 0 } },
        { "log_closed", "Log file {hilight $0} closed", 1, { 0 } },
        { "log_create_failed", "Couldn't create log file {hilight $0}: $1", 2, { 0, 0 } },
-       { "log_locked", "Log file {hilight $0} is locked, probably by another running Irssi-SILC", 1, { 0 } },
+       { "log_locked", "Log file {hilight $0} is locked, probably by another running Irssi", 1, { 0 } },
        { "log_not_open", "Log file {hilight $0} not open", 1, { 0 } },
        { "log_started", "Started logging to file {hilight $0}", 1, { 0 } },
        { "log_stopped", "Stopped logging to file {hilight $0}", 1, { 0 } },
        { "log_not_open", "Log file {hilight $0} not open", 1, { 0 } },
        { "log_started", "Started logging to file {hilight $0}", 1, { 0 } },
        { "log_stopped", "Stopped logging to file {hilight $0}", 1, { 0 } },
@@ -162,14 +187,15 @@ FORMAT_REC fecommon_core_formats[] = {
        /* ---- */
        { NULL, "Modules", 0 },
 
        /* ---- */
        { NULL, "Modules", 0 },
 
-       { "module_header", "Loaded modules:", 0, },
-       { "module_line", "  $0", 1, { 0 } },
+       { "module_header", "Module               Type    Submodules", 0, },
+       { "module_line", "$[!20]0 $[7]1 $2", 3, { 0, 0, 0 } },
        { "module_footer", "", 0, },
        { "module_footer", "", 0, },
-       { "module_already_loaded", "Module {hilight $0} already loaded", 1, { 0 } },
-       { "module_load_error", "Error loading module {hilight $0}: $1", 2, { 0, 0 } },
-       { "module_invalid", "{hilight $0} isn't Irssi-SILC module", 1, { 0 } },
-       { "module_loaded", "Loaded module {hilight $0}", 1, { 0 } },
-       { "module_unloaded", "Unloaded module {hilight $0}", 1, { 0 } },
+       { "module_already_loaded", "Module {hilight $0/$1} already loaded", 2, { 0, 0 } },
+       { "module_not_loaded", "Module {hilight $0/$1} is not loaded", 2, { 0, 0 } },
+       { "module_load_error", "Error loading module {hilight $0/$1}: $2", 3, { 0, 0, 0 } },
+       { "module_invalid", "{hilight $0/$1} isn't Irssi module", 2, { 0, 0 } },
+       { "module_loaded", "Loaded module {hilight $0/$1}", 2, { 0, 0 } },
+       { "module_unloaded", "Unloaded module {hilight $0/$1}", 2, { 0, 0 } },
 
        /* ---- */
        { NULL, "Commands", 0 },
 
        /* ---- */
        { NULL, "Commands", 0 },
@@ -184,6 +210,7 @@ FORMAT_REC fecommon_core_formats[] = {
        { "not_joined", "Not joined to any channel", 0 },
        { "chan_not_found", "Not joined to such channel", 0 },
        { "chan_not_synced", "Channel not fully synchronized yet, try again after a while", 0 },
        { "not_joined", "Not joined to any channel", 0 },
        { "chan_not_found", "Not joined to such channel", 0 },
        { "chan_not_synced", "Channel not fully synchronized yet, try again after a while", 0 },
+       { "illegal_proto", "Command isn't designed for the chat protocol of the active server", 0 },
        { "not_good_idea", "Doing this is not a good idea. Add -YES if you really mean it", 0 },
 
        /* ---- */
        { "not_good_idea", "Doing this is not a good idea. Add -YES if you really mean it", 0 },
 
        /* ---- */
@@ -193,7 +220,10 @@ FORMAT_REC fecommon_core_formats[] = {
        { "theme_save_failed", "Error saving theme to $0: $1", 2, { 0, 0 } },
        { "theme_not_found", "Theme {hilight $0} not found", 1, { 0 } },
        { "theme_changed", "Using now theme {hilight $0} ($1)", 2, { 0, 0 } },
        { "theme_save_failed", "Error saving theme to $0: $1", 2, { 0, 0 } },
        { "theme_not_found", "Theme {hilight $0} not found", 1, { 0 } },
        { "theme_changed", "Using now theme {hilight $0} ($1)", 2, { 0, 0 } },
-       { "window_theme_changed", "Using theme {hilight $0} ($1) in this window", 2, { 0, 0 } },
+       { "window_theme", "Using theme {hilight $0} in this window", 2, { 0, 0 } },
+       { "window_theme_default", "No theme is set for this window", 0 },
+       { "window_theme_changed", "Using now theme {hilight $0} ($1) in this window", 2, { 0, 0 } },
+       { "window_theme_removed", "Removed theme from this window", 0 },
        { "format_title", "%:[{hilight $0}] - [{hilight $1}]%:", 2, { 0, 0 } },
        { "format_subtitle", "[{hilight $0}]", 1, { 0 } },
        { "format_item", "$0 = $1", 2, { 0, 0 } },
        { "format_title", "%:[{hilight $0}] - [{hilight $1}]%:", 2, { 0, 0 } },
        { "format_subtitle", "[{hilight $0}]", 1, { 0 } },
        { "format_item", "$0 = $1", 2, { 0, 0 } },
@@ -213,16 +243,22 @@ FORMAT_REC fecommon_core_formats[] = {
        { NULL, "Misc", 0 },
 
        { "unknown_chat_protocol", "Unknown chat protocol: $0", 1, { 0 } },
        { NULL, "Misc", 0 },
 
        { "unknown_chat_protocol", "Unknown chat protocol: $0", 1, { 0 } },
-       { "unknown_chatnet", "Unknown chat network: $0", 1, { 0 } },
+       { "unknown_chatnet", "Unknown chat network: $0 (create it with /IRCNET ADD)", 1, { 0 } },
        { "not_toggle", "Value must be either ON, OFF or TOGGLE", 0 },
        { "perl_error", "Perl error: $0", 1, { 0 } },
        { "not_toggle", "Value must be either ON, OFF or TOGGLE", 0 },
        { "perl_error", "Perl error: $0", 1, { 0 } },
-       { "bind_key", "$[10]0 $1 $2", 3, { 0, 0, 0 } },
+       { "bind_key", "$[!20]0 $1 $2", 3, { 0, 0, 0 } },
        { "bind_unknown_id", "Unknown bind action: $0", 1, { 0 } },
        { "config_saved", "Saved configuration to file $0", 1, { 0 } },
        { "config_reloaded", "Reloaded configuration", 1, { 0 } },
        { "bind_unknown_id", "Unknown bind action: $0", 1, { 0 } },
        { "config_saved", "Saved configuration to file $0", 1, { 0 } },
        { "config_reloaded", "Reloaded configuration", 1, { 0 } },
-       { "config_modified", "Configuration file was modified since Irssi-SILC was last started - do you want to overwrite the possible changes?", 1, { 0 } },
+       { "config_modified", "Configuration file was modified since irssi was last started - do you want to overwrite the possible changes?", 1, { 0 } },
        { "glib_error", "{error GLib $0} $1", 2, { 0, 0 } },
        { "overwrite_config", "Overwrite config (y/N)?", 0 },
        { "glib_error", "{error GLib $0} $1", 2, { 0, 0 } },
        { "overwrite_config", "Overwrite config (y/N)?", 0 },
+       { "set_title", "[{hilight $0}]", 1, { 0 } },
+       { "set_item", "$0 = $1", 2, { 0, 0 } },
+       { "set_unknown", "Unknown setting $0", 1, { 0 } },
+       { "set_not_boolean", "Setting {hilight $0} isn't boolean, use /SET", 1, { 0 } },
+       { "translation_not_found", "Error opening translation table file $0: $1", 2, { 0, 0 } },
+       { "translation_file_error", "Error parsing translation table file $0", 1, { 0 } },
 
        { NULL, NULL, 0 }
 };
 
        { NULL, NULL, 0 }
 };
index e0f31f9aff5348de77c3b4204a5155f7658a7e2b..fcfd87d5c9ca3a4812373ebe175b2ca63b17c322 100644 (file)
@@ -14,13 +14,31 @@ enum {
        TXT_REFNUM_TOO_LOW,
         TXT_ERROR_SERVER_STICKY,
         TXT_SET_SERVER_STICKY,
        TXT_REFNUM_TOO_LOW,
         TXT_ERROR_SERVER_STICKY,
         TXT_SET_SERVER_STICKY,
-        TXT_UNSET_SERVER_STICKY,
+       TXT_UNSET_SERVER_STICKY,
+        TXT_WINDOW_NAME_NOT_UNIQUE,
         TXT_WINDOW_LEVEL,
        TXT_WINDOWLIST_HEADER,
        TXT_WINDOWLIST_LINE,
        TXT_WINDOWLIST_FOOTER,
        TXT_WINDOWS_LAYOUT_SAVED,
        TXT_WINDOWS_LAYOUT_RESET,
         TXT_WINDOW_LEVEL,
        TXT_WINDOWLIST_HEADER,
        TXT_WINDOWLIST_LINE,
        TXT_WINDOWLIST_FOOTER,
        TXT_WINDOWS_LAYOUT_SAVED,
        TXT_WINDOWS_LAYOUT_RESET,
+        TXT_WINDOW_INFO_HEADER,
+        TXT_WINDOW_INFO_FOOTER,
+       TXT_WINDOW_INFO_REFNUM,
+       TXT_WINDOW_INFO_REFNUM_STICKY,
+       TXT_WINDOW_INFO_NAME,
+       TXT_WINDOW_INFO_HISTORY,
+       TXT_WINDOW_INFO_SIZE,
+       TXT_WINDOW_INFO_LEVEL,
+        TXT_WINDOW_INFO_SERVER,
+       TXT_WINDOW_INFO_SERVER_STICKY,
+        TXT_WINDOW_INFO_THEME,
+       TXT_WINDOW_INFO_BOUND_ITEMS_HEADER,
+       TXT_WINDOW_INFO_BOUND_ITEM,
+       TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER,
+       TXT_WINDOW_INFO_ITEMS_HEADER,
+       TXT_WINDOW_INFO_ITEM,
+        TXT_WINDOW_INFO_ITEMS_FOOTER,
 
        TXT_FILL_2,
 
 
        TXT_FILL_2,
 
@@ -43,6 +61,7 @@ enum {
        TXT_SETUPSERVER_ADDED,
        TXT_SETUPSERVER_REMOVED,
        TXT_SETUPSERVER_NOT_FOUND,
        TXT_SETUPSERVER_ADDED,
        TXT_SETUPSERVER_REMOVED,
        TXT_SETUPSERVER_NOT_FOUND,
+       TXT_YOUR_NICK,
 
        TXT_FILL_3,
 
 
        TXT_FILL_3,
 
@@ -52,6 +71,7 @@ enum {
        TXT_QUIT,
        TXT_QUIT_ONCE,
        TXT_INVITE,
        TXT_QUIT,
        TXT_QUIT_ONCE,
        TXT_INVITE,
+       TXT_NOT_INVITED,
        TXT_NEW_TOPIC,
        TXT_TOPIC_UNSET,
        TXT_YOUR_NICK_CHANGED,
        TXT_NEW_TOPIC,
        TXT_TOPIC_UNSET,
        TXT_YOUR_NICK_CHANGED,
@@ -60,6 +80,10 @@ enum {
        TXT_NOT_IN_CHANNELS,
        TXT_CURRENT_CHANNEL,
        TXT_NAMES,
        TXT_NOT_IN_CHANNELS,
        TXT_CURRENT_CHANNEL,
        TXT_NAMES,
+       TXT_NAMES_PREFIX,
+       TXT_NAMES_NICK_OP,
+       TXT_NAMES_NICK_HALFOP,
+       TXT_NAMES_NICK_VOICE,
        TXT_NAMES_NICK,
        TXT_ENDOFNAMES,
        TXT_CHANLIST_HEADER,
        TXT_NAMES_NICK,
        TXT_ENDOFNAMES,
        TXT_CHANLIST_HEADER,
@@ -91,7 +115,8 @@ enum {
 
        TXT_FILL_5,
 
 
        TXT_FILL_5,
 
-       TXT_QUERY_STARTED,
+       TXT_QUERY_START,
+       TXT_QUERY_STOP,
        TXT_NO_QUERY,
        TXT_QUERY_SERVER_CHANGED,
         TXT_QUERY_MOVE_NOTIFY,
        TXT_NO_QUERY,
        TXT_QUERY_SERVER_CHANGED,
         TXT_QUERY_MOVE_NOTIFY,
@@ -136,6 +161,7 @@ enum {
        TXT_MODULE_LINE,
         TXT_MODULE_FOOTER,
        TXT_MODULE_ALREADY_LOADED,
        TXT_MODULE_LINE,
         TXT_MODULE_FOOTER,
        TXT_MODULE_ALREADY_LOADED,
+       TXT_MODULE_NOT_LOADED,
        TXT_MODULE_LOAD_ERROR,
        TXT_MODULE_INVALID,
        TXT_MODULE_LOADED,
        TXT_MODULE_LOAD_ERROR,
        TXT_MODULE_INVALID,
        TXT_MODULE_LOADED,
@@ -153,6 +179,7 @@ enum {
        TXT_NOT_JOINED,
        TXT_CHAN_NOT_FOUND,
        TXT_CHAN_NOT_SYNCED,
        TXT_NOT_JOINED,
        TXT_CHAN_NOT_FOUND,
        TXT_CHAN_NOT_SYNCED,
+        TXT_ILLEGAL_PROTO,
        TXT_NOT_GOOD_IDEA,
 
        TXT_FILL_11,
        TXT_NOT_GOOD_IDEA,
 
        TXT_FILL_11,
@@ -161,7 +188,10 @@ enum {
        TXT_THEME_SAVE_FAILED,
        TXT_THEME_NOT_FOUND,
        TXT_THEME_CHANGED,
        TXT_THEME_SAVE_FAILED,
        TXT_THEME_NOT_FOUND,
        TXT_THEME_CHANGED,
+       TXT_WINDOW_THEME,
+       TXT_WINDOW_THEME_DEFAULT,
        TXT_WINDOW_THEME_CHANGED,
        TXT_WINDOW_THEME_CHANGED,
+       TXT_WINDOW_THEME_REMOVED,
        TXT_FORMAT_TITLE,
        TXT_FORMAT_SUBTITLE,
        TXT_FORMAT_ITEM,
        TXT_FORMAT_TITLE,
        TXT_FORMAT_SUBTITLE,
        TXT_FORMAT_ITEM,
@@ -188,7 +218,13 @@ enum {
        TXT_CONFIG_RELOADED,
        TXT_CONFIG_MODIFIED,
        TXT_GLIB_ERROR,
        TXT_CONFIG_RELOADED,
        TXT_CONFIG_MODIFIED,
        TXT_GLIB_ERROR,
-        TXT_OVERWRITE_CONFIG
+        TXT_OVERWRITE_CONFIG,
+        TXT_SET_TITLE,
+        TXT_SET_ITEM,
+        TXT_SET_UNKNOWN,
+       TXT_SET_NOT_BOOLEAN,
+       TXT_TRANSLATION_NOT_FOUND,
+        TXT_TRANSLATION_FILE_ERROR
 };
 
 extern FORMAT_REC fecommon_core_formats[];
 };
 
 extern FORMAT_REC fecommon_core_formats[];
index 38de158d3eb8ecf0b74158341d65fddcab167e6c..1900adec34916a6e532a985d5e5b64f705cc7249 100644 (file)
 
 static int beep_msg_level, beep_when_away, beep_when_window_active;
 
 
 static int beep_msg_level, beep_when_away, beep_when_window_active;
 
-static int signal_gui_print_text;
-static int signal_print_text_stripped;
+static int signal_gui_print_text_finished;
+static int signal_print_starting;
 static int signal_print_text;
 static int signal_print_text;
-static int signal_print_text_finished;
 static int signal_print_format;
 static int signal_print_format;
-static int signal_print_starting;
 
 static int sending_print_starting;
 
 static void print_line(TEXT_DEST_REC *dest, const char *text);
 
 
 static int sending_print_starting;
 
 static void print_line(TEXT_DEST_REC *dest, const char *text);
 
-static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest,
-                                   int formatnum, va_list va)
+void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest,
+                                 int formatnum, va_list va)
 {
        char *arglist[MAX_FORMAT_PARAMS];
        char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
 {
        char *arglist[MAX_FORMAT_PARAMS];
        char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
@@ -54,8 +52,7 @@ static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest,
        THEME_REC *theme;
        char *str;
 
        THEME_REC *theme;
        char *str;
 
-       theme = dest->window->theme == NULL ? current_theme :
-               dest->window->theme;
+       theme = window_get_theme(dest->window);
 
        formats = g_hash_table_lookup(default_formats, module);
        format_read_arglist(va, &formats[formatnum],
 
        formats = g_hash_table_lookup(default_formats, module);
        format_read_arglist(va, &formats[formatnum],
@@ -77,6 +74,16 @@ static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest,
        g_free(str);
 }
 
        g_free(str);
 }
 
+void printformat_module_dest(const char *module, TEXT_DEST_REC *dest,
+                            int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_dest_args(module, dest, formatnum, va);
+       va_end(va);
+}
+
 void printformat_module_args(const char *module, void *server,
                             const char *target, int level,
                             int formatnum, va_list va)
 void printformat_module_args(const char *module, void *server,
                             const char *target, int level,
                             int formatnum, va_list va)
@@ -84,7 +91,7 @@ void printformat_module_args(const char *module, void *server,
        TEXT_DEST_REC dest;
 
        format_create_dest(&dest, server, target, level, NULL);
        TEXT_DEST_REC dest;
 
        format_create_dest(&dest, server, target, level, NULL);
-       printformat_module_dest(module, &dest, formatnum, va);
+       printformat_module_dest_args(module, &dest, formatnum, va);
 }
 
 void printformat_module(const char *module, void *server, const char *target,
 }
 
 void printformat_module(const char *module, void *server, const char *target,
@@ -103,7 +110,7 @@ void printformat_module_window_args(const char *module, WINDOW_REC *window,
        TEXT_DEST_REC dest;
 
        format_create_dest(&dest, NULL, NULL, level, window);
        TEXT_DEST_REC dest;
 
        format_create_dest(&dest, NULL, NULL, level, window);
-       printformat_module_dest(module, &dest, formatnum, va);
+       printformat_module_dest_args(module, &dest, formatnum, va);
 }
 
 void printformat_module_window(const char *module, WINDOW_REC *window,
 }
 
 void printformat_module_window(const char *module, WINDOW_REC *window,
@@ -133,7 +140,8 @@ void printformat_module_gui_args(const char *module, int formatnum, va_list va)
                            arglist, sizeof(arglist)/sizeof(char *),
                            buffer, sizeof(buffer));
 
                            arglist, sizeof(arglist)/sizeof(char *),
                            buffer, sizeof(buffer));
 
-       str = format_get_text_theme_charargs(current_theme, module, &dest,
+       str = format_get_text_theme_charargs(window_get_theme(dest.window),
+                                            module, &dest,
                                             formatnum, arglist);
        if (*str != '\0') format_send_to_gui(&dest, str);
        g_free(str);
                                             formatnum, arglist);
        if (*str != '\0') format_send_to_gui(&dest, str);
        g_free(str);
@@ -150,22 +158,22 @@ void printformat_module_gui(const char *module, int formatnum, ...)
 
 static void print_line(TEXT_DEST_REC *dest, const char *text)
 {
 
 static void print_line(TEXT_DEST_REC *dest, const char *text)
 {
-       char *str, *tmp;
+       char *str, *tmp, *stripped;
 
        g_return_if_fail(dest != NULL);
        g_return_if_fail(text != NULL);
 
 
        g_return_if_fail(dest != NULL);
        g_return_if_fail(text != NULL);
 
-       tmp = format_get_level_tag(current_theme, dest);
+       tmp = format_get_level_tag(window_get_theme(dest->window), dest);
        str = format_add_linestart(text, tmp);
        g_free_not_null(tmp);
 
        str = format_add_linestart(text, tmp);
        g_free_not_null(tmp);
 
-       /* send the plain text version for logging etc.. */
-       tmp = strip_codes(str);
-       signal_emit_id(signal_print_text_stripped, 2, dest, tmp);
-       g_free(tmp);
+       /* send both the formatted + stripped (for logging etc.) */
+       stripped = strip_codes(str);
+       signal_emit_id(signal_print_text, 3, dest, str, stripped);
+        g_free_and_null(dest->hilight_color);
 
 
-       signal_emit_id(signal_print_text, 2, dest, str);
        g_free(str);
        g_free(str);
+        g_free(stripped);
 }
 
 /* append string to `out', expand newlines. */
 }
 
 /* append string to `out', expand newlines. */
@@ -237,7 +245,7 @@ static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str,
                        break;
                }
                default:
                        break;
                }
                default:
-                       if (!format_expand_styles(out, *str)) {
+                       if (!format_expand_styles(out, &str, &dest->flags)) {
                                g_string_append_c(out, '%');
                                g_string_append_c(out, *str);
                        }
                                g_string_append_c(out, '%');
                                g_string_append_c(out, *str);
                        }
@@ -250,7 +258,7 @@ static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str,
        return ret;
 }
 
        return ret;
 }
 
-static char *printtext_expand_formats(const char *str)
+static char *printtext_expand_formats(const char *str, int *flags)
 {
        GString *out;
        char *ret;
 {
        GString *out;
        char *ret;
@@ -265,7 +273,7 @@ static char *printtext_expand_formats(const char *str)
                if (*++str == '\0')
                        break;
 
                if (*++str == '\0')
                        break;
 
-               if (!format_expand_styles(out, *str)) {
+               if (!format_expand_styles(out, &str, flags)) {
                        g_string_append_c(out, '%');
                        g_string_append_c(out, *str);
                }
                        g_string_append_c(out, '%');
                        g_string_append_c(out, *str);
                }
@@ -276,7 +284,7 @@ static char *printtext_expand_formats(const char *str)
        return ret;
 }
 
        return ret;
 }
 
-void printtext_dest(TEXT_DEST_REC *dest, const char *text, va_list va)
+static void printtext_dest_args(TEXT_DEST_REC *dest, const char *text, va_list va)
 {
        char *str;
 
 {
        char *str;
 
@@ -291,6 +299,15 @@ void printtext_dest(TEXT_DEST_REC *dest, const char *text, va_list va)
        g_free(str);
 }
 
        g_free(str);
 }
 
+void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...)
+{
+       va_list va;
+
+       va_start(va, text);
+       printtext_dest_args(dest, text, va);
+       va_end(va);
+}
+
 /* Write text to target - convert color codes */
 void printtext(void *server, const char *target, int level, const char *text, ...)
 {
 /* Write text to target - convert color codes */
 void printtext(void *server, const char *target, int level, const char *text, ...)
 {
@@ -302,7 +319,7 @@ void printtext(void *server, const char *target, int level, const char *text, ..
         format_create_dest(&dest, server, target, level, NULL);
 
        va_start(va, text);
         format_create_dest(&dest, server, target, level, NULL);
 
        va_start(va, text);
-       printtext_dest(&dest, text, va);
+       printtext_dest_args(&dest, text, va);
        va_end(va);
 }
 
        va_end(va);
 }
 
@@ -322,7 +339,29 @@ void printtext_string(void *server, const char *target, int level, const char *t
                 sending_print_starting = FALSE;
        }
 
                 sending_print_starting = FALSE;
        }
 
-        str = printtext_expand_formats(text);
+        str = printtext_expand_formats(text, &dest.flags);
+       print_line(&dest, str);
+        g_free(str);
+}
+
+/* Like printtext_window(), but don't handle %s etc. */
+void printtext_string_window(WINDOW_REC *window, int level, const char *text)
+{
+       TEXT_DEST_REC dest;
+        char *str;
+
+       g_return_if_fail(text != NULL);
+
+       format_create_dest(&dest, NULL, NULL, level,
+                          window != NULL ? window : active_win);
+
+       if (!sending_print_starting) {
+               sending_print_starting = TRUE;
+               signal_emit_id(signal_print_starting, 1, dest);
+                sending_print_starting = FALSE;
+       }
+
+        str = printtext_expand_formats(text, &dest.flags);
        print_line(&dest, str);
         g_free(str);
 }
        print_line(&dest, str);
         g_free(str);
 }
@@ -338,7 +377,7 @@ void printtext_window(WINDOW_REC *window, int level, const char *text, ...)
                           window != NULL ? window : active_win);
 
        va_start(va, text);
                           window != NULL ? window : active_win);
 
        va_start(va, text);
-       printtext_dest(&dest, text, va);
+       printtext_dest_args(&dest, text, va);
        va_end(va);
 }
 
        va_end(va);
 }
 
@@ -351,14 +390,14 @@ void printtext_gui(const char *text)
 
         memset(&dest, 0, sizeof(dest));
 
 
         memset(&dest, 0, sizeof(dest));
 
-       str = printtext_expand_formats(text);
+       str = printtext_expand_formats(text, &dest.flags);
        format_send_to_gui(&dest, str);
        g_free(str);
 }
 
 static void msg_beep_check(TEXT_DEST_REC *dest)
 {
        format_send_to_gui(&dest, str);
        g_free(str);
 }
 
 static void msg_beep_check(TEXT_DEST_REC *dest)
 {
-       if (dest->level != 0 && (dest->level & MSGLEVEL_NOHILIGHT) == 0 &&
+       if (dest->level != 0 && (dest->level & MSGLEVEL_NO_ACT) == 0 &&
            (beep_msg_level & dest->level) &&
            (beep_when_away || (dest->server != NULL &&
                                !dest->server->usermode_away)) &&
            (beep_msg_level & dest->level) &&
            (beep_when_away || (dest->server != NULL &&
                                !dest->server->usermode_away)) &&
@@ -372,6 +411,7 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text)
        char *str, *tmp;
 
        g_return_if_fail(dest != NULL);
        char *str, *tmp;
 
        g_return_if_fail(dest != NULL);
+       g_return_if_fail(dest->window != NULL);
        g_return_if_fail(text != NULL);
 
        msg_beep_check(dest);
        g_return_if_fail(text != NULL);
 
        msg_beep_check(dest);
@@ -380,19 +420,15 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text)
 
        /* add timestamp/server tag here - if it's done in print_line()
           it would be written to log files too */
 
        /* add timestamp/server tag here - if it's done in print_line()
           it would be written to log files too */
-       tmp = format_get_line_start(current_theme, dest, time(NULL));
+       tmp = format_get_line_start(window_get_theme(dest->window),
+                                   dest, time(NULL));
        str = format_add_linestart(text, tmp);
        g_free_not_null(tmp);
 
        format_send_to_gui(dest, str);
        g_free(str);
 
        str = format_add_linestart(text, tmp);
        g_free_not_null(tmp);
 
        format_send_to_gui(dest, str);
        g_free(str);
 
-       signal_emit_id(signal_print_text_finished, 1, dest->window);
-}
-
-static void sig_print_text_free(TEXT_DEST_REC *dest, const char *text)
-{
-        g_free_and_null(dest->hilight_color);
+       signal_emit_id(signal_gui_print_text_finished, 1, dest->window);
 }
 
 void printtext_multiline(void *server, const char *target, int level,
 }
 
 void printtext_multiline(void *server, const char *target, int level,
@@ -433,16 +469,13 @@ static void read_settings(void)
 void printtext_init(void)
 {
        sending_print_starting = FALSE;
 void printtext_init(void)
 {
        sending_print_starting = FALSE;
-       signal_gui_print_text = signal_get_uniq_id("gui print text");
-       signal_print_text_stripped = signal_get_uniq_id("print text stripped");
+       signal_gui_print_text_finished = signal_get_uniq_id("gui print text finished");
+       signal_print_starting = signal_get_uniq_id("print starting");
        signal_print_text = signal_get_uniq_id("print text");
        signal_print_text = signal_get_uniq_id("print text");
-       signal_print_text_finished = signal_get_uniq_id("print text finished");
        signal_print_format = signal_get_uniq_id("print format");
        signal_print_format = signal_get_uniq_id("print format");
-       signal_print_starting = signal_get_uniq_id("print starting");
 
        read_settings();
        signal_add("print text", (SIGNAL_FUNC) sig_print_text);
 
        read_settings();
        signal_add("print text", (SIGNAL_FUNC) sig_print_text);
-       signal_add_last("print text", (SIGNAL_FUNC) sig_print_text_free);
        signal_add("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
        signal_add("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
@@ -450,7 +483,6 @@ void printtext_init(void)
 void printtext_deinit(void)
 {
        signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
 void printtext_deinit(void)
 {
        signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
-       signal_remove("print text", (SIGNAL_FUNC) sig_print_text_free);
        signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
        signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
index 14f46564489659b85f0799cd9e574895cd01082a..463f3572810357cc2756d06752093f19a84e526f 100644 (file)
@@ -2,17 +2,22 @@
 #define __PRINTTEXT_H
 
 #include "fe-windows.h"
 #define __PRINTTEXT_H
 
 #include "fe-windows.h"
+#include "formats.h"
 
 void printformat_module(const char *module, void *server, const char *target, int level, int formatnum, ...);
 void printformat_module_window(const char *module, WINDOW_REC *window, int level, int formatnum, ...);
 
 void printformat_module(const char *module, void *server, const char *target, int level, int formatnum, ...);
 void printformat_module_window(const char *module, WINDOW_REC *window, int level, int formatnum, ...);
+void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, int formatnum, ...);
 
 void printformat_module_args(const char *module, void *server, const char *target, int level, int formatnum, va_list va);
 void printformat_module_window_args(const char *module, WINDOW_REC *window, int level, int formatnum, va_list va);
 
 void printformat_module_args(const char *module, void *server, const char *target, int level, int formatnum, va_list va);
 void printformat_module_window_args(const char *module, WINDOW_REC *window, int level, int formatnum, va_list va);
+void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest, int formatnum, va_list va);
 
 void printtext(void *server, const char *target, int level, const char *text, ...);
 void printtext_string(void *server, const char *target, int level, const char *text);
 
 void printtext(void *server, const char *target, int level, const char *text, ...);
 void printtext_string(void *server, const char *target, int level, const char *text);
+void printtext_string_window(WINDOW_REC *window, int level, const char *text);
 void printtext_window(WINDOW_REC *window, int level, const char *text, ...);
 void printtext_multiline(void *server, const char *target, int level, const char *format, const char *text);
 void printtext_window(WINDOW_REC *window, int level, const char *text, ...);
 void printtext_multiline(void *server, const char *target, int level, const char *format, const char *text);
+void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...);
 
 /* only GUI should call these - used for printing text to somewhere else
    than windows */
 
 /* only GUI should call these - used for printing text to somewhere else
    than windows */
@@ -35,6 +40,8 @@ void printtext_deinit(void);
        printformat_module(MODULE_NAME, server, target, level, ##formatnum)
 #  define printformat_window(window, level, formatnum...) \
        printformat_module_window(MODULE_NAME, window, level, ##formatnum)
        printformat_module(MODULE_NAME, server, target, level, ##formatnum)
 #  define printformat_window(window, level, formatnum...) \
        printformat_module_window(MODULE_NAME, window, level, ##formatnum)
+#  define printformat_dest(dest, formatnum...) \
+       printformat_module_dest(MODULE_NAME, dest, ##formatnum)
 #  define printformat_gui(formatnum...) \
        printformat_module_gui(MODULE_NAME, ##formatnum)
 #elif defined (_ISOC99_SOURCE)
 #  define printformat_gui(formatnum...) \
        printformat_module_gui(MODULE_NAME, ##formatnum)
 #elif defined (_ISOC99_SOURCE)
@@ -43,6 +50,8 @@ void printtext_deinit(void);
        printformat_module(MODULE_NAME, server, target, level, formatnum, __VA_ARGS__)
 #  define printformat_window(window, level, formatnum, ...) \
        printformat_module_window(MODULE_NAME, window, level, formatnum, __VA_ARGS__)
        printformat_module(MODULE_NAME, server, target, level, formatnum, __VA_ARGS__)
 #  define printformat_window(window, level, formatnum, ...) \
        printformat_module_window(MODULE_NAME, window, level, formatnum, __VA_ARGS__)
+#  define printformat_dest(dest, formatnum, ...) \
+       printformat_module_dest(MODULE_NAME, dest, formatnum, __VA_ARGS__)
 #  define printformat_gui(formatnum, ...) \
        printformat_module_gui(MODULE_NAME, formatnum, __VA_ARGS__)
 #else
 #  define printformat_gui(formatnum, ...) \
        printformat_module_gui(MODULE_NAME, formatnum, __VA_ARGS__)
 #else
@@ -75,6 +84,20 @@ void printformat_window(WINDOW_REC *window, int level, int formatnum, ...)
        va_end(va);
 }
 
        va_end(va);
 }
 
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+void printformat_dest(TEXT_DEST_REC *dest, int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_dest_args(MODULE_NAME, dest, formatnum, va);
+       va_end(va);
+}
+
 #ifdef G_CAN_INLINE
 G_INLINE_FUNC
 #else
 #ifdef G_CAN_INLINE
 G_INLINE_FUNC
 #else
index ab768ccbe53f8c34e7a0b65951b3709731ae9414..f009f0ba9ac38729569cc5560f4561393b94a575 100644 (file)
@@ -119,7 +119,8 @@ static char *theme_replace_expand(THEME_REC *theme, int index,
        abstract = theme_format_expand_data(theme, (const char **) &abstract,
                                            default_fg, default_bg,
                                            last_fg, last_bg, flags);
        abstract = theme_format_expand_data(theme, (const char **) &abstract,
                                            default_fg, default_bg,
                                            last_fg, last_bg, flags);
-       ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0);
+       ret = parse_special_string(abstract, NULL, NULL, data, NULL,
+                                  PARSE_FLAG_ONLY_ARGS);
        g_free(abstract);
        return ret;
 }
        g_free(abstract);
        return ret;
 }
@@ -128,9 +129,9 @@ static const char *fgcolorformats = "nkrgybmpcwKRGYBMPCW";
 static const char *bgcolorformats = "n01234567";
 
 #define IS_FGCOLOR_FORMAT(c) \
 static const char *bgcolorformats = "n01234567";
 
 #define IS_FGCOLOR_FORMAT(c) \
-        ((c) != '\0' && strchr(fgcolorformats, (c)) != NULL)
+        ((c) != '\0' && strchr(fgcolorformats, c) != NULL)
 #define IS_BGCOLOR_FORMAT(c) \
 #define IS_BGCOLOR_FORMAT(c) \
-        ((c) != '\0' && strchr(bgcolorformats, (c)) != NULL)
+        ((c) != '\0' && strchr(bgcolorformats, c) != NULL)
 
 /* append "variable" part in $variable, ie. not the contents of the variable */
 static void theme_format_append_variable(GString *str, const char **format)
 
 /* append "variable" part in $variable, ie. not the contents of the variable */
 static void theme_format_append_variable(GString *str, const char **format)
@@ -143,7 +144,7 @@ static void theme_format_append_variable(GString *str, const char **format)
        (*format)++;
 
        value = parse_special((char **) format, NULL, NULL,
        (*format)++;
 
        value = parse_special((char **) format, NULL, NULL,
-                             args, &free_ret, NULL, 0);
+                             args, &free_ret, NULL, PARSE_FLAG_ONLY_ARGS);
        if (free_ret) g_free(value);
        (*format)++;
 
        if (free_ret) g_free(value);
        (*format)++;
 
@@ -211,7 +212,9 @@ static void theme_format_append_next(THEME_REC *theme, GString *str,
                        return;
                }
 
                        return;
                }
 
-               /* %{ or %} gives us { or } char */
+               /* %{ or %} gives us { or } char - keep the % char
+                  though to make sure {} isn't treated as abstract */
+               g_string_append_c(str, '%');
                chr = **format;
        }
 
                chr = **format;
        }
 
@@ -232,6 +235,54 @@ static void theme_format_append_next(THEME_REC *theme, GString *str,
         (*format)++;
 }
 
         (*format)++;
 }
 
+/* returns TRUE if data is empty, or the data is a $variable which is empty */
+static int data_is_empty(const char **data)
+{
+       /* since we don't know the real argument list, assume there's always
+          an argument in them */
+       char *arglist[] = {
+               "x", "x", "x", "x", "x", "x","x", "x", "x", "x",
+               NULL
+       };
+       const char *p;
+       char *ret;
+        int free_ret, empty;
+
+        p = *data;
+       while (*p == ' ') p++;
+
+       if (*p == '}') {
+                /* empty */
+                *data = p+1;
+                return TRUE;
+       }
+
+       if (*p != '$') {
+                /* not empty */
+               return FALSE;
+       }
+
+       /* variable - check if it's empty */
+        p++;
+       ret = parse_special((char **) &p,
+                           active_win == NULL ? NULL : active_win->active_server,
+                           active_win == NULL ? NULL : active_win->active,
+                           arglist, &free_ret, NULL, 0);
+        p++;
+
+       while (*p == ' ') p++;
+       empty = *p == '}' && (ret == NULL || *ret == '\0');
+        if (free_ret) g_free(ret);
+
+       if (empty) {
+               /* empty */
+               *data = p+1;
+                return TRUE;
+       }
+
+        return FALSE;
+}
+
 /* expand a single {abstract ...data... } */
 static char *theme_format_expand_abstract(THEME_REC *theme,
                                          const char **formatp,
 /* expand a single {abstract ...data... } */
 static char *theme_format_expand_abstract(THEME_REC *theme,
                                          const char **formatp,
@@ -258,17 +309,11 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
           treated as arguments */
        if (*p == ' ') {
                len++;
           treated as arguments */
        if (*p == ' ') {
                len++;
-               if ((flags & EXPAND_FLAG_IGNORE_EMPTY)) {
-                        /* if the data is empty, ignore the abstract */
-                       p = format+len;
-                       while (*p == ' ') p++;
-                       if (*p == '}') {
-                                *formatp = p+1;
-                               g_free(abstract);
-                               return NULL;
-                       }
+               if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) {
+                       *formatp = p;
+                       g_free(abstract);
+                       return NULL;
                }
                }
-
        }
        *formatp = format+len;
 
        }
        *formatp = format+len;
 
@@ -287,7 +332,7 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
                                        NULL, NULL, flags);
        len = strlen(data);
 
                                        NULL, NULL, flags);
        len = strlen(data);
 
-       if (len > 1 && isdigit(data[len-1]) && data[len-2] == '$') {
+       if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') {
                /* ends with $<digit> .. this breaks things if next
                   character is digit or '-' */
                 char digit, *tmp;
                /* ends with $<digit> .. this breaks things if next
                   character is digit or '-' */
                 char digit, *tmp;
@@ -300,7 +345,8 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
                g_free(tmp);
        }
 
                g_free(tmp);
        }
 
-       ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0);
+       ret = parse_special_string(abstract, NULL, NULL, data, NULL,
+                                  PARSE_FLAG_ONLY_ARGS);
        g_free(abstract);
         g_free(data);
        abstract = ret;
        g_free(abstract);
         g_free(data);
        abstract = ret;
@@ -701,11 +747,11 @@ THEME_REC *theme_load(const char *setname)
        theme = theme_find(name);
 
        /* check home dir */
        theme = theme_find(name);
 
        /* check home dir */
-       fname = g_strdup_printf("%s/.silc/%s.theme", g_get_home_dir(), name);
+       fname = g_strdup_printf("%s/%s.theme", get_irssi_dir(), name);
        if (stat(fname, &statbuf) != 0) {
                /* check global config dir */
                g_free(fname);
        if (stat(fname, &statbuf) != 0) {
                /* check global config dir */
                g_free(fname);
-               fname = g_strdup_printf(SYSCONFDIR"/irssi/%s.theme", name);
+               fname = g_strdup_printf(THEMESDIR"/%s.theme", name);
                if (stat(fname, &statbuf) != 0) {
                        /* theme not found */
                        g_free(fname);
                if (stat(fname, &statbuf) != 0) {
                        /* theme not found */
                        g_free(fname);
@@ -795,9 +841,11 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data)
        }
 
        theme->default_color =
        }
 
        theme->default_color =
-               config_get_int(config, NULL, "default_color", 0);
-       theme->default_real_color =
-               config_get_int(config, NULL, "default_real_color", 7);
+               config_get_int(config, NULL, "default_color", -1);
+       /* FIXME: remove after 0.7.99 */
+       if (theme->default_color == 0 &&
+           config_get_int(config, NULL, "default_real_color", -1) != -1)
+                theme->default_color = -1;
        theme_read_replaces(config, theme);
 
        if (data == NULL) {
        theme_read_replaces(config, theme);
 
        if (data == NULL) {
@@ -952,8 +1000,13 @@ static void cmd_format(const char *data)
         cmd_params_free(free_arg);
 }
 
         cmd_params_free(free_arg);
 }
 
+typedef struct {
+       CONFIG_REC *config;
+        int save_all;
+} THEME_SAVE_REC;
+
 static void module_save(const char *module, MODULE_THEME_REC *rec,
 static void module_save(const char *module, MODULE_THEME_REC *rec,
-                        CONFIG_REC *config)
+                        THEME_SAVE_REC *data)
 {
        CONFIG_NODE *fnode, *node;
        FORMAT_REC *formats;
 {
        CONFIG_NODE *fnode, *node;
        FORMAT_REC *formats;
@@ -962,27 +1015,33 @@ static void module_save(const char *module, MODULE_THEME_REC *rec,
         formats = g_hash_table_lookup(default_formats, rec->name);
        if (formats == NULL) return;
 
         formats = g_hash_table_lookup(default_formats, rec->name);
        if (formats == NULL) return;
 
-       fnode = config_node_traverse(config, "formats", TRUE);
+       fnode = config_node_traverse(data->config, "formats", TRUE);
 
        node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
 
        node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
-       for (n = 0; formats[n].def != NULL; n++) {
+       for (n = 1; formats[n].def != NULL; n++) {
                 if (rec->formats[n] != NULL) {
                 if (rec->formats[n] != NULL) {
-                        config_node_set_str(config, node, formats[n].tag,
+                        config_node_set_str(data->config, node, formats[n].tag,
                                             rec->formats[n]);
                                             rec->formats[n]);
-                }
+               } else if (data->save_all && formats[n].tag != NULL) {
+                        config_node_set_str(data->config, node, formats[n].tag,
+                                            formats[n].def);
+               }
         }
 
         if (node->value == NULL) {
                 /* not modified, don't keep the empty section */
         }
 
         if (node->value == NULL) {
                 /* not modified, don't keep the empty section */
-                config_node_remove(config, fnode, node);
-                if (fnode->value == NULL)
-                        config_node_remove(config, config->mainnode, fnode);
+                config_node_remove(data->config, fnode, node);
+               if (fnode->value == NULL) {
+                       config_node_remove(data->config,
+                                          data->config->mainnode, fnode);
+               }
         }
 }
 
         }
 }
 
-static void theme_save(THEME_REC *theme)
+static void theme_save(THEME_REC *theme, int save_all)
 {
        CONFIG_REC *config;
 {
        CONFIG_REC *config;
+       THEME_SAVE_REC data;
        char *path;
        int ok;
 
        char *path;
        int ok;
 
@@ -1002,10 +1061,12 @@ static void theme_save(THEME_REC *theme)
                 }
         }
 
                 }
         }
 
-       g_hash_table_foreach(theme->modules, (GHFunc) module_save, config);
+       data.config = config;
+        data.save_all = save_all;
+       g_hash_table_foreach(theme->modules, (GHFunc) module_save, &data);
 
 
-        /* always save the theme to ~/.silc/ */
-       path = g_strdup_printf("%s/.silc/%s", g_get_home_dir(),
+        /* always save the theme to ~/.irssi/ */
+       path = g_strdup_printf("%s/%s", get_irssi_dir(),
                               g_basename(theme->path));
        ok = config_write(config, path, 0660) == 0;
 
                               g_basename(theme->path));
        ok = config_write(config, path, 0660) == 0;
 
@@ -1017,16 +1078,27 @@ static void theme_save(THEME_REC *theme)
        config_close(config);
 }
 
        config_close(config);
 }
 
-/* save changed formats */
-static void cmd_save(void)
+/* save changed formats, -format saves all */
+static void cmd_save(const char *data)
 {
        GSList *tmp;
 {
        GSList *tmp;
+        GHashTable *optlist;
+        void *free_arg;
+       char *fname;
+       int saveall;
 
 
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "save", &optlist, &fname))
+               return;
+
+        saveall = g_hash_table_lookup(optlist, "formats") != NULL;
        for (tmp = themes; tmp != NULL; tmp = tmp->next) {
                THEME_REC *theme = tmp->data;
 
        for (tmp = themes; tmp != NULL; tmp = tmp->next) {
                THEME_REC *theme = tmp->data;
 
-               theme_save(theme);
+               theme_save(theme, saveall);
        }
        }
+
+       cmd_params_free(free_arg);
 }
 
 static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list)
 }
 
 static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list)
@@ -1102,9 +1174,9 @@ static void change_theme(const char *name, int verbose)
        if (rec != NULL) {
                current_theme = rec;
                if (verbose) {
        if (rec != NULL) {
                current_theme = rec;
                if (verbose) {
-                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
-                                          TXT_THEME_CHANGED,
-                                          rec->name, rec->path);
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                                   TXT_THEME_CHANGED,
+                                   rec->name, rec->path);
                }
        } else if (verbose) {
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
                }
        } else if (verbose) {
                printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
@@ -1115,9 +1187,13 @@ static void change_theme(const char *name, int verbose)
 static void read_settings(void)
 {
        const char *theme;
 static void read_settings(void)
 {
        const char *theme;
+        int len;
 
        theme = settings_get_str("theme");
 
        theme = settings_get_str("theme");
-       if (strcmp(current_theme->name, theme) != 0)
+       len = strlen(current_theme->name);
+       if (strcmp(current_theme->name, theme) != 0 &&
+           (strncmp(current_theme->name, theme, len) != 0 ||
+            strcmp(theme+len, ".theme") != 0))
                change_theme(theme, TRUE);
 }
 
                change_theme(theme, TRUE);
 }
 
@@ -1131,11 +1207,9 @@ static void themes_read(void)
        /* first there's default theme.. */
        current_theme = theme_load("default");
        if (current_theme == NULL) {
        /* first there's default theme.. */
        current_theme = theme_load("default");
        if (current_theme == NULL) {
-               fname = g_strdup_printf("%s/.silc/default.theme",
-                                       g_get_home_dir());
+               fname = g_strdup_printf("%s/default.theme", get_irssi_dir());
                current_theme = theme_create(fname, "default");
                current_theme = theme_create(fname, "default");
-               current_theme->default_color = 0;
-               current_theme->default_real_color = 7;
+               current_theme->default_color = -1;
                 theme_read(current_theme, NULL, default_theme);
                g_free(fname);
        }
                 theme_read(current_theme, NULL, default_theme);
                g_free(fname);
        }
@@ -1165,6 +1239,7 @@ void themes_init(void)
        signal_add("setup reread", (SIGNAL_FUNC) themes_read);
 
        command_set_options("format", "delete reset");
        signal_add("setup reread", (SIGNAL_FUNC) themes_read);
 
        command_set_options("format", "delete reset");
+       command_set_options("save", "formats");
 }
 
 void themes_deinit(void)
 }
 
 void themes_deinit(void)
index 96f234003a58dba632e8865e2d3b3d9bcdb3dafb..59d2810ad6550be38e377c6369dbae8bcfc60a26 100644 (file)
@@ -16,11 +16,8 @@ typedef struct {
         time_t last_modify;
 
        int default_color; /* default color to use with text with default
         time_t last_modify;
 
        int default_color; /* default color to use with text with default
-                             background. default is 0 which means the default
-                             color set by terminal */
-       int default_real_color; /* default color to use with background set.
-                                  this shouldn't be 0, unless black is really
-                                  wanted. default is 7 (white). */
+                             background. default is -1 which means the
+                             default color set by terminal */
        GHashTable *modules;
 
         int replace_keys[256]; /* index to replace_values for each char */
        GHashTable *modules;
 
         int replace_keys[256]; /* index to replace_values for each char */
@@ -47,7 +44,7 @@ void theme_register_module(const char *module, FORMAT_REC *formats);
 void theme_unregister_module(const char *module);
 
 #define EXPAND_FLAG_IGNORE_REPLACES     0x01 /* don't use the character replaces when expanding */
 void theme_unregister_module(const char *module);
 
 #define EXPAND_FLAG_IGNORE_REPLACES     0x01 /* don't use the character replaces when expanding */
-#define EXPAND_FLAG_IGNORE_EMPTY        0x02 /* if abstract's argument is empty, don't try to expand it (ie. {xx }, but not {xx}) */
+#define EXPAND_FLAG_IGNORE_EMPTY        0x02 /* if abstract's argument is empty, or the argument is a $variable that is empty, don't try to expand it (ie. {xx }, but not {xx}) */
 #define EXPAND_FLAG_RECURSIVE_MASK      0x0f
 /* private */
 #define EXPAND_FLAG_ROOT               0x10
 #define EXPAND_FLAG_RECURSIVE_MASK      0x0f
 /* private */
 #define EXPAND_FLAG_ROOT               0x10
index 2713cc7392dba4b30ef48bf5f42df42dd4849a65..d573fc1742b41c3546ea2620d9b67af51fa280ab 100644 (file)
 */
 
 #include "module.h"
 */
 
 #include "module.h"
+#include "module-formats.h"
 #include "signals.h"
 #include "line-split.h"
 #include "misc.h"
 #include "signals.h"
 #include "line-split.h"
 #include "misc.h"
+#include "levels.h"
 #include "settings.h"
 
 #include "settings.h"
 
+#include "printtext.h"
+
 unsigned char translation_in[256], translation_out[256];
 unsigned char translation_in[256], translation_out[256];
+static char *current_translation;
 
 void translation_reset(void)
 {
 
 void translation_reset(void)
 {
@@ -45,7 +50,7 @@ void translate_output(char *text)
 }
 
 #define gethex(a) \
 }
 
 #define gethex(a) \
-       (isdigit(a) ? ((a)-'0') : (toupper(a)-'A'+10))
+       (i_isdigit(a) ? ((a)-'0') : (i_toupper(a)-'A'+10))
 
 void translation_parse_line(const char *str, int *pos)
 {
 
 void translation_parse_line(const char *str, int *pos)
 {
@@ -81,7 +86,12 @@ int translation_read(const char *file)
        f = open(file, O_RDONLY);
        g_free(path);
 
        f = open(file, O_RDONLY);
        g_free(path);
 
-       if (f == -1) return FALSE;
+       if (f == -1) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_TRANSLATION_NOT_FOUND, file,
+                           g_strerror(errno));
+               return FALSE;
+       }
 
        pos = 0; buffer = NULL;
        while (pos < 512) {
 
        pos = 0; buffer = NULL;
        while (pos < 512) {
@@ -95,20 +105,40 @@ int translation_read(const char *file)
        line_split_free(buffer);
 
        close(f);
        line_split_free(buffer);
 
        close(f);
-       if (pos != 512)
+       if (pos != 512) {
                translation_reset();
                translation_reset();
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_TRANSLATION_FILE_ERROR, file);
+       }
        return pos == 512;
 }
 
 static void read_settings(void)
 {
        return pos == 512;
 }
 
 static void read_settings(void)
 {
-       translation_read(settings_get_str("translation"));
+       const char *translation;
+
+       translation = settings_get_str("translation");
+       if (*translation == '\0') {
+               if (current_translation != NULL) {
+                       g_free_and_null(current_translation);
+                        translation_reset();
+               }
+               return;
+       }
+
+       if (current_translation == NULL ||
+           strcmp(translation, current_translation) != 0) {
+                g_free_not_null(current_translation);
+               current_translation = g_strdup(translation);
+               translation_read(translation);
+       }
 }
 
 void translation_init(void)
 {
        translation_reset();
 
 }
 
 void translation_init(void)
 {
        translation_reset();
 
+        current_translation = NULL;
        settings_add_str("misc", "translation", "");
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 
        settings_add_str("misc", "translation", "");
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 
index 17cf65e5aa8170ca8246dfb689bd0cf8d2402a81..e388f23bb20182b6eb00572fb3eb93881d8e6407 100644 (file)
 #include "windows-layout.h"
 #include "printtext.h"
 
 #include "windows-layout.h"
 #include "printtext.h"
 
-static void cmd_window(const char *data, void *server, WI_ITEM_REC *item)
+static void window_print_binds(WINDOW_REC *win)
 {
 {
-       if (is_numeric(data, 0)) {
-                signal_emit("command window refnum", 3, data, server, item);
-               return;
+       GSList *tmp;
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_BOUND_ITEMS_HEADER);
+       for (tmp = win->bound_items; tmp != NULL; tmp = tmp->next) {
+               WINDOW_BIND_REC *bind = tmp->data;
+
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_BOUND_ITEM,
+                                  bind->name, bind->servertag,
+                                  bind->sticky ? "sticky" : "");
+       }
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER);
+}
+
+static void window_print_items(WINDOW_REC *win)
+{
+       GSList *tmp;
+        const char *type;
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_ITEMS_HEADER);
+       for (tmp = win->items; tmp != NULL; tmp = tmp->next) {
+               WI_ITEM_REC *item = tmp->data;
+
+               type = module_find_id_str("WINDOW ITEM TYPE", item->type);
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_ITEM,
+                                  type == NULL ? "??" : type, item->name,
+                                  item->server == NULL ? "" :
+                                  item->server->tag);
+       }
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_ITEMS_FOOTER);
+}
+
+static void cmd_window_info(WINDOW_REC *win)
+{
+        char *levelstr;
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_HEADER);
+
+        /* Window reference number + sticky status */
+       if (!win->sticky_refnum) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_REFNUM, win->refnum);
+       } else {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_REFNUM_STICKY, win->refnum);
+       }
+
+        /* Window name */
+       if (win->name != NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_NAME, win->name);
+       }
+
+       /* Window history name */
+       if (win->history_name != NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_HISTORY, win->history_name);
+       }
+
+        /* Window width / height */
+       printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_SIZE,
+                          win->width, win->height);
+
+        /* Window level */
+       levelstr = win->level == 0 ?
+               g_strdup("NONE") : bits2level(win->level);
+       printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_LEVEL,
+                          levelstr);
+       g_free(levelstr);
+
+        /* Active window server + sticky status */
+       if (win->servertag == NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_SERVER,
+                                  win->active_server != NULL ?
+                                  win->active_server->tag : "NONE");
+       } else {
+               if (win->active_server != NULL &&
+                   strcmp(win->active_server->tag, win->servertag) != 0)
+                        g_warning("Active server isn't the sticky server!");
+
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_SERVER_STICKY,
+                                  win->servertag);
        }
 
        }
 
-       command_runsub("window", data, server, item);
+        /* Window theme + error status */
+       if (win->theme_name != NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_THEME, win->theme_name,
+                                  win->theme != NULL ? "" : "(not loaded)");
+       }
+
+        /* Bound items in window */
+       if (win->bound_items != NULL)
+                window_print_binds(win);
+
+        /* Item */
+       if (win->items != NULL)
+                window_print_items(win);
+
+        signal_emit("window print info", 1, win);
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_FOOTER);
+}
+
+static void cmd_window(const char *data, void *server, WI_ITEM_REC *item)
+{
+        while (*data == ' ') data++;
+
+       if (*data == '\0')
+                cmd_window_info(active_win);
+       else if (is_numeric(data, 0))
+                signal_emit("command window refnum", 3, data, server, item);
+        else
+               command_runsub("window", data, server, item);
 }
 
 /* SYNTAX: WINDOW NEW [hide] */
 }
 
 /* SYNTAX: WINDOW NEW [hide] */
@@ -77,7 +194,7 @@ static void cmd_window_close(const char *data)
        }
 
        first_num = *first == '\0' ? active_win->refnum : atoi(first);
        }
 
        first_num = *first == '\0' ? active_win->refnum : atoi(first);
-       last_num = *last == '\0' ? active_win->refnum : atoi(last);
+       last_num = *last == '\0' ? first_num : atoi(last);
 
         /* get list of windows to destroy */
         destroys = NULL;
 
         /* get list of windows to destroy */
         destroys = NULL;
@@ -224,7 +341,7 @@ static void cmd_window_server(const char *data)
                            "window server", &optlist, &tag))
                return;
 
                            "window server", &optlist, &tag))
                return;
 
-       if (*tag == '\0' &&
+       if (*tag == '\0' && active_win->active_server != NULL &&
            (g_hash_table_lookup(optlist, "sticky") != NULL ||
             g_hash_table_lookup(optlist, "unsticky") != NULL)) {
                tag = active_win->active_server->tag;
            (g_hash_table_lookup(optlist, "sticky") != NULL ||
             g_hash_table_lookup(optlist, "unsticky") != NULL)) {
                tag = active_win->active_server->tag;
@@ -238,7 +355,7 @@ static void cmd_window_server(const char *data)
            active_win->servertag != NULL) {
                g_free_and_null(active_win->servertag);
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
            active_win->servertag != NULL) {
                g_free_and_null(active_win->servertag);
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
-                                  TXT_UNSET_SERVER_STICKY, server->tag);
+                                  TXT_UNSET_SERVER_STICKY);
        }
 
        if (active_win->servertag != NULL &&
        }
 
        if (active_win->servertag != NULL &&
@@ -297,18 +414,25 @@ static void cmd_window_item_goto(const char *data, SERVER_REC *server)
 static void cmd_window_item_move(const char *data, SERVER_REC *server,
                                  WI_ITEM_REC *item)
 {
 static void cmd_window_item_move(const char *data, SERVER_REC *server,
                                  WI_ITEM_REC *item)
 {
-        WINDOW_REC *window;
+       WINDOW_REC *window;
+       void *free_arg;
+       char *target;
 
 
-        if (is_numeric(data, '\0')) {
+       if (!cmd_get_params(data, &free_arg, 1, &target))
+               return;
+
+        if (is_numeric(target, '\0')) {
                 /* move current window item to specified window */
                 /* move current window item to specified window */
-                window = window_find_refnum(atoi(data));
+                window = window_find_refnum(atoi(target));
         } else {
                 /* move specified window item to current window */
         } else {
                 /* move specified window item to current window */
-                item = window_item_find(server, data);
+                item = window_item_find(server, target);
                 window = active_win;
         }
         if (window != NULL && item != NULL)
                 window = active_win;
         }
         if (window != NULL && item != NULL)
-                window_item_set_active(window, item);
+               window_item_set_active(window, item);
+
+       cmd_params_free(free_arg);
 }
 
 /* SYNTAX: WINDOW NUMBER [-sticky] <number> */
 }
 
 /* SYNTAX: WINDOW NUMBER [-sticky] <number> */
@@ -342,75 +466,101 @@ static void cmd_window_number(const char *data)
 /* SYNTAX: WINDOW NAME <name> */
 static void cmd_window_name(const char *data)
 {
 /* SYNTAX: WINDOW NAME <name> */
 static void cmd_window_name(const char *data)
 {
-        window_set_name(active_win, data);
+       if (window_find_name(data) == NULL)
+               window_set_name(active_win, data);
+       else {
+               printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+                                  TXT_WINDOW_NAME_NOT_UNIQUE, data);
+       }
+}
+
+/* SYNTAX: WINDOW HISTORY <name> */
+void cmd_window_history(const char *data)
+{
+       window_set_history(active_win, data);
 }
 
 /* we're moving the first window to last - move the first contiguous block
    of refnums to left. Like if there's windows 1..5 and 7..10, move 1 to
 }
 
 /* we're moving the first window to last - move the first contiguous block
    of refnums to left. Like if there's windows 1..5 and 7..10, move 1 to
-   11, 2..5 to 1..4 and leave 7..10 alone  */
-static void windows_move_left(WINDOW_REC *move_window)
+   11, 2..5 to 1..4 and leave 7..10 alone */
+static void window_refnums_move_left(WINDOW_REC *move_window)
 {
        WINDOW_REC *window;
 {
        WINDOW_REC *window;
-       int refnum;
+       int refnum, new_refnum;
 
 
-       window_set_refnum(move_window, windows_refnum_last()+1);
-       for (refnum = 2;; refnum++) {
+        new_refnum = windows_refnum_last();
+       for (refnum = move_window->refnum+1; refnum <= new_refnum; refnum++) {
                window = window_find_refnum(refnum);
                window = window_find_refnum(refnum);
-               if (window == NULL) break;
+               if (window == NULL) {
+                        new_refnum++;
+                       break;
+               }
 
                window_set_refnum(window, refnum-1);
        }
 
                window_set_refnum(window, refnum-1);
        }
+
+       window_set_refnum(move_window, new_refnum);
 }
 
 /* we're moving the last window to first - make some space so we can use the
    refnum 1 */
 }
 
 /* we're moving the last window to first - make some space so we can use the
    refnum 1 */
-static void windows_move_right(WINDOW_REC *move_window)
+static void window_refnums_move_right(WINDOW_REC *move_window)
 {
        WINDOW_REC *window;
 {
        WINDOW_REC *window;
-       int refnum;
+       int refnum, new_refnum;
+
+        new_refnum = 1;
+       if (window_find_refnum(new_refnum) == NULL) {
+               window_set_refnum(move_window, new_refnum);
+                return;
+       }
 
        /* find the first unused refnum, like if there's windows
           1..5 and 7..10, we only need to move 1..5 to 2..6 */
 
        /* find the first unused refnum, like if there's windows
           1..5 and 7..10, we only need to move 1..5 to 2..6 */
-       refnum = 1;
-       while (window_find_refnum(refnum) != NULL) refnum++;
-
+       refnum = new_refnum;
+       while (move_window->refnum == refnum ||
+              window_find_refnum(refnum) != NULL) refnum++;
        refnum--;
        refnum--;
-       while (refnum > 0) {
+
+       while (refnum >= new_refnum) {
                window = window_find_refnum(refnum);
                window = window_find_refnum(refnum);
-               g_return_if_fail(window != NULL);
-               window_set_refnum(window, window == move_window ? 1 : refnum+1);
+               window_set_refnum(window, refnum+1);
 
                refnum--;
        }
 
                refnum--;
        }
+
+       window_set_refnum(move_window, new_refnum);
 }
 
 }
 
-static void cmd_window_move_left(void)
+/* SYNTAX: WINDOW MOVE PREV */
+static void cmd_window_move_prev(void)
 {
        int refnum;
 
 {
        int refnum;
 
-       refnum = window_refnum_prev(active_win->refnum, TRUE);
+       refnum = window_refnum_prev(active_win->refnum, FALSE);
        if (refnum != -1) {
                window_set_refnum(active_win, refnum);
                return;
        }
 
        if (refnum != -1) {
                window_set_refnum(active_win, refnum);
                return;
        }
 
-       windows_move_left(active_win);
+       window_refnums_move_left(active_win);
 }
 
 }
 
-static void cmd_window_move_right(void)
+/* SYNTAX: WINDOW MOVE NEXT */
+static void cmd_window_move_next(void)
 {
        int refnum;
 
 {
        int refnum;
 
-       refnum = window_refnum_next(active_win->refnum, TRUE);
+       refnum = window_refnum_next(active_win->refnum, FALSE);
        if (refnum != -1) {
                window_set_refnum(active_win, refnum);
                return;
        }
 
        if (refnum != -1) {
                window_set_refnum(active_win, refnum);
                return;
        }
 
-        windows_move_right(active_win);
+        window_refnums_move_right(active_win);
 }
 
 }
 
-/* SYNTAX: WINDOW MOVE <number>|left|right */
+/* SYNTAX: WINDOW MOVE <number>|<direction> */
 static void cmd_window_move(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        int new_refnum, refnum;
 static void cmd_window_move(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        int new_refnum, refnum;
@@ -463,23 +613,49 @@ static void cmd_window_list(void)
         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_FOOTER);
 }
 
         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_FOOTER);
 }
 
-/* SYNTAX: WINDOW THEME <name> */
+/* SYNTAX: WINDOW THEME [-delete] [<name>] */
 static void cmd_window_theme(const char *data)
 {
        THEME_REC *theme;
 static void cmd_window_theme(const char *data)
 {
        THEME_REC *theme;
+       GHashTable *optlist;
+        char *name;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "window theme", &optlist, &name))
+               return;
 
 
-       g_free_not_null(active_win->theme_name);
-       active_win->theme_name = g_strdup(data);
+       if (g_hash_table_lookup(optlist, "delete") != NULL) {
+               g_free_and_null(active_win->theme_name);
 
 
-       active_win->theme = theme = theme_load(data);
-       if (theme != NULL) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
-                                  TXT_WINDOW_THEME_CHANGED,
-                                  theme->name, theme->path);
+                                  TXT_WINDOW_THEME_REMOVED);
+       } else if (*name == '\0') {
+               if (active_win->theme == NULL) {
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_WINDOW_THEME_DEFAULT);
+               } else {
+                        theme = active_win->theme;
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_WINDOW_THEME,
+                                          theme->name, theme->path);
+               }
        } else {
        } else {
-               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
-                                  TXT_THEME_NOT_FOUND, data);
+               g_free_not_null(active_win->theme_name);
+               active_win->theme_name = g_strdup(data);
+
+               active_win->theme = theme = theme_load(data);
+               if (theme != NULL) {
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_WINDOW_THEME_CHANGED,
+                                          theme->name, theme->path);
+               } else {
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_THEME_NOT_FOUND, data);
+               }
        }
        }
+
+       cmd_params_free(free_arg);
 }
 
 static void cmd_layout(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 }
 
 static void cmd_layout(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
@@ -491,16 +667,20 @@ static void cmd_layout(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 static void cmd_foreach_window(const char *data)
 {
         WINDOW_REC *old;
 static void cmd_foreach_window(const char *data)
 {
         WINDOW_REC *old;
-       GSList *tmp;
+        GSList *list;
 
         old = active_win;
 
         old = active_win;
-       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-               WINDOW_REC *rec = tmp->data;
 
 
-                active_win = rec;
+       list = g_slist_copy(windows);
+       while (list != NULL) {
+               WINDOW_REC *rec = list->data;
+
+               active_win = rec;
                signal_emit("send command", 3, data, rec->active_server,
                            rec->active);
                signal_emit("send command", 3, data, rec->active_server,
                            rec->active);
+                list = g_slist_remove(list, list->data);
        }
        }
+
         active_win = old;
 }
 
         active_win = old;
 }
 
@@ -524,9 +704,10 @@ void window_commands_init(void)
        command_bind("window item move", NULL, (SIGNAL_FUNC) cmd_window_item_move);
        command_bind("window number", NULL, (SIGNAL_FUNC) cmd_window_number);
        command_bind("window name", NULL, (SIGNAL_FUNC) cmd_window_name);
        command_bind("window item move", NULL, (SIGNAL_FUNC) cmd_window_item_move);
        command_bind("window number", NULL, (SIGNAL_FUNC) cmd_window_number);
        command_bind("window name", NULL, (SIGNAL_FUNC) cmd_window_name);
+       command_bind("window history", NULL, (SIGNAL_FUNC) cmd_window_history);
        command_bind("window move", NULL, (SIGNAL_FUNC) cmd_window_move);
        command_bind("window move", NULL, (SIGNAL_FUNC) cmd_window_move);
-       command_bind("window move left", NULL, (SIGNAL_FUNC) cmd_window_move_left);
-       command_bind("window move right", NULL, (SIGNAL_FUNC) cmd_window_move_right);
+       command_bind("window move prev", NULL, (SIGNAL_FUNC) cmd_window_move_prev);
+       command_bind("window move next", NULL, (SIGNAL_FUNC) cmd_window_move_next);
        command_bind("window list", NULL, (SIGNAL_FUNC) cmd_window_list);
        command_bind("window theme", NULL, (SIGNAL_FUNC) cmd_window_theme);
        command_bind("layout", NULL, (SIGNAL_FUNC) cmd_layout);
        command_bind("window list", NULL, (SIGNAL_FUNC) cmd_window_list);
        command_bind("window theme", NULL, (SIGNAL_FUNC) cmd_window_theme);
        command_bind("layout", NULL, (SIGNAL_FUNC) cmd_layout);
@@ -538,6 +719,7 @@ void window_commands_init(void)
 
        command_set_options("window number", "sticky");
        command_set_options("window server", "sticky unsticky");
 
        command_set_options("window number", "sticky");
        command_set_options("window server", "sticky unsticky");
+       command_set_options("window theme", "delete");
 }
 
 void window_commands_deinit(void)
 }
 
 void window_commands_deinit(void)
@@ -560,9 +742,10 @@ void window_commands_deinit(void)
        command_unbind("window item move", (SIGNAL_FUNC) cmd_window_item_move);
        command_unbind("window number", (SIGNAL_FUNC) cmd_window_number);
        command_unbind("window name", (SIGNAL_FUNC) cmd_window_name);
        command_unbind("window item move", (SIGNAL_FUNC) cmd_window_item_move);
        command_unbind("window number", (SIGNAL_FUNC) cmd_window_number);
        command_unbind("window name", (SIGNAL_FUNC) cmd_window_name);
+       command_unbind("window history", (SIGNAL_FUNC) cmd_window_history);
        command_unbind("window move", (SIGNAL_FUNC) cmd_window_move);
        command_unbind("window move", (SIGNAL_FUNC) cmd_window_move);
-       command_unbind("window move left", (SIGNAL_FUNC) cmd_window_move_left);
-       command_unbind("window move right", (SIGNAL_FUNC) cmd_window_move_right);
+       command_unbind("window move prev", (SIGNAL_FUNC) cmd_window_move_prev);
+       command_unbind("window move next", (SIGNAL_FUNC) cmd_window_move_next);
        command_unbind("window list", (SIGNAL_FUNC) cmd_window_list);
        command_unbind("window theme", (SIGNAL_FUNC) cmd_window_theme);
        command_unbind("layout", (SIGNAL_FUNC) cmd_layout);
        command_unbind("window list", (SIGNAL_FUNC) cmd_window_list);
        command_unbind("window theme", (SIGNAL_FUNC) cmd_window_theme);
        command_unbind("layout", (SIGNAL_FUNC) cmd_layout);
index d77265214721c89fec8e245a69ddd15f7570042b..e53e5a1d1ee273ddc06fb61390c979df21584edc 100644 (file)
@@ -35,6 +35,7 @@ void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic)
 {
        g_return_if_fail(window != NULL);
        g_return_if_fail(item != NULL);
 {
        g_return_if_fail(window != NULL);
        g_return_if_fail(item != NULL);
+       g_return_if_fail(item->window == NULL);
 
         item->window = window;
 
 
         item->window = window;
 
@@ -52,7 +53,8 @@ void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic)
        window->items = g_slist_append(window->items, item);
        signal_emit("window item new", 2, window, item);
 
        window->items = g_slist_append(window->items, item);
        signal_emit("window item new", 2, window, item);
 
-       if (!automatic || g_slist_length(window->items) == 1) {
+       if (g_slist_length(window->items) == 1 ||
+           (!automatic && settings_get_bool("autofocus_new_items"))) {
                 window->active = NULL;
                window_item_set_active(window, item);
        }
                 window->active = NULL;
                window_item_set_active(window, item);
        }
@@ -66,7 +68,7 @@ void window_item_remove(WI_ITEM_REC *item)
 
        window = window_item_window(item);
 
 
        window = window_item_window(item);
 
-       if (g_slist_find(window->items, item) == NULL)
+       if (window == NULL)
                return;
 
         item->window = NULL;
                return;
 
         item->window = NULL;
@@ -86,8 +88,7 @@ void window_item_destroy(WI_ITEM_REC *item)
 
        window = window_item_window(item);
         window_item_remove(item);
 
        window = window_item_window(item);
         window_item_remove(item);
-
-       signal_emit("window item destroy", 2, window, item);
+        item->destroy(item);
 }
 
 void window_item_change_server(WI_ITEM_REC *item, void *server)
 }
 
 void window_item_change_server(WI_ITEM_REC *item, void *server)
@@ -241,14 +242,13 @@ static int window_bind_has_sticky(WINDOW_REC *window)
 void window_item_create(WI_ITEM_REC *item, int automatic)
 {
        WINDOW_REC *window;
 void window_item_create(WI_ITEM_REC *item, int automatic)
 {
        WINDOW_REC *window;
+        WINDOW_BIND_REC *bind;
        GSList *tmp, *sorted;
        int clear_waiting, reuse_unused_windows;
 
        g_return_if_fail(item != NULL);
 
        GSList *tmp, *sorted;
        int clear_waiting, reuse_unused_windows;
 
        g_return_if_fail(item != NULL);
 
-       reuse_unused_windows =
-               !settings_get_bool("autoclose_windows") ||
-               settings_get_bool("reuse_unused_windows");
+       reuse_unused_windows = settings_get_bool("reuse_unused_windows");
 
        clear_waiting = TRUE;
        window = NULL;
 
        clear_waiting = TRUE;
        window = NULL;
@@ -257,11 +257,16 @@ void window_item_create(WI_ITEM_REC *item, int automatic)
                WINDOW_REC *rec = tmp->data;
 
                 /* is item bound to this window? */
                WINDOW_REC *rec = tmp->data;
 
                 /* is item bound to this window? */
-               if (item->server != NULL &&
-                   window_bind_find(rec, item->server->tag, item->name)) {
-                       window = rec;
-                       clear_waiting = FALSE;
-                       break;
+               if (item->server != NULL) {
+                       bind = window_bind_find(rec, item->server->tag,
+                                               item->name);
+                       if (bind != NULL) {
+                                if (!bind->sticky)
+                                       window_bind_destroy(rec, bind);
+                               window = rec;
+                               clear_waiting = FALSE;
+                               break;
+                       }
                }
 
                /* use this window IF:
                }
 
                /* use this window IF:
@@ -290,6 +295,10 @@ void window_item_create(WI_ITEM_REC *item, int automatic)
 
        if (window == NULL) {
                /* create new window to use */
 
        if (window == NULL) {
                /* create new window to use */
+               if (settings_get_bool("autocreate_split_windows")) {
+                       signal_emit("gui window create override", 1,
+                                   GINT_TO_POINTER(0));
+               }
                window = window_create(item, automatic);
        } else {
                /* use existing window */
                window = window_create(item, automatic);
        } else {
                /* use existing window */
@@ -316,6 +325,8 @@ void window_items_init(void)
 {
        settings_add_bool("lookandfeel", "reuse_unused_windows", FALSE);
        settings_add_bool("lookandfeel", "autocreate_windows", TRUE);
 {
        settings_add_bool("lookandfeel", "reuse_unused_windows", FALSE);
        settings_add_bool("lookandfeel", "autocreate_windows", TRUE);
+       settings_add_bool("lookandfeel", "autocreate_split_windows", FALSE);
+       settings_add_bool("lookandfeel", "autofocus_new_items", TRUE);
 
        signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
 }
 
        signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
 }
index 814127fbe7de55ca675fd3116b46ace17c774d60..56ebcb89e2d3be74560f7a02939a2461fe723e40 100644 (file)
 #include "fe-windows.h"
 #include "window-items.h"
 
 #include "fe-windows.h"
 #include "window-items.h"
 
-static void sig_window_restore_item(WINDOW_REC *window, const char *type,
+static WINDOW_REC *restore_win;
+
+static void signal_query_created_curwin(QUERY_REC *query)
+{
+       g_return_if_fail(IS_QUERY(query));
+
+       window_item_add(restore_win, (WI_ITEM_REC *) query, TRUE);
+}
+
+static void sig_layout_restore_item(WINDOW_REC *window, const char *type,
                                    CONFIG_NODE *node)
 {
        char *name, *tag, *chat_type;
                                    CONFIG_NODE *node)
 {
        char *name, *tag, *chat_type;
@@ -53,7 +62,14 @@ static void sig_window_restore_item(WINDOW_REC *window, const char *type,
                 rec->sticky = TRUE;
        } else if (g_strcasecmp(type, "QUERY") == 0 && chat_type != NULL) {
                /* create query immediately */
                 rec->sticky = TRUE;
        } else if (g_strcasecmp(type, "QUERY") == 0 && chat_type != NULL) {
                /* create query immediately */
+               signal_add("query created",
+                          (SIGNAL_FUNC) signal_query_created_curwin);
+
+                restore_win = window;
                chat_protocol_find(chat_type)->query_create(tag, name, TRUE);
                chat_protocol_find(chat_type)->query_create(tag, name, TRUE);
+
+               signal_remove("query created",
+                             (SIGNAL_FUNC) signal_query_created_curwin);
        }
 }
 
        }
 }
 
@@ -65,18 +81,24 @@ static void window_add_items(WINDOW_REC *window, CONFIG_NODE *node)
        if (node == NULL)
                return;
 
        if (node == NULL)
                return;
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                CONFIG_NODE *node = tmp->data;
 
                type = config_node_get_str(node, "type", NULL);
                if (type != NULL) {
                CONFIG_NODE *node = tmp->data;
 
                type = config_node_get_str(node, "type", NULL);
                if (type != NULL) {
-                       signal_emit("window restore item", 3,
+                       signal_emit("layout restore item", 3,
                                    window, type, node);
                }
        }
 }
 
 void windows_layout_restore(void)
                                    window, type, node);
                }
        }
 }
 
 void windows_layout_restore(void)
+{
+       signal_emit("layout restore", 0);
+}
+
+static void sig_layout_restore(void)
 {
        WINDOW_REC *window;
        CONFIG_NODE *node;
 {
        WINDOW_REC *window;
        CONFIG_NODE *node;
@@ -85,13 +107,18 @@ void windows_layout_restore(void)
        node = iconfig_node_traverse("windows", FALSE);
        if (node == NULL) return;
 
        node = iconfig_node_traverse("windows", FALSE);
        if (node == NULL) return;
 
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
                CONFIG_NODE *node = tmp->data;
 
                CONFIG_NODE *node = tmp->data;
 
-               window = window_create(NULL, TRUE);
+               window = window_find_refnum(atoi(node->key));
+               if (window == NULL)
+                       window = window_create(NULL, TRUE);
+
                window_set_refnum(window, atoi(node->key));
                 window->sticky_refnum = config_node_get_bool(node, "sticky_refnum", FALSE);
                window_set_name(window, config_node_get_str(node, "name", NULL));
                window_set_refnum(window, atoi(node->key));
                 window->sticky_refnum = config_node_get_bool(node, "sticky_refnum", FALSE);
                window_set_name(window, config_node_get_str(node, "name", NULL));
+               window_set_history(window, config_node_get_str(node, "history_name", NULL));
                window_set_level(window, level2bits(config_node_get_str(node, "level", "")));
 
                window->servertag = g_strdup(config_node_get_str(node, "servertag", NULL));
                window_set_level(window, level2bits(config_node_get_str(node, "level", "")));
 
                window->servertag = g_strdup(config_node_get_str(node, "servertag", NULL));
@@ -100,10 +127,8 @@ void windows_layout_restore(void)
                        window->theme = theme_load(window->theme_name);
 
                window_add_items(window, config_node_section(node, "items", -1));
                        window->theme = theme_load(window->theme_name);
 
                window_add_items(window, config_node_section(node, "items", -1));
-               signal_emit("window restore", 2, window, node);
+               signal_emit("layout restore window", 2, window, node);
        }
        }
-
-       signal_emit("windows restored", 0);
 }
 
 static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node)
 }
 
 static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node)
@@ -148,6 +173,10 @@ static void window_save(WINDOW_REC *window, CONFIG_NODE *node)
 
        if (window->name != NULL)
                iconfig_node_set_str(node, "name", window->name);
 
        if (window->name != NULL)
                iconfig_node_set_str(node, "name", window->name);
+
+       if (window->history_name != NULL)
+               iconfig_node_set_str(node, "history_name", window->history_name);
+
        if (window->servertag != NULL)
                iconfig_node_set_str(node, "servertag", window->servertag);
        if (window->level != 0) {
        if (window->servertag != NULL)
                iconfig_node_set_str(node, "servertag", window->servertag);
        if (window->level != 0) {
@@ -161,7 +190,7 @@ static void window_save(WINDOW_REC *window, CONFIG_NODE *node)
        if (window->items != NULL)
                window_save_items(window, node);
 
        if (window->items != NULL)
                window_save_items(window, node);
 
-       signal_emit("window save", 2, window, node);
+       signal_emit("layout save window", 2, window, node);
 }
 
 void windows_layout_save(void)
 }
 
 void windows_layout_save(void)
@@ -172,7 +201,7 @@ void windows_layout_save(void)
        node = iconfig_node_traverse("windows", TRUE);
 
        g_slist_foreach(windows, (GFunc) window_save, node);
        node = iconfig_node_traverse("windows", TRUE);
 
        g_slist_foreach(windows, (GFunc) window_save, node);
-       signal_emit("windows saved", 0);
+       signal_emit("layout save", 0);
 
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_WINDOWS_LAYOUT_SAVED);
 
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_WINDOWS_LAYOUT_SAVED);
@@ -181,16 +210,20 @@ void windows_layout_save(void)
 void windows_layout_reset(void)
 {
        iconfig_set_str(NULL, "windows", NULL);
 void windows_layout_reset(void)
 {
        iconfig_set_str(NULL, "windows", NULL);
+       signal_emit("layout reset", 0);
+
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_WINDOWS_LAYOUT_RESET);
 }
 
 void windows_layout_init(void)
 {
        printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                    TXT_WINDOWS_LAYOUT_RESET);
 }
 
 void windows_layout_init(void)
 {
-       signal_add("window restore item", (SIGNAL_FUNC) sig_window_restore_item);
+       signal_add("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item);
+       signal_add("layout restore", (SIGNAL_FUNC) sig_layout_restore);
 }
 
 void windows_layout_deinit(void)
 {
 }
 
 void windows_layout_deinit(void)
 {
-       signal_remove("window restore item", (SIGNAL_FUNC) sig_window_restore_item);
+       signal_remove("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item);
+       signal_remove("layout restore", (SIGNAL_FUNC) sig_layout_restore);
 }
 }
diff --git a/apps/irssi/src/fe-text/.cvsignore b/apps/irssi/src/fe-text/.cvsignore
new file mode 100644 (file)
index 0000000..6974ee8
--- /dev/null
@@ -0,0 +1,9 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+irssi
index 326ec232bee84477b33e74d0c13c2c1fd2a1fff6..6407d8611aa92094059f4d1cc8fc1a7b3ba60f3e 100644 (file)
@@ -2,27 +2,48 @@ bin_PROGRAMS = silc
 
 include $(top_srcdir)/Makefile.defines.in
 
 
 include $(top_srcdir)/Makefile.defines.in
 
-ADD_INCLUDES = \
-       $(GLIB_CFLAGS) \
+INCLUDES = \
        -I$(top_srcdir)/src \
        -I$(top_srcdir)/src/core/ \
        -I$(top_srcdir)/src \
        -I$(top_srcdir)/src/core/ \
-       -I$(top_srcdir)/src/silc/core/ \
        -I$(top_srcdir)/src/fe-common/core/ \
        -I$(top_srcdir)/src/fe-common/silc/ \
        -I$(top_srcdir)/src/fe-common/core/ \
        -I$(top_srcdir)/src/fe-common/silc/ \
+       $(GLIB_CFLAGS) \
        $(CURSES_INCLUDEDIR) \
        -DLOCALEDIR=\""$(datadir)/locale"\"
 
        $(CURSES_INCLUDEDIR) \
        -DLOCALEDIR=\""$(datadir)/locale"\"
 
-silc_DEPENDENCIES = @COMMON_LIBS@
+silc_DEPENDENCIES =            \
+       @COMMON_LIBS@           \
+       @PERL_LINK_LIBS@        \
+       @PERL_FE_LINK_LIBS@
 
 LIBS = $(SILC_COMMON_LIBS)
 silc_LDADD = \
        @COMMON_LIBS@ \
        @PERL_LINK_LIBS@ \
        @PERL_FE_LINK_LIBS@ \
 
 LIBS = $(SILC_COMMON_LIBS)
 silc_LDADD = \
        @COMMON_LIBS@ \
        @PERL_LINK_LIBS@ \
        @PERL_FE_LINK_LIBS@ \
-       $(PROG_LIBS) \
-        $(CURSES_LIBS) \
+       @PERL_LINK_FLAGS@ \
+       @PROG_LIBS@     \
        -L../../../lib -lsilcclient
 
        -L../../../lib -lsilcclient
 
+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
 
 silc_SOURCES = \
         gui-entry.c \
 
 silc_SOURCES = \
         gui-entry.c \
@@ -33,13 +54,19 @@ silc_SOURCES = \
        lastlog.c \
         mainwindows.c \
         mainwindow-activity.c \
        lastlog.c \
         mainwindows.c \
         mainwindow-activity.c \
-        mainwindows-save.c \
-        screen.c \
+        mainwindows-layout.c \
         statusbar.c \
         statusbar.c \
+        statusbar-config.c \
         statusbar-items.c \
         statusbar-items.c \
+        term.c \
+       term-dummy.c \
+       $(use_tparm_sources) \
+       $(use_term_sources) \
         textbuffer.c \
         textbuffer-commands.c \
         textbuffer.c \
         textbuffer-commands.c \
+        textbuffer-reformat.c \
         textbuffer-view.c \
         textbuffer-view.c \
+       utf8.c \
         silc.c \
        module-formats.c
 
         silc.c \
        module-formats.c
 
@@ -50,8 +77,17 @@ noinst_HEADERS = \
         gui-windows.h \
         mainwindows.h \
         statusbar.h \
         gui-windows.h \
         mainwindows.h \
         statusbar.h \
-        screen.h \
+        statusbar-config.h \
+       term.h \
+       terminfo-core.h \
         textbuffer.h \
         textbuffer-view.h \
         textbuffer.h \
         textbuffer-view.h \
+        textbuffer-reformat.h \
+       utf8.h \
        module.h \
        module-formats.h
        module.h \
        module-formats.h
+
+EXTRA_DIST = \
+       $(tparm_sources) \
+       $(terminfo_sources) \
+       $(curses_sources)
index 7544539a304c4059ef586f1d263bfe2e7aee4764..f70bf0dfe04e22279198230f460aed0f90edbdc8 100644 (file)
 */
 
 #include "module.h"
 */
 
 #include "module.h"
+#include "utf8.h"
 #include "formats.h"
 
 #include "formats.h"
 
+#include "gui-entry.h"
 #include "gui-printtext.h"
 #include "gui-printtext.h"
-#include "screen.h"
+#include "term.h"
 
 
-static GString *entry;
-static int promptlen, permanent_prompt, pos, scrstart, scrpos;
-static int prompt_hidden;
-static char *prompt;
+GUI_ENTRY_REC *active_entry;
 
 
-static void entry_screenpos(void)
+GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
 {
 {
-       if (pos-scrstart < COLS-2-promptlen && pos-scrstart > 0) {
-               scrpos = pos-scrstart;
-               return;
-       }
+       GUI_ENTRY_REC *rec;
+
+       rec = g_new0(GUI_ENTRY_REC, 1);
+       rec->xpos = xpos;
+       rec->ypos = ypos;
+        rec->width = width;
+       rec->text = g_string_new(NULL);
+        rec->utf8 = utf8;
+       return rec;
+}
+
+void gui_entry_destroy(GUI_ENTRY_REC *entry)
+{
+        g_return_if_fail(entry != NULL);
 
 
-       if (pos < COLS-1-promptlen) {
-               scrstart = 0;
-               scrpos = pos;
+       if (active_entry == entry)
+               gui_entry_set_active(NULL);
+
+       g_free_not_null(entry->prompt);
+       g_string_free(entry->text, TRUE);
+        g_free(entry);
+}
+
+/* Fixes the cursor position in screen */
+static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
+{
+       int old_scrstart;
+
+        old_scrstart = entry->scrstart;
+       if (entry->pos - entry->scrstart < entry->width-2 - entry->promptlen &&
+           entry->pos - entry->scrstart > 0) {
+               entry->scrpos = entry->pos - entry->scrstart;
+       } else if (entry->pos < entry->width-1 - entry->promptlen) {
+               entry->scrstart = 0;
+               entry->scrpos = entry->pos;
        } else {
        } else {
-               scrpos = (COLS-promptlen)*2/3;
-               scrstart = pos-scrpos;
+               entry->scrpos = (entry->width - entry->promptlen)*2/3;
+               entry->scrstart = entry->pos - entry->scrpos;
        }
        }
+
+       if (old_scrstart != entry->scrstart)
+                entry->redraw_needed_from = 0;
 }
 
 }
 
-static void entry_update(void)
+static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
 {
 {
-       char *p;
-       int n, len;
-
-       len = entry->len-scrstart > COLS-1-promptlen ?
-               COLS-1-promptlen : entry->len-scrstart;
-
-       set_color(stdscr, 0);
-       move(LINES-1, promptlen);
-
-       for (p = entry->str+scrstart, n = 0; n < len; n++, p++) {
-               if (prompt_hidden)
-                        addch(' ');
-               else if ((unsigned char) *p >= 32)
-                       addch((unsigned char) *p);
-               else {
-                       set_color(stdscr, ATTR_REVERSE);
-                       addch(*p+'A'-1);
-                       set_color(stdscr, 0);
+       const unsigned char *p, *end;
+       int xpos, end_xpos;
+
+       if (entry->utf8) {
+               /* FIXME: a stupid kludge to make the chars output correctly */
+               pos = 0;
+       }
+
+        xpos = entry->xpos + entry->promptlen + pos;
+        end_xpos = entry->xpos + entry->width;
+       if (xpos > end_xpos)
+                return;
+
+       term_set_color(root_window, ATTR_RESET);
+       term_move(root_window, xpos, entry->ypos);
+
+       p = (unsigned char *) (entry->scrstart + pos >= entry->text->len ? "" :
+                              entry->text->str + entry->scrstart + pos);
+       for (; *p != '\0' && xpos < end_xpos; p++, xpos++) {
+                end = p;
+               if (entry->utf8)
+                       get_utf8_char(&end);
+
+               if (entry->hidden)
+                        term_addch(root_window, ' ');
+               else if (*p >= 32 && (end != p || (*p & 127) >= 32)) {
+                        for (; p < end; p++)
+                               term_addch(root_window, *p);
+                       term_addch(root_window, *p);
+               } else {
+                       term_set_color(root_window, ATTR_RESET|ATTR_REVERSE);
+                       term_addch(root_window, *p+'A'-1);
+                       term_set_color(root_window, ATTR_RESET);
                }
        }
                }
        }
-       clrtoeol();
 
 
-       move_cursor(LINES-1, scrpos+promptlen);
-       screen_refresh(NULL);
+        /* clear the rest of the input line */
+        if (end_xpos == term_width)
+               term_clrtoeol(root_window);
+       else {
+               while (xpos < end_xpos) {
+                        term_addch(root_window, ' ');
+                        xpos++;
+               }
+       }
 }
 
 }
 
-void gui_entry_set_prompt(const char *str)
+static void gui_entry_draw(GUI_ENTRY_REC *entry)
 {
 {
-       if (str != NULL) {
-               if (permanent_prompt) return;
+       if (entry->redraw_needed_from >= 0) {
+               gui_entry_draw_from(entry, entry->redraw_needed_from);
+                entry->redraw_needed_from = -1;
+       }
+
+       term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen,
+                        entry->ypos);
+       term_refresh(NULL);
+}
 
 
-               g_free_not_null(prompt);
-               prompt = g_strdup(str);
-               promptlen = format_get_length(prompt);
+static void gui_entry_redraw_from(GUI_ENTRY_REC *entry, int pos)
+{
+       pos -= entry->scrstart;
+       if (pos < 0) pos = 0;
+
+       if (entry->redraw_needed_from == -1 ||
+           entry->redraw_needed_from > pos)
+               entry->redraw_needed_from = pos;
+}
+
+void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width)
+{
+       int old_width;
+
+        g_return_if_fail(entry != NULL);
+
+       if (entry->xpos != xpos || entry->ypos != ypos) {
+                /* position in screen changed - needs a full redraw */
+               entry->xpos = xpos;
+               entry->ypos = ypos;
+               entry->width = width;
+               gui_entry_redraw(entry);
+                return;
+       }
+
+       if (entry->width == width)
+                return; /* no changes */
+
+       if (width > entry->width) {
+                /* input line grew - need to draw text at the end */
+                old_width = width;
+               entry->width = width;
+               gui_entry_redraw_from(entry, old_width);
+       } else {
+               /* input line shrinked - make sure the cursor
+                  is inside the input line */
+               entry->width = width;
+               if (entry->pos - entry->scrstart >
+                   entry->width-2 - entry->promptlen) {
+                       gui_entry_fix_cursor(entry);
+               }
        }
 
        }
 
-        if (prompt != NULL)
-               gui_printtext(0, LINES-1, prompt);
+       gui_entry_draw(entry);
+}
 
 
-       entry_screenpos();
-       entry_update();
+void gui_entry_set_active(GUI_ENTRY_REC *entry)
+{
+       active_entry = entry;
+
+       if (entry != NULL) {
+               term_move_cursor(entry->xpos + entry->scrpos +
+                                entry->promptlen, entry->ypos);
+               term_refresh(NULL);
+       }
 }
 
 }
 
-void gui_entry_set_perm_prompt(const char *str)
+void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
 {
 {
-       g_return_if_fail(str != NULL);
+       int oldlen;
+
+        g_return_if_fail(entry != NULL);
 
 
-       g_free_not_null(prompt);
-       prompt = g_strdup(str);
-       promptlen = format_get_length(prompt);
+        oldlen = entry->promptlen;
+       if (str != NULL) {
+               g_free_not_null(entry->prompt);
+               entry->prompt = g_strdup(str);
+               entry->promptlen = format_get_length(str);
+       }
+
+        if (entry->prompt != NULL)
+               gui_printtext(entry->xpos, entry->ypos, entry->prompt);
 
 
-       permanent_prompt = TRUE;
-       gui_entry_set_prompt(NULL);
+       if (entry->promptlen != oldlen) {
+               gui_entry_fix_cursor(entry);
+               gui_entry_draw(entry);
+       }
 }
 
 }
 
-void gui_entry_set_hidden(int hidden)
+void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden)
 {
 {
-        prompt_hidden = hidden;
+        g_return_if_fail(entry != NULL);
+
+        entry->hidden = hidden;
 }
 
 }
 
-void gui_entry_remove_perm_prompt(void)
+void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8)
 {
 {
-        permanent_prompt = FALSE;
+        g_return_if_fail(entry != NULL);
+
+        entry->utf8 = utf8;
 }
 
 }
 
-void gui_entry_set_text(const char *str)
+void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str)
 {
 {
+        g_return_if_fail(entry != NULL);
        g_return_if_fail(str != NULL);
 
        g_return_if_fail(str != NULL);
 
-       g_string_assign(entry, str);
-       pos = entry->len;
+       g_string_assign(entry->text, str);
+       entry->pos = entry->text->len;
 
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, 0);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-char *gui_entry_get_text(void)
+char *gui_entry_get_text(GUI_ENTRY_REC *entry)
 {
 {
-       return entry->str;
+       g_return_val_if_fail(entry != NULL, NULL);
+
+       return entry->text->str;
 }
 
 }
 
-void gui_entry_insert_text(const char *str)
+void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
 {
 {
+        g_return_if_fail(entry != NULL);
        g_return_if_fail(str != NULL);
 
        g_return_if_fail(str != NULL);
 
-       g_string_insert(entry, pos, str);
-       pos += strlen(str);
+        gui_entry_redraw_from(entry, entry->pos);
+       g_string_insert(entry->text, entry->pos, str);
+       entry->pos += strlen(str);
 
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-void gui_entry_insert_char(char chr)
+void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr)
 {
 {
-       g_string_insert_c(entry, pos, chr);
-       pos++;
+        g_return_if_fail(entry != NULL);
+
+       if (chr == 0 || chr == 13 || chr == 10)
+               return; /* never insert NUL, CR or LF characters */
 
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, entry->pos);
+       g_string_insert_c(entry->text, entry->pos, chr);
+       entry->pos++;
+
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-void gui_entry_erase(int size)
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size)
 {
 {
-       if (pos < size) return;
+        g_return_if_fail(entry != NULL);
+
+       if (entry->pos < size)
+               return;
 
 #ifdef WANT_BIG5
 
 #ifdef WANT_BIG5
-       if (is_big5(entry->str[pos-2], entry->str[pos-1]))
+       if (is_big5(entry->text->str[entry->pos-2],
+                   entry->text->str[entry->pos-1]))
                size++;
                size++;
-#endif WANT_BIG5
+#endif
 
 
-       pos -= size;
-       g_string_erase(entrypos, size);
+       entry->pos -= size;
+       g_string_erase(entry->text, entry->pos, size);
 
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_redraw_from(entry, entry->pos);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-void gui_entry_erase_word(void)
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
 {
        int to;
 {
        int to;
-       
-       if (pos == 0) return;
-
-       to = pos - 1;
 
 
-       while (entry->str[to] == ' ' && to > 0)
-               to--;
+        g_return_if_fail(entry != NULL);
+       if (entry->pos == 0)
+               return;
 
 
-       while (entry->str[to] != ' ' && to > 0)
-               to--;
+       to = entry->pos - 1;
 
 
-       if (entry->str[to] == ' ' && to > 0) 
-               to++;
+       if (to_space) {
+               while (entry->text->str[to] == ' ' && to > 0)
+                       to--;
+               while (entry->text->str[to] != ' ' && to > 0)
+                       to--;
+       } else {
+               while (!i_isalnum(entry->text->str[to]) && to > 0)
+                       to--;
+               while (i_isalnum(entry->text->str[to]) && to > 0)
+                       to--;
+       }
+       if (to > 0) to++;
 
 
-       g_string_erase(entry, to, pos - to);
-       pos = to;
+       g_string_erase(entry->text, to, entry->pos - to);
+       entry->pos = to;
 
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, entry->pos);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-void gui_entry_erase_next_word(void)
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
 {
 {
-       int to = pos;
-       
-       if (pos == entry->len) return;
+       int to;
 
 
-       while (entry->str[to] == ' ' && to < entry->len)
-               to++;
+        g_return_if_fail(entry != NULL);
+       if (entry->pos == entry->text->len)
+               return;
 
 
-       while (entry->str[to] != ' ' && to < entry->len)
-               to++;
+        to = entry->pos;
+       if (to_space) {
+               while (entry->text->str[to] == ' ' && to < entry->text->len)
+                       to++;
+               while (entry->text->str[to] != ' ' && to < entry->text->len)
+                       to++;
+       } else {
+               while (!i_isalnum(entry->text->str[to]) && to < entry->text->len)
+                       to++;
+               while (i_isalnum(entry->text->str[to]) && to < entry->text->len)
+                       to++;
+       }
 
 
-       g_string_erase(entry, pos, to - pos);
+       g_string_erase(entry->text, entry->pos, to - entry->pos);
 
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, entry->pos);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-int gui_entry_get_pos(void)
+int gui_entry_get_pos(GUI_ENTRY_REC *entry)
 {
 {
-       return pos;
+        g_return_val_if_fail(entry != NULL, 0);
+
+       return entry->pos;
 }
 
 }
 
-void gui_entry_set_pos(int p)
+void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
 {
 {
-       if (p >= 0 && p <= entry->len)
-               pos = p;
+        g_return_if_fail(entry != NULL);
+
+       if (pos >= 0 && pos <= entry->text->len)
+               entry->pos = pos;
 
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-void gui_entry_move_pos(int p)
+void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
 {
 {
-#ifdef WANT_BIG5
-       if (p > 0 && is_big5 (entry->str[pos], entry->str[pos+1]))
-               p++;
-       else if (p < 0 && is_big5 (entry->str[pos-1], entry->str[pos]))
-               p--;
-#endif WANT_BIG5
+        g_return_if_fail(entry != NULL);
 
 
-       if (pos+p >= 0 && pos+p <= entry->len)
-               pos += p;
-
-       entry_screenpos();
-       entry_update();
+#ifdef WANT_BIG5
+       if (pos > 0 && is_big5(entry->text->str[entry->pos],
+                              entry->text->str[entry->pos+1]))
+               pos++;
+       else if (pos < 0 && is_big5(entry->text->str[entry->pos-1],
+                                   entry->text->str[entry->pos]))
+               pos--;
+#endif
+
+       if (entry->pos+pos >= 0 && entry->pos+pos <= entry->text->len)
+               entry->pos += pos;
+
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-static void gui_entry_move_words_left(int count)
+static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_space)
 {
 {
-       if (pos == 0) return;
+       int pos;
 
 
+       pos = entry->pos;
        while (count > 0 && pos > 0) {
        while (count > 0 && pos > 0) {
-               while (pos > 0 && entry->str[pos-1] == ' ')
-                       pos--;
-               while (pos > 0 && entry->str[pos-1] != ' ')
-                       pos--;
+               if (to_space) {
+                       while (pos > 0 && entry->text->str[pos-1] == ' ')
+                               pos--;
+                       while (pos > 0 && entry->text->str[pos-1] != ' ')
+                               pos--;
+               } else {
+                       while (pos > 0 && !i_isalnum(entry->text->str[pos-1]))
+                               pos--;
+                       while (pos > 0 &&  i_isalnum(entry->text->str[pos-1]))
+                               pos--;
+               }
                count--;
        }
                count--;
        }
+
+        entry->pos = pos;
 }
 
 }
 
-static void gui_entry_move_words_right(int count)
+static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_space)
 {
 {
-       if (pos == entry->len) return;
-
-       while (count > 0 && pos < entry->len) {
-               while (pos < entry->len && entry->str[pos] != ' ')
-                       pos++;
-               while (pos < entry->len && entry->str[pos] == ' ')
-                       pos++;
+       int pos;
+
+       pos = entry->pos;
+       while (count > 0 && pos < entry->text->len) {
+               if (to_space) {
+                       while (pos < entry->text->len && entry->text->str[pos] == ' ')
+                               pos++;
+                       while (pos < entry->text->len && entry->text->str[pos] != ' ')
+                               pos++;
+               } else {
+                       while (pos < entry->text->len && !i_isalnum(entry->text->str[pos]))
+                               pos++;
+                       while (pos < entry->text->len &&  i_isalnum(entry->text->str[pos]))
+                               pos++;
+               }
                count--;
        }
                count--;
        }
+
+        entry->pos = pos;
 }
 
 }
 
-void gui_entry_move_words(int count)
+void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space)
 {
 {
+        g_return_if_fail(entry != NULL);
+
        if (count < 0)
        if (count < 0)
-               gui_entry_move_words_left(-count);
+               gui_entry_move_words_left(entry, -count, to_space);
        else if (count > 0)
        else if (count > 0)
-               gui_entry_move_words_right(count);
+               gui_entry_move_words_right(entry, count, to_space);
 
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
 }
 
-void gui_entry_redraw(void)
+void gui_entry_redraw(GUI_ENTRY_REC *entry)
 {
 {
-       gui_entry_set_prompt(NULL);
+        g_return_if_fail(entry != NULL);
 
 
-       entry_screenpos();
-       entry_update();
-}
-
-void gui_entry_init(void)
-{
-       entry = g_string_new(NULL);
-
-       pos = scrpos = 0;
-       prompt = NULL; promptlen = 0;
-       permanent_prompt = FALSE;
-        prompt_hidden = FALSE;
-}
-
-void gui_entry_deinit(void)
-{
-       if (prompt != NULL) g_free(prompt);
-       g_string_free(entry, TRUE);
+       gui_entry_set_prompt(entry, NULL);
+        gui_entry_redraw_from(entry, 0);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 }
index ff8f3ef5b42d159e6fb2f409eb66976e79879209..364f92da3a6700042fb2abb778410daa529277b1 100644 (file)
@@ -1,31 +1,46 @@
 #ifndef __GUI_ENTRY_H
 #define __GUI_ENTRY_H
 
 #ifndef __GUI_ENTRY_H
 #define __GUI_ENTRY_H
 
-void gui_entry_set_prompt(const char *str);
+typedef struct {
+       GString *text;
+       int xpos, ypos, width; /* entry position in screen */
+       int pos, scrstart, scrpos; /* cursor position */
+        int hidden; /* print the chars as spaces in input line (useful for passwords) */
 
 
-/* permanent prompt can't be overwritten with gui_entry_set_prompt() */
-void gui_entry_set_perm_prompt(const char *str);
-void gui_entry_remove_perm_prompt(void);
-void gui_entry_set_hidden(int hidden);
+       int promptlen;
+       char *prompt;
 
 
-void gui_entry_set_text(const char *str);
-char *gui_entry_get_text(void);
+       int redraw_needed_from;
+       unsigned int utf8:1;
+} GUI_ENTRY_REC;
 
 
-void gui_entry_insert_text(const char *str);
-void gui_entry_insert_char(char chr);
+extern GUI_ENTRY_REC *active_entry;
 
 
-void gui_entry_erase(int size);
-void gui_entry_erase_word(void);
-void gui_entry_erase_next_word(void);
+GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8);
+void gui_entry_destroy(GUI_ENTRY_REC *entry);
 
 
-int gui_entry_get_pos(void);
-void gui_entry_set_pos(int pos);
-void gui_entry_move_pos(int pos);
-void gui_entry_move_words(int count);
+void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width);
+void gui_entry_set_active(GUI_ENTRY_REC *entry);
 
 
-void gui_entry_redraw(void);
+void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str);
+void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden);
+void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8);
 
 
-void gui_entry_init(void);
-void gui_entry_deinit(void);
+void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str);
+char *gui_entry_get_text(GUI_ENTRY_REC *entry);
+
+void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str);
+void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr);
+
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size);
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space);
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space);
+
+int gui_entry_get_pos(GUI_ENTRY_REC *entry);
+void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos);
+void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos);
+void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space);
+
+void gui_entry_redraw(GUI_ENTRY_REC *entry);
 
 #endif
 
 #endif
index c95ac621b40ebfbc0369109b9f12b5ffb4d9e318..b07bf129cacd44bf0d4ba3406ed8312c897db2e5 100644 (file)
@@ -37,7 +37,7 @@ static char *expando_idletime(SERVER_REC *server, void *item, int *free_ret)
 /* current contents of the input line */
 static char *expando_inputline(SERVER_REC *server, void *item, int *free_ret)
 {
 /* current contents of the input line */
 static char *expando_inputline(SERVER_REC *server, void *item, int *free_ret)
 {
-       return gui_entry_get_text();
+       return gui_entry_get_text(active_entry);
 }
 
 /* value of cutbuffer */
 }
 
 /* value of cutbuffer */
index 5faae0fa3b53b2d025a726dae523ff5124081aaa..3bd1de3696557b17dd0f6d0784a767a0edbab2bb 100644 (file)
 #include "formats.h"
 #include "printtext.h"
 
 #include "formats.h"
 #include "printtext.h"
 
-#include "screen.h"
+#include "term.h"
+#include "gui-printtext.h"
 #include "gui-windows.h"
 
 #include "gui-windows.h"
 
-int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 14, 10, 3, 11, 9, 13, 8, 7 };
+int mirc_colors[] = { 15, 0, 1, 2, 12, 4, 5, 6, 14, 10, 3, 11, 9, 13, 8, 7 };
 static int scrollback_lines, scrollback_hours, scrollback_burst_remove;
 
 static int scrollback_lines, scrollback_hours, scrollback_burst_remove;
 
-static int scrollback_save_formats;
-static GString *format;
-
-static int last_color, last_flags;
+static int last_fg, last_bg, last_flags;
 static int next_xpos, next_ypos;
 
 static int next_xpos, next_ypos;
 
+static GHashTable *indent_functions;
+static INDENT_FUNC default_indent_func;
+
+void gui_register_indent_func(const char *name, INDENT_FUNC func)
+{
+       gpointer key, value;
+        GSList *list;
+
+       if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
+                list = value;
+               g_hash_table_remove(indent_functions, key);
+       } else {
+               key = g_strdup(name);
+                list = NULL;
+       }
+
+       list = g_slist_append(list, (void *) func);
+       g_hash_table_insert(indent_functions, key, list);
+}
+
+void gui_unregister_indent_func(const char *name, INDENT_FUNC func)
+{
+       gpointer key, value;
+        GSList *list;
+
+       if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
+               list = value;
+
+               list = g_slist_remove(list, (void *) func);
+               g_hash_table_remove(indent_functions, key);
+               if (list == NULL)
+                       g_free(key);
+                else
+                       g_hash_table_insert(indent_functions, key, list);
+       }
+
+       if (default_indent_func == func)
+               gui_set_default_indent(NULL);
+
+       textbuffer_views_unregister_indent_func(func);
+}
+
+void gui_set_default_indent(const char *name)
+{
+       GSList *list;
+
+       list = name == NULL ? NULL :
+               g_hash_table_lookup(indent_functions, name);
+       default_indent_func = list == NULL ? NULL : list->data;
+        gui_windows_reset_settings();
+}
+
+INDENT_FUNC get_default_indent_func(void)
+{
+        return default_indent_func;
+}
+
 void gui_printtext(int xpos, int ypos, const char *str)
 {
        next_xpos = xpos;
 void gui_printtext(int xpos, int ypos, const char *str)
 {
        next_xpos = xpos;
@@ -47,6 +102,18 @@ void gui_printtext(int xpos, int ypos, const char *str)
        next_xpos = next_ypos = -1;
 }
 
        next_xpos = next_ypos = -1;
 }
 
+void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str)
+{
+       GUI_WINDOW_REC *gui;
+
+       gui = WINDOW_GUI(dest->window);
+
+       gui->use_insert_after = TRUE;
+       gui->insert_after = prev;
+       format_send_to_gui(dest, str);
+       gui->use_insert_after = FALSE;
+}
+
 static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
 {
        LINE_REC *line;
 static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
 {
        LINE_REC *line;
@@ -57,9 +124,12 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
            scrollback_lines+scrollback_burst_remove) {
                 /* remove lines by line count */
                while (view->buffer->lines_count > scrollback_lines) {
            scrollback_lines+scrollback_burst_remove) {
                 /* remove lines by line count */
                while (view->buffer->lines_count > scrollback_lines) {
-                       line = view->buffer->lines->data;
-                       if (line->info.time >= old_time) {
-                               /* too new line, don't remove yet */
+                       line = view->buffer->first_line;
+                       if (line->info.time >= old_time ||
+                           scrollback_lines == 0) {
+                               /* too new line, don't remove yet - also
+                                  if scrollback_lines is 0, we want to check
+                                  only scrollback_hours setting. */
                                break;
                        }
                        textbuffer_view_remove_line(view, line);
                                break;
                        }
                        textbuffer_view_remove_line(view, line);
@@ -67,77 +137,85 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
        }
 }
 
        }
 }
 
-static void get_colors(int flags, int *fg, int *bg)
+static void get_colors(int flags, int *fg, int *bg, int *attr)
 {
 {
-       if (flags & PRINTFLAG_MIRC_COLOR) {
+       if (flags & GUI_PRINT_FLAG_MIRC_COLOR) {
                /* mirc colors - real range is 0..15, but after 16
                   colors wrap to 0, 1, ... */
                /* mirc colors - real range is 0..15, but after 16
                   colors wrap to 0, 1, ... */
-               *bg = *bg < 0 ? 0 : mirc_colors[*bg % 16];
-               if (*fg > 0) *fg = mirc_colors[*fg % 16];
-       } else {
-               /* default colors */
-               *bg = *bg < 0 || *bg > 15 ? 0 : *bg;
-                if (*fg > 8) *fg &= ~8;
+                if (*bg >= 0) *bg = mirc_colors[*bg % 16];
+               if (*fg >= 0) *fg = mirc_colors[*fg % 16];
        }
 
        }
 
-       if (*fg < 0 || *fg > 15) {
-               *fg = *bg == 0 ? current_theme->default_color :
-                       current_theme->default_real_color;
-       }
-
-       if (flags & PRINTFLAG_REVERSE) {
-               int tmp;
-
-               tmp = *fg; *fg = *bg; *bg = tmp;
-       }
+       if (*fg < 0 || *fg > 15)
+               *fg = current_theme->default_color;
+       if (*bg < 0 || *bg > 15)
+                *bg = -1;
 
 
-       if (*fg == 8) *fg |= ATTR_COLOR8;
-       if (flags & PRINTFLAG_BOLD) {
-               if (*fg == 0) *fg = current_theme->default_real_color;
-               *fg |= 8;
-       }
-       if (flags & PRINTFLAG_UNDERLINE) *fg |= ATTR_UNDERLINE;
-       if (flags & PRINTFLAG_BLINK) *bg |= 0x08;
+       *attr = 0;
+       if (flags & GUI_PRINT_FLAG_REVERSE) *attr |= ATTR_REVERSE;
+       if (flags & GUI_PRINT_FLAG_BOLD) *attr |= ATTR_BOLD;
+       if (flags & GUI_PRINT_FLAG_UNDERLINE) *attr |= ATTR_UNDERLINE;
+       if (flags & GUI_PRINT_FLAG_BLINK) *attr |= ATTR_BLINK;
 }
 
 static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
                            int fg, int bg, int flags)
 {
 }
 
 static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
                            int fg, int bg, int flags)
 {
-       unsigned char data[12];
-       int color, pos;
+       unsigned char data[20];
+       int pos;
 
 
-       /* color should never have last bit on or it would be treated as a
-          command! */
-       color = (fg & 0x0f) | ((bg & 0x07) << 4);
-       pos = 0;
+        /* get the fg & bg command chars */
+       fg = fg < 0 ? LINE_COLOR_DEFAULT : fg & 0x0f;
+       bg = LINE_COLOR_BG | (bg < 0 ? LINE_COLOR_DEFAULT : bg & 0x0f);
+       if (flags & GUI_PRINT_FLAG_BOLD)
+               fg |= LINE_COLOR_BOLD;
+       if (flags & GUI_PRINT_FLAG_BLINK)
+                bg |= LINE_COLOR_BLINK;
 
 
-       if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != last_color) ||
-           ((fg & ATTR_COLOR8) && (fg & 0xf0) != (last_color & 0xf0))) {
+       pos = 0;
+       if (fg != last_fg) {
+               last_fg = fg;
                data[pos++] = 0;
                data[pos++] = 0;
-               data[pos++] = color == 0 ? LINE_CMD_COLOR0 : color;
+               data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg;
        }
        }
-
-       if ((flags & PRINTFLAG_UNDERLINE) != (last_flags & PRINTFLAG_UNDERLINE)) {
+       if (bg != last_bg) {
+                last_bg = bg;
                data[pos++] = 0;
                data[pos++] = 0;
-               data[pos++] = LINE_CMD_UNDERLINE;
+               data[pos++] = bg;
        }
        }
-       if (fg & ATTR_COLOR8) {
+
+       if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (last_flags & GUI_PRINT_FLAG_UNDERLINE)) {
                data[pos++] = 0;
                data[pos++] = 0;
-               data[pos++] = LINE_CMD_COLOR8;
+               data[pos++] = LINE_CMD_UNDERLINE;
        }
        }
-       if (bg & 0x08) {
+       if ((flags & GUI_PRINT_FLAG_REVERSE) != (last_flags & GUI_PRINT_FLAG_REVERSE)) {
                data[pos++] = 0;
                data[pos++] = 0;
-               data[pos++] = LINE_CMD_BLINK;
+               data[pos++] = LINE_CMD_REVERSE;
        }
        }
-       if (flags & PRINTFLAG_INDENT) {
+       if (flags & GUI_PRINT_FLAG_INDENT) {
                data[pos++] = 0;
                data[pos++] = LINE_CMD_INDENT;
        }
 
                data[pos++] = 0;
                data[pos++] = LINE_CMD_INDENT;
        }
 
-       *line = textbuffer_insert(buffer, *line, data, pos, NULL);
+        if (pos > 0)
+               *line = textbuffer_insert(buffer, *line, data, pos, NULL);
 
        last_flags = flags;
 
        last_flags = flags;
-       last_color = fg | (bg << 4);
+}
+
+static void line_add_indent_func(TEXT_BUFFER_REC *buffer, LINE_REC **line,
+                                const char *function)
+{
+        GSList *list;
+        unsigned char data[1+sizeof(INDENT_FUNC)];
+
+        list = g_hash_table_lookup(indent_functions, function);
+       if (list != NULL) {
+               data[0] = LINE_CMD_INDENT_FUNC;
+               memcpy(data+1, list->data, sizeof(INDENT_FUNC));
+               *line = textbuffer_insert(buffer, *line,
+                                         data, sizeof(data), NULL);
+       }
 }
 
 static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line)
 }
 
 static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line)
@@ -152,22 +230,28 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
                               void *bgcolor, void *pflags,
                               char *str, void *level)
 {
                               void *bgcolor, void *pflags,
                               char *str, void *level)
 {
+        GUI_WINDOW_REC *gui;
         TEXT_BUFFER_VIEW_REC *view;
        LINE_REC *insert_after;
         LINE_INFO_REC lineinfo;
         TEXT_BUFFER_VIEW_REC *view;
        LINE_REC *insert_after;
         LINE_INFO_REC lineinfo;
-       int fg, bg, flags;
+       int fg, bg, flags, attr;
 
        flags = GPOINTER_TO_INT(pflags);
        fg = GPOINTER_TO_INT(fgcolor);
        bg = GPOINTER_TO_INT(bgcolor);
 
        flags = GPOINTER_TO_INT(pflags);
        fg = GPOINTER_TO_INT(fgcolor);
        bg = GPOINTER_TO_INT(bgcolor);
-       get_colors(flags, &fg, &bg);
+       get_colors(flags, &fg, &bg, &attr);
 
        if (window == NULL) {
                 g_return_if_fail(next_xpos != -1);
 
 
        if (window == NULL) {
                 g_return_if_fail(next_xpos != -1);
 
-               wmove(stdscr, next_ypos, next_xpos);
-               set_color(stdscr, fg | (bg << 4));
-                addstr(str);
+               attr |= fg > 0 ? fg : ATTR_RESETFG;
+               attr |= bg > 0 ? (bg << 4) : ATTR_RESETBG;
+               term_set_color(root_window, attr);
+
+               term_move(root_window, next_xpos, next_ypos);
+               if (flags & GUI_PRINT_FLAG_CLRTOEOL)
+                       term_clrtoeol(root_window);
+               term_addstr(root_window, str);
                next_xpos += strlen(str);
                 return;
        }
                next_xpos += strlen(str);
                 return;
        }
@@ -175,23 +259,33 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
        lineinfo.level = GPOINTER_TO_INT(level);
         lineinfo.time = time(NULL);
 
        lineinfo.level = GPOINTER_TO_INT(level);
         lineinfo.time = time(NULL);
 
-       view = WINDOW_GUI(window)->view;
-       insert_after = WINDOW_GUI(window)->use_insert_after ?
-               WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
+        gui = WINDOW_GUI(window);
+       view = gui->view;
+       insert_after = gui->use_insert_after ?
+               gui->insert_after : view->buffer->cur_line;
 
 
-       if (flags & PRINTFLAG_NEWLINE)
+       if (flags & GUI_PRINT_FLAG_NEWLINE)
                 view_add_eol(view, &insert_after);
        line_add_colors(view->buffer, &insert_after, fg, bg, flags);
                 view_add_eol(view, &insert_after);
        line_add_colors(view->buffer, &insert_after, fg, bg, flags);
-       textbuffer_insert(view->buffer, insert_after,
-                         str, strlen(str), &lineinfo);
+
+       if (flags & GUI_PRINT_FLAG_INDENT_FUNC) {
+               /* specify the indentation function */
+                line_add_indent_func(view->buffer, &insert_after, str);
+       } else {
+               insert_after = textbuffer_insert(view->buffer, insert_after,
+                                                (unsigned char *) str,
+                                                strlen(str), &lineinfo);
+       }
+       if (gui->use_insert_after)
+                gui->insert_after = insert_after;
 }
 
 }
 
-static void sig_printtext_finished(WINDOW_REC *window)
+static void sig_gui_printtext_finished(WINDOW_REC *window)
 {
        TEXT_BUFFER_VIEW_REC *view;
        LINE_REC *insert_after;
 
 {
        TEXT_BUFFER_VIEW_REC *view;
        LINE_REC *insert_after;
 
-        last_color = 0;
+        last_fg = last_bg = -1;
        last_flags = 0;
 
        view = WINDOW_GUI(window)->view;
        last_flags = 0;
 
        view = WINDOW_GUI(window)->view;
@@ -202,74 +296,36 @@ static void sig_printtext_finished(WINDOW_REC *window)
        remove_old_lines(view);
 }
 
        remove_old_lines(view);
 }
 
-static void sig_print_format(THEME_REC *theme, const char *module,
-                            TEXT_DEST_REC *dest, void *formatnump,
-                            char **args)
-{
-       FORMAT_REC *formats;
-       int formatnum, n;
-
-       if (!scrollback_save_formats)
-               return;
-
-       formatnum = GPOINTER_TO_INT(formatnump);
-       formats = g_hash_table_lookup(default_formats, module);
-
-       /* <module><format_name><arg...> */
-       g_string_truncate(format, 0);
-
-       g_string_append_c(format, '\0');
-       g_string_append_c(format, (char)LINE_CMD_FORMAT);
-
-        g_string_append(format, module);
-
-       g_string_append_c(format, '\0');
-       g_string_append_c(format, (char)LINE_CMD_FORMAT);
-
-       g_string_append(format, formats[formatnum].tag);
-
-       for (n = 0; n < formats[formatnum].params; n++) {
-               g_string_append_c(format, '\0');
-               g_string_append_c(format, (char)LINE_CMD_FORMAT);
-
-               g_string_append(format, args[n]);
-       }
-}
-
 static void read_settings(void)
 {
        scrollback_lines = settings_get_int("scrollback_lines");
        scrollback_hours = settings_get_int("scrollback_hours");
         scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
 static void read_settings(void)
 {
        scrollback_lines = settings_get_int("scrollback_lines");
        scrollback_hours = settings_get_int("scrollback_hours");
         scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
-        scrollback_save_formats = settings_get_bool("scrollback_save_formats");
 }
 
 void gui_printtext_init(void)
 {
        next_xpos = next_ypos = -1;
 }
 
 void gui_printtext_init(void)
 {
        next_xpos = next_ypos = -1;
-       format = g_string_new(NULL);
+       default_indent_func = NULL;
+       indent_functions = g_hash_table_new((GHashFunc) g_str_hash,
+                                           (GCompareFunc) g_str_equal);
 
        settings_add_int("history", "scrollback_lines", 500);
        settings_add_int("history", "scrollback_hours", 24);
        settings_add_int("history", "scrollback_burst_remove", 10);
 
        settings_add_int("history", "scrollback_lines", 500);
        settings_add_int("history", "scrollback_hours", 24);
        settings_add_int("history", "scrollback_burst_remove", 10);
-       settings_add_bool("history", "scrollback_save_formats", FALSE);
 
        signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
 
        signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
-       signal_add("print text finished", (SIGNAL_FUNC) sig_printtext_finished);
-       signal_add("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
-       signal_add("beep", (SIGNAL_FUNC) beep);
 
        read_settings();
 }
 
 void gui_printtext_deinit(void)
 {
 
        read_settings();
 }
 
 void gui_printtext_deinit(void)
 {
-       g_string_free(format, TRUE);
+       g_hash_table_destroy(indent_functions);
 
        signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
 
        signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
-       signal_remove("print text finished", (SIGNAL_FUNC) sig_printtext_finished);
-       signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
-       signal_remove("beep", (SIGNAL_FUNC) beep);
 }
 }
index 3b2098b7805a25dcecf315f7d32d0a30a6f479a7..47cd3c2322bfc0248747169d3810d8f831885a25 100644 (file)
@@ -2,12 +2,21 @@
 #define __GUI_PRINTTEXT_H
 
 #include "gui-windows.h"
 #define __GUI_PRINTTEXT_H
 
 #include "gui-windows.h"
+#include "textbuffer-view.h"
+#include "formats.h"
 
 extern int mirc_colors[];
 
 void gui_printtext_init(void);
 void gui_printtext_deinit(void);
 
 
 extern int mirc_colors[];
 
 void gui_printtext_init(void);
 void gui_printtext_deinit(void);
 
+void gui_register_indent_func(const char *name, INDENT_FUNC func);
+void gui_unregister_indent_func(const char *name, INDENT_FUNC func);
+
+void gui_set_default_indent(const char *name);
+INDENT_FUNC get_default_indent_func(void);
+
 void gui_printtext(int xpos, int ypos, const char *str);
 void gui_printtext(int xpos, int ypos, const char *str);
+void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str);
 
 #endif
 
 #endif
index bc3429d8b04d8ee0f16338146e86a2f484cdd36c..8fe1741089524393a68f6032d7220fd4f7ad5094 100644 (file)
 #include "misc.h"
 #include "settings.h"
 #include "special-vars.h"
 #include "misc.h"
 #include "settings.h"
 #include "special-vars.h"
+#include "servers.h"
 
 #include "completion.h"
 #include "command-history.h"
 #include "keyboard.h"
 #include "translation.h"
 
 
 #include "completion.h"
 #include "command-history.h"
 #include "keyboard.h"
 #include "translation.h"
 
-#include "screen.h"
+#include "term.h"
 #include "gui-entry.h"
 #include "gui-windows.h"
 
 #include "gui-entry.h"
 #include "gui-windows.h"
 
@@ -51,6 +52,25 @@ char *cutbuffer;
 static int readtag;
 static time_t idle_time;
 
 static int readtag;
 static time_t idle_time;
 
+static void sig_input(void);
+
+void input_listen_init(int handle)
+{
+        GIOChannel *stdin_channel;
+
+       stdin_channel = g_io_channel_unix_new(handle);
+       readtag = g_input_add_full(stdin_channel,
+                                  G_PRIORITY_HIGH, G_INPUT_READ,
+                                  (GInputFunction) sig_input, NULL);
+        g_io_channel_unref(stdin_channel);
+}
+
+void input_listen_deinit(void)
+{
+       g_source_remove(readtag);
+        readtag = -1;
+}
+
 static void handle_key_redirect(int key)
 {
        ENTRY_REDIRECT_KEY_FUNC func;
 static void handle_key_redirect(int key)
 {
        ENTRY_REDIRECT_KEY_FUNC func;
@@ -63,8 +83,7 @@ static void handle_key_redirect(int key)
        if (func != NULL)
                func(key, data, active_win->active_server, active_win->active);
 
        if (func != NULL)
                func(key, data, active_win->active_server, active_win->active);
 
-       gui_entry_remove_perm_prompt();
-       window_update_prompt();
+       gui_entry_set_prompt(active_entry, "");
 }
 
 static void handle_entry_redirect(const char *line)
 }
 
 static void handle_entry_redirect(const char *line)
@@ -72,7 +91,7 @@ static void handle_entry_redirect(const char *line)
        ENTRY_REDIRECT_ENTRY_FUNC func;
        void *data;
 
        ENTRY_REDIRECT_ENTRY_FUNC func;
        void *data;
 
-        gui_entry_set_hidden(FALSE);
+        gui_entry_set_hidden(active_entry, FALSE);
 
        func = (ENTRY_REDIRECT_ENTRY_FUNC) redir->func;
        data = redir->data;
 
        func = (ENTRY_REDIRECT_ENTRY_FUNC) redir->func;
        data = redir->data;
@@ -83,8 +102,7 @@ static void handle_entry_redirect(const char *line)
                     active_win->active);
        }
 
                     active_win->active);
        }
 
-       gui_entry_remove_perm_prompt();
-       window_update_prompt();
+       gui_entry_set_prompt(active_entry, "");
 }
 
 static int get_scroll_count(void)
 }
 
 static int get_scroll_count(void)
@@ -99,8 +117,9 @@ static int get_scroll_count(void)
        else if (count < 1)
                 count = 1.0/count;
 
        else if (count < 1)
                 count = 1.0/count;
 
-       if (*str == '/')
-               count = WINDOW_GUI(active_win)->parent->height/count;
+       if (*str == '/') {
+               count = (active_mainwin->height-active_mainwin->statusbar_lines)/count;
+       }
        return (int)count;
 }
 
        return (int)count;
 }
 
@@ -141,36 +160,44 @@ void handle_key(int key)
 
        if (!key_pressed(keyboard, str)) {
                 /* key wasn't used for anything, print it */
 
        if (!key_pressed(keyboard, str)) {
                 /* key wasn't used for anything, print it */
-               gui_entry_insert_char((char) key);
+               gui_entry_insert_char(active_entry, (char) key);
        }
 }
 
 static void key_send_line(void)
 {
        }
 }
 
 static void key_send_line(void)
 {
-       int add_history;
-        char *str;
+       HISTORY_REC *history;
+        char *str, *add_history;
 
 
-       str = gui_entry_get_text();
+       str = gui_entry_get_text(active_entry);
        if (*str == '\0') return;
 
        if (*str == '\0') return;
 
+       /* we can't use gui_entry_get_text() later, since the entry might
+          have been destroyed after we get back */
+       add_history = g_strdup(str);
+       history = command_history_current(active_win);
+
        translate_output(str);
 
        translate_output(str);
 
-       add_history = TRUE;
        if (redir == NULL) {
                signal_emit("send command", 3, str,
                            active_win->active_server,
                            active_win->active);
        } else {
                if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
        if (redir == NULL) {
                signal_emit("send command", 3, str,
                            active_win->active_server,
                            active_win->active);
        } else {
                if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
-                       add_history = FALSE;
+                        g_free_and_null(add_history);
                handle_entry_redirect(str);
        }
 
                handle_entry_redirect(str);
        }
 
-       if (add_history) {
-               command_history_add(active_win, gui_entry_get_text(),
-                                   FALSE);
+       if (add_history != NULL) {
+               history = command_history_find(history);
+               if (history != NULL)
+                       command_history_add(history, add_history);
+                g_free(add_history);
        }
        }
-       gui_entry_set_text("");
+
+       if (active_entry != NULL)
+               gui_entry_set_text(active_entry, "");
        command_history_clear_pos(active_win);
 }
 
        command_history_clear_pos(active_win);
 }
 
@@ -182,83 +209,93 @@ static void key_backward_history(void)
 {
        const char *text;
 
 {
        const char *text;
 
-       text = command_history_prev(active_win, gui_entry_get_text());
-       gui_entry_set_text(text);
+       text = command_history_prev(active_win, gui_entry_get_text(active_entry));
+       gui_entry_set_text(active_entry, text);
 }
 
 static void key_forward_history(void)
 {
        const char *text;
 
 }
 
 static void key_forward_history(void)
 {
        const char *text;
 
-       text = command_history_next(active_win, gui_entry_get_text());
-       gui_entry_set_text(text);
+       text = command_history_next(active_win, gui_entry_get_text(active_entry));
+       gui_entry_set_text(active_entry, text);
 }
 
 static void key_beginning_of_line(void)
 {
 }
 
 static void key_beginning_of_line(void)
 {
-        gui_entry_set_pos(0);
+        gui_entry_set_pos(active_entry, 0);
 }
 
 static void key_end_of_line(void)
 {
 }
 
 static void key_end_of_line(void)
 {
-       gui_entry_set_pos(strlen(gui_entry_get_text()));
+       gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry)));
 }
 
 static void key_backward_character(void)
 {
 }
 
 static void key_backward_character(void)
 {
-       gui_entry_move_pos(-1);
+       gui_entry_move_pos(active_entry, -1);
 }
 
 static void key_forward_character(void)
 {
 }
 
 static void key_forward_character(void)
 {
-       gui_entry_move_pos(1);
+       gui_entry_move_pos(active_entry, 1);
 }
 
 static void key_backward_word(void)
 {
 }
 
 static void key_backward_word(void)
 {
-       gui_entry_move_words(-1);
+       gui_entry_move_words(active_entry, -1, FALSE);
 }
 
 static void key_forward_word(void)
 {
 }
 
 static void key_forward_word(void)
 {
-       gui_entry_move_words(1);
+       gui_entry_move_words(active_entry, 1, FALSE);
+}
+
+static void key_backward_to_space(void)
+{
+       gui_entry_move_words(active_entry, -1, TRUE);
+}
+
+static void key_forward_to_space(void)
+{
+       gui_entry_move_words(active_entry, 1, TRUE);
 }
 
 static void key_erase_line(void)
 {
        g_free_not_null(cutbuffer);
 }
 
 static void key_erase_line(void)
 {
        g_free_not_null(cutbuffer);
-       cutbuffer = g_strdup(gui_entry_get_text());
+       cutbuffer = g_strdup(gui_entry_get_text(active_entry));
 
 
-       gui_entry_set_text("");
+       gui_entry_set_text(active_entry, "");
 }
 
 static void key_erase_to_beg_of_line(void)
 {
        int pos;
 
 }
 
 static void key_erase_to_beg_of_line(void)
 {
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
        g_free_not_null(cutbuffer);
        g_free_not_null(cutbuffer);
-       cutbuffer = g_strndup(gui_entry_get_text(), pos);
+       cutbuffer = g_strndup(gui_entry_get_text(active_entry), pos);
 
 
-       gui_entry_erase(pos);
+       gui_entry_erase(active_entry, pos);
 }
 
 static void key_erase_to_end_of_line(void)
 {
        int pos;
 
 }
 
 static void key_erase_to_end_of_line(void)
 {
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
        g_free_not_null(cutbuffer);
        g_free_not_null(cutbuffer);
-       cutbuffer = g_strdup(gui_entry_get_text()+pos);
+       cutbuffer = g_strdup(gui_entry_get_text(active_entry)+pos);
 
 
-       gui_entry_set_pos(strlen(gui_entry_get_text()));
-       gui_entry_erase(strlen(gui_entry_get_text()) - pos);
+       gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry)));
+       gui_entry_erase(active_entry, strlen(gui_entry_get_text(active_entry)) - pos);
 }
 
 static void key_yank_from_cutbuffer(void)
 {
        if (cutbuffer != NULL)
 }
 
 static void key_yank_from_cutbuffer(void)
 {
        if (cutbuffer != NULL)
-               gui_entry_insert_text(cutbuffer);
+               gui_entry_insert_text(active_entry, cutbuffer);
 }
 
 static void key_transpose_characters(void)
 }
 
 static void key_transpose_characters(void)
@@ -266,61 +303,72 @@ static void key_transpose_characters(void)
        char *line, c;
        int pos;
 
        char *line, c;
        int pos;
 
-       pos = gui_entry_get_pos();
-       line = gui_entry_get_text();
+       pos = gui_entry_get_pos(active_entry);
+       line = gui_entry_get_text(active_entry);
        if (pos == 0 || strlen(line) < 2)
                return;
 
        if (line[pos] != '\0')
        if (pos == 0 || strlen(line) < 2)
                return;
 
        if (line[pos] != '\0')
-               gui_entry_move_pos(1);
-       c = line[gui_entry_get_pos()-1];
-        gui_entry_erase(1);
-       gui_entry_move_pos(-1);
-       gui_entry_insert_char(c);
-        gui_entry_set_pos(pos);
+               gui_entry_move_pos(active_entry, 1);
+       c = line[gui_entry_get_pos(active_entry)-1];
+        gui_entry_erase(active_entry, 1);
+       gui_entry_move_pos(active_entry, -1);
+       gui_entry_insert_char(active_entry, c);
+        gui_entry_set_pos(active_entry, pos);
+       gui_entry_move_pos(active_entry, 1);
 }
 
 static void key_delete_character(void)
 {
 }
 
 static void key_delete_character(void)
 {
-       if (gui_entry_get_pos() < (int)strlen(gui_entry_get_text())) {
-               gui_entry_move_pos(1);
-               gui_entry_erase(1);
+       if (gui_entry_get_pos(active_entry) < (int)strlen(gui_entry_get_text(active_entry))) {
+               gui_entry_move_pos(active_entry, 1);
+               gui_entry_erase(active_entry, 1);
        }
 }
 
 static void key_backspace(void)
 {
        }
 }
 
 static void key_backspace(void)
 {
-       gui_entry_erase(1);
+       gui_entry_erase(active_entry, 1);
 }
 
 static void key_delete_previous_word(void)
 {
 }
 
 static void key_delete_previous_word(void)
 {
-  gui_entry_erase_word();
+       gui_entry_erase_word(active_entry, FALSE);
 }
 
 static void key_delete_next_word(void)
 {
 }
 
 static void key_delete_next_word(void)
 {
-       gui_entry_erase_next_word();
+       gui_entry_erase_next_word(active_entry, FALSE);
 }
 
 static void key_delete_to_previous_space(void)
 {
 }
 
 static void key_delete_to_previous_space(void)
 {
-       gui_entry_erase_word();
+       gui_entry_erase_word(active_entry, TRUE);
+}
+
+static void key_delete_to_next_space(void)
+{
+       gui_entry_erase_next_word(active_entry, TRUE);
 }
 
 }
 
-void readline(void)
+static void sig_input(void)
 {
 {
-       int key;
+        unsigned char buffer[128];
+       int ret, i;
 
 
-       for (;;) {
-               key = getch();
-               if (key == ERR
-#ifdef KEY_RESIZE
-                   || key == KEY_RESIZE
-#endif
-                  ) break;
+       if (!active_entry) {
+                /* no active entry yet - wait until we have it */
+               return;
+       }
 
 
-               handle_key(key);
+       ret = term_gets(buffer, sizeof(buffer));
+       if (ret == -1) {
+               /* lost terminal */
+               if (!term_detached)
+                       signal_emit("command quit", 1, "Lost terminal");
+       } else {
+               for (i = 0; i < ret; i++)
+                       handle_key(buffer[i]);
        }
 }
 
        }
 }
 
@@ -354,32 +402,43 @@ static void key_change_window(const char *data)
        signal_emit("command window goto", 3, data, active_win->active_server, active_win->active);
 }
 
        signal_emit("command window goto", 3, data, active_win->active_server, active_win->active);
 }
 
-static void key_word_completion(void)
+static void key_completion(int erase)
 {
        char *line;
        int pos;
 
 {
        char *line;
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
 
 
-       line = word_complete(active_win, gui_entry_get_text(), &pos);
+       line = word_complete(active_win, gui_entry_get_text(active_entry),
+                            &pos, erase);
        if (line != NULL) {
        if (line != NULL) {
-               gui_entry_set_text(line);
-               gui_entry_set_pos(pos);
+               gui_entry_set_text(active_entry, line);
+               gui_entry_set_pos(active_entry, pos);
                g_free(line);
        }
 }
 
                g_free(line);
        }
 }
 
+static void key_word_completion(void)
+{
+        key_completion(FALSE);
+}
+
+static void key_erase_completion(void)
+{
+        key_completion(TRUE);
+}
+
 static void key_check_replaces(void)
 {
        char *line;
        int pos;
 
 static void key_check_replaces(void)
 {
        char *line;
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
 
 
-       line = auto_word_complete(gui_entry_get_text(), &pos);
+       line = auto_word_complete(gui_entry_get_text(active_entry), &pos);
        if (line != NULL) {
        if (line != NULL) {
-               gui_entry_set_text(line);
-               gui_entry_set_pos(pos);
+               gui_entry_set_text(active_entry, line);
+               gui_entry_set_pos(active_entry, pos);
                g_free(line);
        }
 }
                g_free(line);
        }
 }
@@ -467,14 +526,22 @@ static void key_insert_text(const char *data)
 
        str = parse_special_string(data, active_win->active_server,
                                   active_win->active, "", NULL, 0);
 
        str = parse_special_string(data, active_win->active_server,
                                   active_win->active, "", NULL, 0);
-       gui_entry_insert_text(str);
+       gui_entry_insert_text(active_entry, str);
         g_free(str);
 }
 
         g_free(str);
 }
 
+static void key_sig_stop(void)
+{
+        term_stop();
+}
+
 static void sig_window_auto_changed(void)
 {
 static void sig_window_auto_changed(void)
 {
-       command_history_next(active_win, gui_entry_get_text());
-       gui_entry_set_text("");
+       if (active_entry == NULL)
+               return;
+
+       command_history_next(active_win, gui_entry_get_text(active_entry));
+       gui_entry_set_text(active_entry, "");
 }
 
 static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
 }
 
 static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
@@ -486,8 +553,8 @@ static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
        redir->data = data;
 
        if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
        redir->data = data;
 
        if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
-               gui_entry_set_hidden(TRUE);
-       gui_entry_set_perm_prompt(entry);
+               gui_entry_set_hidden(active_entry, TRUE);
+       gui_entry_set_prompt(active_entry, entry);
 }
 
 void gui_readline_init(void)
 }
 
 void gui_readline_init(void)
@@ -499,17 +566,18 @@ void gui_readline_init(void)
        cutbuffer = NULL;
        redir = NULL;
        idle_time = time(NULL);
        cutbuffer = NULL;
        redir = NULL;
        idle_time = time(NULL);
-       readtag = g_input_add_full(g_io_channel_unix_new(0),
-                                  G_PRIORITY_HIGH, G_INPUT_READ,
-                                  (GInputFunction) readline, NULL);
+        input_listen_init(STDIN_FILENO);
 
        settings_add_str("history", "scroll_page_count", "/2");
 
        keyboard = keyboard_create(NULL);
         key_configure_freeze();
 
 
        settings_add_str("history", "scroll_page_count", "/2");
 
        keyboard = keyboard_create(NULL);
         key_configure_freeze();
 
+       key_bind("key", NULL, " ", "space", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "^M", "return", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "^J", "return", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "^M", "return", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "^J", "return", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "^H", "backspace", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "^?", "backspace", (SIGNAL_FUNC) key_combo);
 
         /* meta */
        key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo);
 
         /* meta */
        key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo);
@@ -539,11 +607,18 @@ void gui_readline_init(void)
        key_bind("key", NULL, "meta2-2~", "insert", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "meta2-3~", "delete", (SIGNAL_FUNC) key_combo);
 
        key_bind("key", NULL, "meta2-2~", "insert", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "meta2-3~", "delete", (SIGNAL_FUNC) key_combo);
 
-        /* cursor movement */
+       key_bind("key", NULL, "meta2-d", "cleft", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "meta2-c", "cright", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "meta2-5D", "cleft", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "meta2-5C", "cright", (SIGNAL_FUNC) key_combo);
+
+       /* cursor movement */
        key_bind("backward_character", "", "left", NULL, (SIGNAL_FUNC) key_backward_character);
        key_bind("forward_character", "", "right", NULL, (SIGNAL_FUNC) key_forward_character);
        key_bind("backward_character", "", "left", NULL, (SIGNAL_FUNC) key_backward_character);
        key_bind("forward_character", "", "right", NULL, (SIGNAL_FUNC) key_forward_character);
-       key_bind("backward_word", "", "meta2-d", NULL, (SIGNAL_FUNC) key_backward_word);
-       key_bind("forward_word", "", "meta2-c", NULL, (SIGNAL_FUNC) key_forward_word);
+       key_bind("backward_word", "", "cleft", NULL, (SIGNAL_FUNC) key_backward_word);
+       key_bind("forward_word", "", "cright", NULL, (SIGNAL_FUNC) key_forward_word);
+       key_bind("backward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_backward_to_space);
+       key_bind("forward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_forward_to_space);
        key_bind("beginning_of_line", "", "home", NULL, (SIGNAL_FUNC) key_beginning_of_line);
        key_bind("beginning_of_line", NULL, "^A", NULL, (SIGNAL_FUNC) key_beginning_of_line);
        key_bind("end_of_line", "", "end", NULL, (SIGNAL_FUNC) key_end_of_line);
        key_bind("beginning_of_line", "", "home", NULL, (SIGNAL_FUNC) key_beginning_of_line);
        key_bind("beginning_of_line", NULL, "^A", NULL, (SIGNAL_FUNC) key_beginning_of_line);
        key_bind("end_of_line", "", "end", NULL, (SIGNAL_FUNC) key_end_of_line);
@@ -554,13 +629,13 @@ void gui_readline_init(void)
        key_bind("forward_history", "", "down", NULL, (SIGNAL_FUNC) key_forward_history);
 
         /* line editing */
        key_bind("forward_history", "", "down", NULL, (SIGNAL_FUNC) key_forward_history);
 
         /* line editing */
-       key_bind("backspace", "", "^H", NULL, (SIGNAL_FUNC) key_backspace);
-       key_bind("backspace", "", "^?", NULL, (SIGNAL_FUNC) key_backspace);
+       key_bind("backspace", "", "backspace", NULL, (SIGNAL_FUNC) key_backspace);
        key_bind("delete_character", "", "delete", NULL, (SIGNAL_FUNC) key_delete_character);
        key_bind("delete_character", NULL, "^D", NULL, (SIGNAL_FUNC) key_delete_character);
        key_bind("delete_next_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_next_word);
        key_bind("delete_character", "", "delete", NULL, (SIGNAL_FUNC) key_delete_character);
        key_bind("delete_character", NULL, "^D", NULL, (SIGNAL_FUNC) key_delete_character);
        key_bind("delete_next_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_next_word);
-       key_bind("delete_previous_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word);
+       key_bind("delete_previous_word", "meta-backspace", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word);
        key_bind("delete_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space);
        key_bind("delete_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space);
+       key_bind("delete_to_next_space", "", "", NULL, (SIGNAL_FUNC) key_delete_to_next_space);
        key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line);
        key_bind("erase_to_beg_of_line", "", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
        key_bind("erase_to_end_of_line", "", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
        key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line);
        key_bind("erase_to_beg_of_line", "", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
        key_bind("erase_to_end_of_line", "", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
@@ -570,8 +645,8 @@ void gui_readline_init(void)
         /* line transmitting */
        key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line);
        key_bind("word_completion", "", "^I", NULL, (SIGNAL_FUNC) key_word_completion);
         /* line transmitting */
        key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line);
        key_bind("word_completion", "", "^I", NULL, (SIGNAL_FUNC) key_word_completion);
-       key_bind("check_replaces", "Check word replaces", " ", NULL, (SIGNAL_FUNC) key_check_replaces);
-       key_bind("check_replaces", NULL, NULL, NULL, (SIGNAL_FUNC) key_check_replaces);
+       key_bind("erase_completion", "", "meta-k", NULL, (SIGNAL_FUNC) key_erase_completion);
+       key_bind("check_replaces", "Check word replaces", NULL, NULL, (SIGNAL_FUNC) key_check_replaces);
 
         /* window managing */
        key_bind("previous_window", "Previous window", "^P", NULL, (SIGNAL_FUNC) key_previous_window);
 
         /* window managing */
        key_bind("previous_window", "Previous window", "^P", NULL, (SIGNAL_FUNC) key_previous_window);
@@ -595,8 +670,11 @@ void gui_readline_init(void)
         /* inserting special input characters to line.. */
        key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text);
 
         /* inserting special input characters to line.. */
        key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text);
 
+        /* autoreplaces */
        key_bind("multi", NULL, "return", "check_replaces;send_line", NULL);
        key_bind("multi", NULL, "return", "check_replaces;send_line", NULL);
+       key_bind("multi", NULL, "space", "check_replaces;insert_text  ", NULL);
 
 
+        /* moving between windows */
        for (n = 0; changekeys[n] != '\0'; n++) {
                key = g_strdup_printf("meta-%c", changekeys[n]);
                ltoa(data, n+1);
        for (n = 0; changekeys[n] != '\0'; n++) {
                key = g_strdup_printf("meta-%c", changekeys[n]);
                ltoa(data, n+1);
@@ -604,6 +682,9 @@ void gui_readline_init(void)
                g_free(key);
        }
 
                g_free(key);
        }
 
+        /* misc */
+       key_bind("stop_irc", "Send SIGSTOP to client", "^Z", NULL, (SIGNAL_FUNC) key_sig_stop);
+
         key_configure_thaw();
 
        signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
         key_configure_thaw();
 
        signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
@@ -613,7 +694,7 @@ void gui_readline_init(void)
 void gui_readline_deinit(void)
 {
        g_free_not_null(cutbuffer);
 void gui_readline_deinit(void)
 {
        g_free_not_null(cutbuffer);
-       g_source_remove(readtag);
+        input_listen_deinit();
 
         key_configure_freeze();
 
 
         key_configure_freeze();
 
@@ -621,6 +702,8 @@ void gui_readline_deinit(void)
        key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character);
        key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word);
        key_unbind("forward_word", (SIGNAL_FUNC) key_forward_word);
        key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character);
        key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word);
        key_unbind("forward_word", (SIGNAL_FUNC) key_forward_word);
+       key_unbind("backward_to_space", (SIGNAL_FUNC) key_backward_to_space);
+       key_unbind("forward_to_space", (SIGNAL_FUNC) key_forward_to_space);
        key_unbind("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line);
        key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line);
 
        key_unbind("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line);
        key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line);
 
@@ -631,6 +714,7 @@ void gui_readline_deinit(void)
        key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character);
        key_unbind("delete_next_word", (SIGNAL_FUNC) key_delete_next_word);
        key_unbind("delete_previous_word", (SIGNAL_FUNC) key_delete_previous_word);
        key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character);
        key_unbind("delete_next_word", (SIGNAL_FUNC) key_delete_next_word);
        key_unbind("delete_previous_word", (SIGNAL_FUNC) key_delete_previous_word);
+       key_unbind("delete_to_next_space", (SIGNAL_FUNC) key_delete_to_next_space);
        key_unbind("delete_to_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space);
        key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line);
        key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
        key_unbind("delete_to_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space);
        key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line);
        key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
@@ -657,6 +741,7 @@ void gui_readline_deinit(void)
 
        key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text);
        key_unbind("change_window", (SIGNAL_FUNC) key_change_window);
 
        key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text);
        key_unbind("change_window", (SIGNAL_FUNC) key_change_window);
+       key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop);
         keyboard_destroy(keyboard);
 
         key_configure_thaw();
         keyboard_destroy(keyboard);
 
         key_configure_thaw();
index 6464921f59e56b636b3c79e06e030ba9925683de..9ce3a742412b036390ce64ced9c70e5a4762b7d2 100644 (file)
@@ -3,6 +3,9 @@
 
 extern char *cutbuffer;
 
 
 extern char *cutbuffer;
 
+void input_listen_init(int handle);
+void input_listen_deinit(void);
+
 void readline(void);
 time_t get_idle_time(void);
 
 void readline(void);
 time_t get_idle_time(void);
 
index 5d7ad0d734b150b306f74cdf072b46b17580da19..cd834ee48c791f1c387710e67098d08ee70bb802 100644 (file)
 #include "settings.h"
 #include "special-vars.h"
 
 #include "settings.h"
 #include "special-vars.h"
 
-#include "screen.h"
+#include "term.h"
 #include "gui-entry.h"
 #include "gui-windows.h"
 #include "gui-printtext.h"
 
 static int window_create_override;
 
 #include "gui-entry.h"
 #include "gui-windows.h"
 #include "gui-printtext.h"
 
 static int window_create_override;
 
-static char *prompt, *prompt_window;
-
 static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window,
                                       MAIN_WINDOW_REC *parent)
 {
        GUI_WINDOW_REC *gui;
 
        window->width = parent->width;
 static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window,
                                       MAIN_WINDOW_REC *parent)
 {
        GUI_WINDOW_REC *gui;
 
        window->width = parent->width;
-        window->height = parent->height;
+        window->height = MAIN_WINDOW_TEXT_HEIGHT(parent);
 
        gui = g_new0(GUI_WINDOW_REC, 1);
        gui->parent = parent;
        gui->view = textbuffer_view_create(textbuffer_create(),
                                           window->width, window->height,
 
        gui = g_new0(GUI_WINDOW_REC, 1);
        gui->parent = parent;
        gui->view = textbuffer_view_create(textbuffer_create(),
                                           window->width, window->height,
+                                          settings_get_bool("scroll"),
+                                          settings_get_bool("term_utf8"));
+       textbuffer_view_set_default_indent(gui->view,
                                           settings_get_int("indent"),
                                           settings_get_int("indent"),
-                                          settings_get_bool("indent_always"));
+                                          !settings_get_bool("indent_always"),
+                                          get_default_indent_func());
+       if (parent->active == window)
+               textbuffer_view_set_window(gui->view, parent->screen_win);
        return gui;
 }
 
        return gui;
 }
 
@@ -61,33 +65,37 @@ static void sig_window_create_override(gpointer tab)
        window_create_override = GPOINTER_TO_INT(tab);
 }
 
        window_create_override = GPOINTER_TO_INT(tab);
 }
 
-static void gui_window_created(WINDOW_REC *window)
+static void gui_window_created(WINDOW_REC *window, void *automatic)
 {
        MAIN_WINDOW_REC *parent;
 {
        MAIN_WINDOW_REC *parent;
+        int empty_window, new_parent;
 
        g_return_if_fail(window != NULL);
 
 
        g_return_if_fail(window != NULL);
 
-       parent = window_create_override != 0 &&
-               active_win != NULL && WINDOW_GUI(active_win) != NULL ?
-               WINDOW_GUI(active_win)->parent : mainwindow_create();
+       new_parent = window_create_override == 0 ||
+               window_create_override == 2 ||
+               active_win == NULL || WINDOW_GUI(active_win) == NULL;
+       parent = !new_parent ? WINDOW_MAIN(active_win) : mainwindow_create();
        if (parent == NULL) {
                /* not enough space for new window, but we really can't
                   abort creation of the window anymore, so create hidden
                   window instead. */
        if (parent == NULL) {
                /* not enough space for new window, but we really can't
                   abort creation of the window anymore, so create hidden
                   window instead. */
-               parent = WINDOW_GUI(active_win)->parent;
+               parent = WINDOW_MAIN(active_win);
        }
        window_create_override = -1;
 
        }
        window_create_override = -1;
 
-       if (settings_get_bool("autostick_split_windows") &&
-           (parent->sticky_windows != NULL ||
-            (mainwindows->next != NULL && parent->active == NULL))) {
-                /* set the window sticky */
-               parent->sticky_windows =
-                       g_slist_append(parent->sticky_windows, window);
-       }
+        empty_window = parent->active == NULL;
 
        if (parent->active == NULL) parent->active = window;
        window->gui_data = gui_window_init(window, parent);
 
        if (parent->active == NULL) parent->active = window;
        window->gui_data = gui_window_init(window, parent);
+
+       /* set only non-automatic windows sticky so that the windows
+          irssi creates at startup wont get sticky. */
+       if (automatic == NULL &&
+           (parent->sticky_windows ||
+            (new_parent && settings_get_bool("autostick_split_windows"))))
+               gui_window_set_sticky(window);
+
        signal_emit("gui window created", 1, window);
 }
 
        signal_emit("gui window created", 1, window);
 }
 
@@ -101,21 +109,29 @@ static void gui_window_destroyed(WINDOW_REC *window)
        gui = WINDOW_GUI(window);
        parent = gui->parent;
 
        gui = WINDOW_GUI(window);
        parent = gui->parent;
 
+       gui_window_set_unsticky(window);
+
        signal_emit("gui window destroyed", 1, window);
 
        gui_window_deinit(gui);
        window->gui_data = NULL;
 
        signal_emit("gui window destroyed", 1, window);
 
        gui_window_deinit(gui);
        window->gui_data = NULL;
 
-       if (parent->active == window && mainwindows->next != NULL)
-               mainwindow_destroy(parent);
+       if (parent->active == window)
+               mainwindow_change_active(parent, window);
 }
 
 void gui_window_resize(WINDOW_REC *window, int width, int height)
 {
        GUI_WINDOW_REC *gui;
 
 }
 
 void gui_window_resize(WINDOW_REC *window, int width, int height)
 {
        GUI_WINDOW_REC *gui;
 
+       if (window->width == width && window->height == height)
+                return;
+
        gui = WINDOW_GUI(window);
 
        gui = WINDOW_GUI(window);
 
+       irssi_set_dirty();
+        WINDOW_MAIN(window)->dirty = TRUE;
+
         window->width = width;
        window->height = height;
         textbuffer_view_resize(gui->view, width, height);
         window->width = width;
        window->height = height;
         textbuffer_view_resize(gui->view, width, height);
@@ -138,68 +154,66 @@ void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line)
        signal_emit("gui page scrolled", 1, window);
 }
 
        signal_emit("gui page scrolled", 1, window);
 }
 
-void window_update_prompt(void)
+void gui_window_set_sticky(WINDOW_REC *window)
 {
 {
-        const char *special;
-       char *prompt, *text;
-        int var_used;
-
-       special = settings_get_str(active_win->active != NULL ?
-                                  "prompt" : "prompt_window");
-       if (*special == '\0') {
-               gui_entry_set_prompt("");
-               return;
-       }
+       GUI_WINDOW_REC *gui = WINDOW_GUI(window);
 
 
-       prompt = parse_special_string(special, active_win->active_server,
-                                     active_win->active, "", &var_used,
-                                     PARSE_FLAG_ISSET_ANY |
-                                     PARSE_FLAG_ESCAPE_VARS);
-       if (!var_used && strchr(special, '$') != NULL) {
-                /* none of the $vars had non-empty values, use empty prompt */
-               *prompt = '\0';
+       if (!gui->sticky) {
+               gui->sticky = TRUE;
+               gui->parent->sticky_windows++;
        }
        }
-
-       /* set prompt */
-       text = show_lowascii(prompt);
-       gui_entry_set_prompt(text);
-       g_free(text);
-
-       g_free(prompt);
-}
-
-static void window_update_prompt_server(SERVER_REC *server)
-{
-       if (server == active_win->active_server)
-                window_update_prompt();
 }
 
 }
 
-static void window_update_prompt_window(WINDOW_REC *window)
+void gui_window_set_unsticky(WINDOW_REC *window)
 {
 {
-       if (window == active_win)
-                window_update_prompt();
-}
+       GUI_WINDOW_REC *gui = WINDOW_GUI(window);
 
 
-static void window_update_prompt_window_item(WI_ITEM_REC *item)
-{
-       if (item == active_win->active)
-                window_update_prompt();
+       if (gui->sticky) {
+               gui->sticky = FALSE;
+               gui->parent->sticky_windows--;
+       }
 }
 
 void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent)
 {
        MAIN_WINDOW_REC *oldparent;
 
 }
 
 void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent)
 {
        MAIN_WINDOW_REC *oldparent;
 
-       oldparent = WINDOW_GUI(window)->parent;
+       oldparent = WINDOW_MAIN(window);
        if (oldparent == parent)
                return;
 
        if (oldparent == parent)
                return;
 
+        gui_window_set_unsticky(window);
        textbuffer_view_set_window(WINDOW_GUI(window)->view, NULL);
 
        textbuffer_view_set_window(WINDOW_GUI(window)->view, NULL);
 
-       WINDOW_GUI(window)->parent = parent;
-       if (parent->height != oldparent->height ||
-           parent->width != oldparent->width)
-               gui_window_resize(window, parent->width, parent->height);
+       WINDOW_MAIN(window) = parent;
+        if (parent->sticky_windows)
+               gui_window_set_sticky(window);
+
+       if (MAIN_WINDOW_TEXT_HEIGHT(parent) !=
+           MAIN_WINDOW_TEXT_HEIGHT(oldparent) ||
+           parent->width != oldparent->width) {
+               gui_window_resize(window, parent->width,
+                                 MAIN_WINDOW_TEXT_HEIGHT(parent));
+       }
+}
+
+void gui_windows_reset_settings(void)
+{
+       GSList *tmp;
+
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+                GUI_WINDOW_REC *gui = WINDOW_GUI(rec);
+
+                textbuffer_view_set_default_indent(gui->view,
+                                                  settings_get_int("indent"),
+                                                  !settings_get_bool("indent_always"),
+                                                   get_default_indent_func());
+
+               textbuffer_view_set_scroll(gui->view,
+                                          gui->use_scroll ? gui->scroll :
+                                          settings_get_bool("scroll"));
+       }
 }
 
 static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
 }
 
 static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
@@ -209,7 +223,7 @@ static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-               if (rec->sticky_windows == NULL)
+               if (!rec->sticky_windows)
                         return rec;
        }
 
                         return rec;
        }
 
@@ -220,26 +234,26 @@ static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
 static void signal_window_changed(WINDOW_REC *window)
 {
        MAIN_WINDOW_REC *parent;
 static void signal_window_changed(WINDOW_REC *window)
 {
        MAIN_WINDOW_REC *parent;
-       WINDOW_REC *old_window;
+        WINDOW_REC *old_window;
 
        g_return_if_fail(window != NULL);
 
         if (quitting) return;
 
 
        g_return_if_fail(window != NULL);
 
         if (quitting) return;
 
-        parent = WINDOW_GUI(window)->parent;
+        parent = WINDOW_MAIN(window);
        if (is_window_visible(window)) {
                /* already visible */
                active_mainwin = parent;
        } else if (active_mainwin == NULL) {
                 /* no main window set yet */
                active_mainwin = parent;
        if (is_window_visible(window)) {
                /* already visible */
                active_mainwin = parent;
        } else if (active_mainwin == NULL) {
                 /* no main window set yet */
                active_mainwin = parent;
-       } else if (g_slist_find(parent->sticky_windows, window) != NULL) {
+       } else if (WINDOW_GUI(window)->sticky) {
                 /* window is sticky, switch to correct main window */
                if (parent != active_mainwin)
                         active_mainwin = parent;
        } else {
                /* move window to active main window */
                 /* window is sticky, switch to correct main window */
                if (parent != active_mainwin)
                         active_mainwin = parent;
        } else {
                /* move window to active main window */
-                if (active_mainwin->sticky_windows != NULL) {
+                if (active_mainwin->sticky_windows) {
                        /* active mainwindow is sticky, we'll need to
                           set the window active somewhere else */
                         active_mainwin = mainwindow_find_unsticky();
                        /* active mainwindow is sticky, we'll need to
                           set the window active somewhere else */
                         active_mainwin = mainwindow_find_unsticky();
@@ -248,54 +262,20 @@ static void signal_window_changed(WINDOW_REC *window)
        }
 
        old_window = active_mainwin->active;
        }
 
        old_window = active_mainwin->active;
-       if (old_window != NULL)
-                textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL);
-        active_mainwin->active = window;
+       if (old_window != NULL && old_window != window)
+               textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL);
 
 
-       textbuffer_view_set_window(WINDOW_GUI(window)->view,
-                                  parent->curses_win);
-
-       window_update_prompt();
-}
+       active_mainwin->active = window;
 
 
-static void sig_check_window_update(WINDOW_REC *window)
-{
-       if (window == active_win)
-                window_update_prompt();
+       textbuffer_view_set_window(WINDOW_GUI(window)->view,
+                                  active_mainwin->screen_win);
+       if (WINDOW_GUI(window)->view->dirty)
+               active_mainwin->dirty = TRUE;
 }
 
 static void read_settings(void)
 {
 }
 
 static void read_settings(void)
 {
-       GSList *tmp;
-
-       SIGNAL_FUNC funcs[] = {
-                (SIGNAL_FUNC) window_update_prompt,
-                (SIGNAL_FUNC) window_update_prompt_server,
-                (SIGNAL_FUNC) window_update_prompt_window,
-                (SIGNAL_FUNC) window_update_prompt_window_item
-       };
-
-       if (prompt != NULL) {
-               special_vars_remove_signals(prompt, 4, funcs);
-               special_vars_remove_signals(prompt_window, 4, funcs);
-               g_free(prompt);
-                g_free(prompt_window);
-       }
-       prompt = g_strdup(settings_get_str("prompt"));
-       prompt_window = g_strdup(settings_get_str("prompt_window"));
-
-       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-               WINDOW_REC *rec = tmp->data;
-
-                textbuffer_view_set_default_indent(WINDOW_GUI(rec)->view,
-                                                  settings_get_int("indent"),
-                                                  settings_get_bool("indent_always"));
-       }
-
-       special_vars_add_signals(prompt, 4, funcs);
-       special_vars_add_signals(prompt_window, 4, funcs);
-
-       if (active_win != NULL) window_update_prompt();
+        gui_windows_reset_settings();
 }
 
 void gui_windows_init(void)
 }
 
 void gui_windows_init(void)
@@ -303,10 +283,8 @@ void gui_windows_init(void)
         settings_add_bool("lookandfeel", "autostick_split_windows", TRUE);
        settings_add_int("lookandfeel", "indent", 10);
        settings_add_bool("lookandfeel", "indent_always", FALSE);
         settings_add_bool("lookandfeel", "autostick_split_windows", TRUE);
        settings_add_int("lookandfeel", "indent", 10);
        settings_add_bool("lookandfeel", "indent_always", FALSE);
-       settings_add_str("lookandfeel", "prompt", "[$[.15]T] ");
-       settings_add_str("lookandfeel", "prompt_window", "[$winname] ");
+       settings_add_bool("lookandfeel", "scroll", TRUE);
 
 
-        prompt = NULL; prompt_window = NULL;
        window_create_override = -1;
 
        read_settings();
        window_create_override = -1;
 
        read_settings();
@@ -314,15 +292,11 @@ void gui_windows_init(void)
        signal_add("window created", (SIGNAL_FUNC) gui_window_created);
        signal_add("window destroyed", (SIGNAL_FUNC) gui_window_destroyed);
        signal_add_first("window changed", (SIGNAL_FUNC) signal_window_changed);
        signal_add("window created", (SIGNAL_FUNC) gui_window_created);
        signal_add("window destroyed", (SIGNAL_FUNC) gui_window_destroyed);
        signal_add_first("window changed", (SIGNAL_FUNC) signal_window_changed);
-       signal_add("window item remove", (SIGNAL_FUNC) sig_check_window_update);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void gui_windows_deinit(void)
 {
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void gui_windows_deinit(void)
 {
-        g_free_not_null(prompt);
-        g_free_not_null(prompt_window);
-
        while (windows != NULL)
                window_destroy(windows->data);
 
        while (windows != NULL)
                window_destroy(windows->data);
 
@@ -330,6 +304,5 @@ void gui_windows_deinit(void)
        signal_remove("window created", (SIGNAL_FUNC) gui_window_created);
        signal_remove("window destroyed", (SIGNAL_FUNC) gui_window_destroyed);
        signal_remove("window changed", (SIGNAL_FUNC) signal_window_changed);
        signal_remove("window created", (SIGNAL_FUNC) gui_window_created);
        signal_remove("window destroyed", (SIGNAL_FUNC) gui_window_destroyed);
        signal_remove("window changed", (SIGNAL_FUNC) signal_window_changed);
-       signal_remove("window item remove", (SIGNAL_FUNC) sig_check_window_update);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
index fb94b46f589d1a8583bfe40c589d529d9d49c09b..9d4afac7a4df2cff1ffc6f5f3e2b7563c80d227a 100644 (file)
@@ -5,6 +5,7 @@
 #include "textbuffer-view.h"
 
 #define WINDOW_GUI(a) ((GUI_WINDOW_REC *) ((a)->gui_data))
 #include "textbuffer-view.h"
 
 #define WINDOW_GUI(a) ((GUI_WINDOW_REC *) ((a)->gui_data))
+#define WINDOW_MAIN(a) (WINDOW_GUI(a)->parent)
 
 #define is_window_visible(win) \
     (WINDOW_GUI(win)->parent->active == (win))
 
 #define is_window_visible(win) \
     (WINDOW_GUI(win)->parent->active == (win))
@@ -13,6 +14,10 @@ typedef struct {
        MAIN_WINDOW_REC *parent;
        TEXT_BUFFER_VIEW_REC *view;
 
        MAIN_WINDOW_REC *parent;
        TEXT_BUFFER_VIEW_REC *view;
 
+       unsigned int scroll:1;
+       unsigned int use_scroll:1;
+
+       unsigned int sticky:1;
        unsigned int use_insert_after:1;
         LINE_REC *insert_after;
 } GUI_WINDOW_REC;
        unsigned int use_insert_after:1;
         LINE_REC *insert_after;
 } GUI_WINDOW_REC;
@@ -31,6 +36,9 @@ void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent);
 void gui_window_scroll(WINDOW_REC *window, int lines);
 void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line);
 
 void gui_window_scroll(WINDOW_REC *window, int lines);
 void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line);
 
-void window_update_prompt(void);
+void gui_window_set_sticky(WINDOW_REC *window);
+void gui_window_set_unsticky(WINDOW_REC *window);
+
+void gui_windows_reset_settings(void);
 
 #endif
 
 #endif
index 75e68da3188fc83aa9a0bec11f7da87c915062d7..61fb5fc1d2a4617af6a30977f1789368c8356599 100644 (file)
 #include "gui-windows.h"
 #include "gui-printtext.h"
 
 #include "gui-windows.h"
 #include "gui-printtext.h"
 
+#define DEFAULT_LASTLOG_BEFORE 3
+#define DEFAULT_LASTLOG_AFTER 3
 #define MAX_LINES_WITHOUT_FORCE 1000
 
 static void window_lastlog_clear(WINDOW_REC *window)
 {
 #define MAX_LINES_WITHOUT_FORCE 1000
 
 static void window_lastlog_clear(WINDOW_REC *window)
 {
-        TEXT_BUFFER_VIEW_REC *view;
-       GList *tmp, *next;
+       TEXT_BUFFER_VIEW_REC *view;
+        LINE_REC *line, *next;
 
 
-        screen_refresh_freeze();
+        term_refresh_freeze();
        view = WINDOW_GUI(window)->view;
        view = WINDOW_GUI(window)->view;
-       for (tmp = textbuffer_view_get_lines(view); tmp != NULL; tmp = next) {
-               LINE_REC *line = tmp->data;
+       line = textbuffer_view_get_lines(view);
 
 
-                next = tmp->next;
-                if (line->info.level & MSGLEVEL_LASTLOG)
+       while (line != NULL) {
+                next = line->next;
+
+               if (line->info.level & MSGLEVEL_LASTLOG)
                        textbuffer_view_remove_line(view, line);
                        textbuffer_view_remove_line(view, line);
+                line = next;
        }
         textbuffer_view_redraw(view);
        }
         textbuffer_view_redraw(view);
-        screen_refresh_thaw();
+        term_refresh_thaw();
 }
 
 /* Only unknown keys in `optlist' should be levels.
 }
 
 /* Only unknown keys in `optlist' should be levels.
@@ -98,7 +102,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        GList *list, *tmp;
        GString *line;
         char *str;
        GList *list, *tmp;
        GString *line;
         char *str;
-       int level, len;
+       int level, before, after, len;
 
         level = cmd_options_get_level("lastlog", optlist);
        if (level == -1) return; /* error in options */
 
         level = cmd_options_get_level("lastlog", optlist);
        if (level == -1) return; /* error in options */
@@ -131,14 +135,26 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        else
                startline = NULL;
 
        else
                startline = NULL;
 
-       if (startline == NULL) {
-                list = textbuffer_view_get_lines(WINDOW_GUI(window)->view);
-               startline = list == NULL ? NULL : list->data;
+       if (startline == NULL)
+                startline = textbuffer_view_get_lines(WINDOW_GUI(window)->view);
+
+       str = g_hash_table_lookup(optlist, "#");
+       if (str != NULL) {
+               before = after = atoi(str);
+       } else {
+               str = g_hash_table_lookup(optlist, "before");
+               before = str == NULL ? 0 : *str != '\0' ?
+                       atoi(str) : DEFAULT_LASTLOG_BEFORE;
+
+               str = g_hash_table_lookup(optlist, "after");
+               if (str == NULL) str = g_hash_table_lookup(optlist, "a");
+               after = str == NULL ? 0 : *str != '\0' ?
+                       atoi(str) : DEFAULT_LASTLOG_AFTER;
        }
 
        list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline,
                                    level, MSGLEVEL_LASTLOG,
        }
 
        list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline,
                                    level, MSGLEVEL_LASTLOG,
-                                   searchtext,
+                                   searchtext, before, after,
                                    g_hash_table_lookup(optlist, "regexp") != NULL,
                                    g_hash_table_lookup(optlist, "word") != NULL,
                                    g_hash_table_lookup(optlist, "case") != NULL);
                                    g_hash_table_lookup(optlist, "regexp") != NULL,
                                    g_hash_table_lookup(optlist, "word") != NULL,
                                    g_hash_table_lookup(optlist, "case") != NULL);
@@ -147,15 +163,20 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        if (count <= 0)
                tmp = list;
        else {
        if (count <= 0)
                tmp = list;
        else {
-               int pos = len-count;
-
+               int pos = len-count-start;
                if (pos < 0) pos = 0;
                if (pos < 0) pos = 0;
-               pos += start;
 
                tmp = pos > len ? NULL : g_list_nth(list, pos);
                len = g_list_length(tmp);
        }
 
 
                tmp = pos > len ? NULL : g_list_nth(list, pos);
                len = g_list_length(tmp);
        }
 
+       if (g_hash_table_lookup(optlist, "count") != NULL) {
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_LASTLOG_COUNT, len);
+               g_list_free(list);
+               return;
+       }
+
        if (len > MAX_LINES_WITHOUT_FORCE && fhandle == -1 &&
            g_hash_table_lookup(optlist, "force") == NULL) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
        if (len > MAX_LINES_WITHOUT_FORCE && fhandle == -1 &&
            g_hash_table_lookup(optlist, "force") == NULL) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
@@ -171,6 +192,20 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
         while (tmp != NULL && (count < 0 || count > 0)) {
                LINE_REC *rec = tmp->data;
 
         while (tmp != NULL && (count < 0 || count > 0)) {
                LINE_REC *rec = tmp->data;
 
+               if (rec == NULL) {
+                       if (tmp->next == NULL)
+                                break;
+                       if (fhandle != -1) {
+                               write(fhandle, "--\n", 3);
+                       } else {
+                               printformat_window(active_win,
+                                                  MSGLEVEL_LASTLOG,
+                                                  TXT_LASTLOG_SEPARATOR);
+                       }
+                        tmp = tmp->next;
+                       continue;
+               }
+
                 /* get the line text */
                textbuffer_line2text(rec, fhandle == -1, line);
                if (!settings_get_bool("timestamps")) {
                 /* get the line text */
                textbuffer_line2text(rec, fhandle == -1, line);
                if (!settings_get_bool("timestamps")) {
@@ -207,9 +242,10 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        g_list_free(list);
 }
 
        g_list_free(list);
 }
 
-/* SYNTAX: LASTLOG [-] [-file <filename>] [-clear] [-<level> -<level...>]
-                  [-new | -away] [-regexp | -word] [-case]
-                  [-window <ref#|name>] [<pattern>] [<count> [<start>]] */
+/* SYNTAX: LASTLOG [-] [-file <filename>] [-window <ref#|name>] [-new | -away]
+                  [-<level> -<level...>] [-clear] [-count] [-case]
+                  [-regexp | -word] [-before [<#>]] [-after [<#>]]
+                  [-<# before+after>] [<pattern>] [<count> [<start>]] */
 static void cmd_lastlog(const char *data)
 {
        GHashTable *optlist;
 static void cmd_lastlog(const char *data)
 {
        GHashTable *optlist;
@@ -219,13 +255,14 @@ static void cmd_lastlog(const char *data)
 
        g_return_if_fail(data != NULL);
 
 
        g_return_if_fail(data != NULL);
 
-       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS,
-                           "lastlog", &optlist, &text, &countstr, &start))
+       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS, "lastlog", &optlist,
+                           &text, &countstr, &start))
                return;
 
                return;
 
-       if (*start == '\0' && is_numeric(text, 0)) {
-               if (is_numeric(countstr, 0))
-                       start = countstr;
+       if (*start == '\0' && is_numeric(text, 0) && *text != '0' &&
+           (*countstr == '\0' || is_numeric(countstr, 0))) {
+               start = countstr;
                countstr = text;
                text = "";
        }
                countstr = text;
                text = "";
        }
@@ -258,7 +295,7 @@ void lastlog_init(void)
 {
        command_bind("lastlog", NULL, (SIGNAL_FUNC) cmd_lastlog);
 
 {
        command_bind("lastlog", NULL, (SIGNAL_FUNC) cmd_lastlog);
 
-       command_set_options("lastlog", "!- force clear -file -window new away word regexp case");
+       command_set_options("lastlog", "!- # force clear -file -window new away word regexp case count @a @after @before");
 }
 
 void lastlog_deinit(void)
 }
 
 void lastlog_deinit(void)
diff --git a/apps/irssi/src/fe-text/mainwindows-layout.c b/apps/irssi/src/fe-text/mainwindows-layout.c
new file mode 100644 (file)
index 0000000..072b9b2
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ mainwindows-layout.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "mainwindows.h"
+#include "gui-windows.h"
+#include "textbuffer-view.h"
+
+static void sig_layout_window_save(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       WINDOW_REC *active;
+        GUI_WINDOW_REC *gui;
+
+        gui = WINDOW_GUI(window);
+       if (gui->sticky) {
+               iconfig_node_set_bool(node, "sticky", TRUE);
+               active = gui->parent->active;
+               if (window != active)
+                       iconfig_node_set_int(node, "parent", active->refnum);
+       }
+
+       if (gui->use_scroll)
+                iconfig_node_set_bool(node, "scroll", gui->scroll);
+}
+
+static void sig_layout_window_restore(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       WINDOW_REC *parent;
+        GUI_WINDOW_REC *gui;
+
+        gui = WINDOW_GUI(window);
+
+       parent = window_find_refnum(config_node_get_int(node, "parent", -1));
+       if (parent != NULL)
+               gui_window_reparent(window, WINDOW_MAIN(parent));
+
+       if (config_node_get_bool(node, "sticky", FALSE))
+               gui_window_set_sticky(window);
+       if (config_node_get_str(node, "scroll", NULL) != NULL) {
+               gui->use_scroll = TRUE;
+               gui->scroll = config_node_get_bool(node, "scroll", TRUE);
+                textbuffer_view_set_scroll(gui->view, gui->scroll);
+       }
+}
+
+static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node)
+{
+        char num[MAX_INT_STRLEN];
+
+        ltoa(num, window->active->refnum);
+       node = config_node_section(node, num, NODE_TYPE_BLOCK);
+
+       iconfig_node_set_int(node, "first_line", window->first_line);
+       iconfig_node_set_int(node, "lines", window->height);
+}
+
+static void sig_layout_save(void)
+{
+       CONFIG_NODE *node;
+
+       iconfig_set_str(NULL, "mainwindows", NULL);
+       node = iconfig_node_traverse("mainwindows", TRUE);
+
+       g_slist_foreach(mainwindows, (GFunc) main_window_save, node);
+}
+
+static int window_node_cmp(CONFIG_NODE *n1, CONFIG_NODE *n2)
+{
+       return config_node_get_int(n1, "first_line", 0) >
+               config_node_get_int(n2, "first_line", 0) ? -1 : 1;
+}
+
+/* Returns list of mainwindow nodes sorted by first_line
+   (lowest in screen first) */
+static GSList *get_sorted_windows_config(CONFIG_NODE *node)
+{
+       GSList *tmp, *output;
+
+        output = NULL;
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               output = g_slist_insert_sorted(output, tmp->data,
+                                              (GCompareFunc) window_node_cmp);
+       }
+
+        return output;
+}
+
+static void sig_layout_restore(void)
+{
+        MAIN_WINDOW_REC *lower_window;
+        WINDOW_REC *window;
+       CONFIG_NODE *node;
+       GSList *tmp, *sorted_config;
+        int avail_height, height, *heights;
+       int i, lower_size, windows_count, diff;
+
+       node = iconfig_node_traverse("mainwindows", FALSE);
+       if (node == NULL) return;
+
+       sorted_config = get_sorted_windows_config(node);
+        windows_count = g_slist_length(sorted_config);
+
+        /* calculate the saved terminal height */
+       avail_height = term_height -
+               screen_reserved_top - screen_reserved_bottom;
+       height = 0;
+        heights = g_new0(int, windows_count);
+       for (i = 0, tmp = sorted_config; tmp != NULL; tmp = tmp->next, i++) {
+               CONFIG_NODE *node = tmp->data;
+
+                heights[i] = config_node_get_int(node, "lines", 0);
+               height += heights[i];
+       }
+
+       if (avail_height <= (WINDOW_MIN_SIZE*2)+1) {
+               /* we can fit only one window to screen -
+                  give it all the height we can */
+               windows_count = 1;
+                heights[0] = avail_height;
+       } else if (height != avail_height) {
+               /* Terminal's height is different from the saved one.
+                  Resize the windows so they fit to screen. */
+               while (height > avail_height &&
+                      windows_count*(WINDOW_MIN_SIZE+1) > avail_height) {
+                       /* all windows can't fit into screen,
+                          remove the lowest ones */
+                        windows_count--;
+               }
+
+                /* try to keep the windows' size about the same in percents */
+               for (i = 0; i < windows_count; i++) {
+                       int size = avail_height*heights[i]/height;
+                       if (size < WINDOW_MIN_SIZE+1)
+                                size = WINDOW_MIN_SIZE+1;
+                       heights[i] = size;
+               }
+
+               /* give/remove the last bits */
+                height = 0;
+               for (i = 0; i < windows_count; i++)
+                        height += heights[i];
+
+               diff = height < avail_height ? 1 : -1;
+               for (i = 0; height != avail_height; i++) {
+                       if (i == windows_count)
+                               i = 0;
+
+                       if (heights[i] > WINDOW_MIN_SIZE+1) {
+                               height += diff;
+                               heights[i] += diff;
+                       }
+               }
+       }
+
+       /* create all the visible windows with correct size */
+       lower_window = NULL; lower_size = 0;
+       for (i = 0, tmp = sorted_config; i < windows_count; tmp = tmp->next, i++) {
+               CONFIG_NODE *node = tmp->data;
+
+               /* create a new window + mainwindow */
+               signal_emit("gui window create override", 1,
+                           GINT_TO_POINTER(0));
+
+               window = window_create(NULL, TRUE);
+                window_set_refnum(window, atoi(node->key));
+
+               if (lower_size > 0)
+                       mainwindow_set_size(lower_window, lower_size, FALSE);
+
+               window_set_active(window);
+                active_mainwin = WINDOW_MAIN(window);
+
+                lower_window = WINDOW_MAIN(window);
+               lower_size = heights[i];
+               if (lower_size < WINDOW_MIN_SIZE+1)
+                       lower_size = WINDOW_MIN_SIZE+1;
+       }
+       g_slist_free(sorted_config);
+       g_free(heights);
+
+       if (lower_size > 0)
+               mainwindow_set_size(lower_window, lower_size, FALSE);
+}
+
+static void sig_layout_reset(void)
+{
+       iconfig_set_str(NULL, "mainwindows", NULL);
+}
+
+void mainwindows_layout_init(void)
+{
+       signal_add("layout save window", (SIGNAL_FUNC) sig_layout_window_save);
+       signal_add("layout restore window", (SIGNAL_FUNC) sig_layout_window_restore);
+       signal_add("layout save", (SIGNAL_FUNC) sig_layout_save);
+       signal_add_first("layout restore", (SIGNAL_FUNC) sig_layout_restore);
+       signal_add("layout reset", (SIGNAL_FUNC) sig_layout_reset);
+}
+
+void mainwindows_layout_deinit(void)
+{
+       signal_remove("layout save window", (SIGNAL_FUNC) sig_layout_window_save);
+       signal_remove("layout restore window", (SIGNAL_FUNC) sig_layout_window_restore);
+       signal_remove("layout save", (SIGNAL_FUNC) sig_layout_save);
+       signal_remove("layout restore", (SIGNAL_FUNC) sig_layout_restore);
+       signal_remove("layout reset", (SIGNAL_FUNC) sig_layout_reset);
+}
index d727464ee3efeb9992ce18ad17cf148e3591c631..7fcd910333b3ad0beb6cf69f00e91c63c88a9ab4 100644 (file)
@@ -27,8 +27,7 @@
 #include "settings.h"
 #include "printtext.h"
 
 #include "settings.h"
 #include "printtext.h"
 
-#include "screen.h"
-#include "statusbar.h"
+#include "term.h"
 #include "gui-windows.h"
 
 #define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1)
 #include "gui-windows.h"
 
 #define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1)
 GSList *mainwindows;
 MAIN_WINDOW_REC *active_mainwin;
 
 GSList *mainwindows;
 MAIN_WINDOW_REC *active_mainwin;
 
-static int reserved_up, reserved_down;
-static int screen_width, screen_height;
+int screen_reserved_top, screen_reserved_bottom;
+static int old_screen_width, old_screen_height;
+
+#define mainwindow_create_screen(window) \
+       term_window_create(0, \
+                          (window)->first_line + (window)->statusbar_lines_top, \
+                          (window)->width, \
+                          (window)->height - (window)->statusbar_lines)
+
+#define mainwindow_set_screen_size(window) \
+       term_window_move((window)->screen_win, 0, \
+                        (window)->first_line + (window)->statusbar_lines_top, \
+                        (window)->width, \
+                        (window)->height - (window)->statusbar_lines);
+
 
 static MAIN_WINDOW_REC *find_window_with_room(void)
 {
 
 static MAIN_WINDOW_REC *find_window_with_room(void)
 {
@@ -49,7 +61,7 @@ static MAIN_WINDOW_REC *find_window_with_room(void)
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-               space = rec->height;
+               space = MAIN_WINDOW_TEXT_HEIGHT(rec);
                if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) {
                        biggest = space;
                        biggest_rec = rec;
                if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) {
                        biggest = space;
                        biggest_rec = rec;
@@ -59,45 +71,100 @@ static MAIN_WINDOW_REC *find_window_with_room(void)
        return biggest_rec;
 }
 
        return biggest_rec;
 }
 
-#ifdef USE_CURSES_WINDOWS
-static void create_curses_window(MAIN_WINDOW_REC *window)
+#define window_size_equals(window, mainwin) \
+       ((window)->width == (mainwin)->width && \
+        (window)->height == MAIN_WINDOW_TEXT_HEIGHT(mainwin))
+
+static void mainwindow_resize_windows(MAIN_WINDOW_REC *window)
 {
 {
-       window->curses_win = newwin(window->height, window->width,
-                                   window->first_line, 0);
-        idlok(window->curses_win, 1);
+       GSList *tmp;
+        int resized;
+
+       mainwindow_set_screen_size(window);
+
+       resized = FALSE;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->gui_data != NULL &&
+                   WINDOW_GUI(rec)->parent == window &&
+                   !window_size_equals(rec, window)) {
+                        resized = TRUE;
+                       gui_window_resize(rec, window->width,
+                                         MAIN_WINDOW_TEXT_HEIGHT(window));
+               }
+       }
+
+        if (resized)
+               signal_emit("mainwindow resized", 1, window);
 }
 }
-#endif
 
 static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff)
 {
 
 static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff)
 {
-       GSList *tmp;
-
-       if (xdiff == 0 && ydiff == 0)
+       if (quitting || (xdiff == 0 && ydiff == 0))
                 return;
 
         window->width += xdiff;
        window->height = window->last_line-window->first_line+1;
                 return;
 
         window->width += xdiff;
        window->height = window->last_line-window->first_line+1;
-#ifdef USE_CURSES_WINDOWS
-#ifdef HAVE_CURSES_WRESIZE
-       wresize(window->curses_win, window->height, window->width);
-       mvwin(window->curses_win, window->first_line, 0);
-#else
-       delwin(window->curses_win);
-       create_curses_window(window);
-#endif
-#endif
+        window->size_dirty = TRUE;
+}
+
+static GSList *get_sticky_windows_sorted(MAIN_WINDOW_REC *mainwin)
+{
+       GSList *tmp, *list;
 
 
+        list = NULL;
        for (tmp = windows; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *rec = tmp->data;
 
        for (tmp = windows; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *rec = tmp->data;
 
-               if (rec->gui_data != NULL &&
-                   WINDOW_GUI(rec)->parent == window)
-                       gui_window_resize(rec, window->width, window->height);
+               if (WINDOW_GUI(rec)->sticky && WINDOW_MAIN(rec) == mainwin) {
+                       list = g_slist_insert_sorted(list, rec, (GCompareFunc)
+                                                    window_refnum_cmp);
+               }
        }
 
        }
 
-       textbuffer_view_set_window(WINDOW_GUI(window->active)->view,
-                                  window->curses_win);
-       signal_emit("mainwindow resized", 1, window);
+        return list;
+}
+
+void mainwindow_change_active(MAIN_WINDOW_REC *mainwin,
+                             WINDOW_REC *skip_window)
+{
+        WINDOW_REC *window, *other;
+       GSList *tmp;
+
+        mainwin->active = NULL;
+       if (mainwin->sticky_windows) {
+               /* sticky window */
+                tmp = get_sticky_windows_sorted(mainwin);
+                window = tmp->data;
+               if (window == skip_window) {
+                       window = tmp->next == NULL ? NULL :
+                               tmp->next->data;
+               }
+                g_slist_free(tmp);
+
+               if (window != NULL) {
+                       window_set_active(window);
+                       return;
+               }
+       }
+
+        other = NULL;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec != skip_window) {
+                       if (WINDOW_MAIN(rec) == mainwin) {
+                               window_set_active(rec);
+                               return;
+                       }
+                        other = rec;
+               }
+       }
+
+       /* no more non-sticky windows, remove main window */
+       window_set_active(other);
+       mainwindow_destroy(mainwin);
 }
 
 void mainwindows_recreate(void)
 }
 
 void mainwindows_recreate(void)
@@ -107,11 +174,10 @@ void mainwindows_recreate(void)
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-#ifdef USE_CURSES_WINDOWS
-               create_curses_window(rec);
-#endif
+               rec->screen_win = mainwindow_create_screen(rec);
+                rec->dirty = TRUE;
                textbuffer_view_set_window(WINDOW_GUI(rec->active)->view,
                textbuffer_view_set_window(WINDOW_GUI(rec->active)->view,
-                                          rec->curses_win);
+                                          rec->screen_win);
        }
 }
 
        }
 }
 
@@ -121,37 +187,36 @@ MAIN_WINDOW_REC *mainwindow_create(void)
        int space;
 
        rec = g_new0(MAIN_WINDOW_REC, 1);
        int space;
 
        rec = g_new0(MAIN_WINDOW_REC, 1);
-       rec->width = screen_width;
-       rec->statusbar_lines = 1;
+       rec->dirty = TRUE;
+       rec->width = term_width;
 
        if (mainwindows == NULL) {
                active_mainwin = rec;
 
 
        if (mainwindows == NULL) {
                active_mainwin = rec;
 
-               rec->first_line = reserved_up;
-               rec->last_line = screen_height-1 -
-                       reserved_down-rec->statusbar_lines;
+               rec->first_line = screen_reserved_top;
+               rec->last_line = term_height-1 - screen_reserved_bottom;
                rec->height = rec->last_line-rec->first_line+1;
        } else {
                rec->height = rec->last_line-rec->first_line+1;
        } else {
-               parent = WINDOW_GUI(active_win)->parent;
-               if (parent->height < WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
+               parent = WINDOW_MAIN(active_win);
+               if (MAIN_WINDOW_TEXT_HEIGHT(parent) <
+                   WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
                        parent = find_window_with_room();
                if (parent == NULL)
                        return NULL; /* not enough space */
 
                        parent = find_window_with_room();
                if (parent == NULL)
                        return NULL; /* not enough space */
 
-               space = (parent->height-parent->statusbar_lines)/2;
+               space = parent->height / 2;
                rec->first_line = parent->first_line;
                rec->first_line = parent->first_line;
-               rec->last_line = rec->first_line + space-rec->statusbar_lines;
+               rec->last_line = rec->first_line + space;
                rec->height = rec->last_line-rec->first_line+1;
                rec->height = rec->last_line-rec->first_line+1;
-               parent->first_line = rec->last_line+1+rec->statusbar_lines;
+
+               parent->first_line = rec->last_line+1;
                parent->height = parent->last_line-parent->first_line+1;
 
                mainwindow_resize(parent, 0, -space-1);
        }
 
                parent->height = parent->last_line-parent->first_line+1;
 
                mainwindow_resize(parent, 0, -space-1);
        }
 
-#ifdef USE_CURSES_WINDOWS
-       rec->curses_win = newwin(rec->height, rec->width, rec->first_line, 0);
-       refresh();
-#endif
+       rec->screen_win = mainwindow_create_screen(rec);
+       term_refresh(NULL);
 
        mainwindows = g_slist_append(mainwindows, rec);
        signal_emit("mainwindow created", 1, rec);
 
        mainwindows = g_slist_append(mainwindows, rec);
        signal_emit("mainwindow created", 1, rec);
@@ -211,7 +276,7 @@ static void mainwindows_add_space(int first_line, int last_line)
 
        rec = mainwindows_find_upper(first_line);
        if (rec != NULL) {
 
        rec = mainwindows_find_upper(first_line);
        if (rec != NULL) {
-               rec->last_line = last_line-rec->statusbar_lines;
+               rec->last_line = last_line;
                mainwindow_resize(rec, 0, size);
        }
 }
                mainwindow_resize(rec, 0, size);
        }
 }
@@ -225,7 +290,7 @@ static void gui_windows_remove_parent(MAIN_WINDOW_REC *window)
        for (tmp = windows; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *rec = tmp->data;
 
        for (tmp = windows; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *rec = tmp->data;
 
-               if (rec->gui_data != NULL && WINDOW_GUI(rec)->parent == window)
+               if (rec->gui_data != NULL && WINDOW_MAIN(rec) == window)
                         gui_window_reparent(rec, new_parent);
        }
 }
                         gui_window_reparent(rec, new_parent);
        }
 }
@@ -234,20 +299,18 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window)
 {
        g_return_if_fail(window != NULL);
 
 {
        g_return_if_fail(window != NULL);
 
-#ifdef USE_CURSES_WINDOWS
-       delwin(window->curses_win);
-#endif
-
        mainwindows = g_slist_remove(mainwindows, window);
        signal_emit("mainwindow destroyed", 1, window);
 
        mainwindows = g_slist_remove(mainwindows, window);
        signal_emit("mainwindow destroyed", 1, window);
 
+        term_window_destroy(window->screen_win);
+
        if (!quitting && mainwindows != NULL) {
                gui_windows_remove_parent(window);
        if (!quitting && mainwindows != NULL) {
                gui_windows_remove_parent(window);
-               mainwindows_add_space(window->first_line, window->last_line+window->statusbar_lines);
+               mainwindows_add_space(window->first_line, window->last_line);
 
                mainwindows_redraw();
 
                mainwindows_redraw();
-               statusbar_redraw(NULL);
        }
        }
+
        g_free(window);
 
        if (active_mainwin == window) active_mainwin = NULL;
        g_free(window);
 
        if (active_mainwin == window) active_mainwin = NULL;
@@ -257,13 +320,12 @@ void mainwindows_redraw(void)
 {
         GSList *tmp;
 
 {
         GSList *tmp;
 
-       screen_refresh_freeze();
+        irssi_set_dirty();
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-                gui_window_redraw(rec->active);
+                rec->dirty = TRUE;
        }
        }
-       screen_refresh_thaw();
 }
 
 static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2)
 }
 
 static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2)
@@ -289,62 +351,46 @@ GSList *mainwindows_get_sorted(int reverse)
        return list;
 }
 
        return list;
 }
 
-static void mainwindows_resize_too_small(int xdiff, int ydiff)
-{
-       GSList *sorted, *tmp;
-        int space, moved;
-
-       /* terminal is too small - just take the space whereever possible */
-       sorted = mainwindows_get_sorted(FALSE);
-       moved = 0;
-       for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
-
-               space = rec->height;
-               if (ydiff == 0 || space <= 0) {
-                       if (moved > 0) {
-                               rec->first_line -= moved;
-                               rec->last_line -= moved;
-                               signal_emit("mainwindow moved", 1, rec);
-                       }
-                       continue;
-               }
-
-               if (space > -ydiff) space = -ydiff;
-               ydiff += space;
-               rec->first_line -= moved;
-               moved += space;
-               rec->last_line -= space;
-               mainwindow_resize(rec, xdiff, -space);
-       }
-       g_slist_free(sorted);
-}
-
 static void mainwindows_resize_smaller(int xdiff, int ydiff)
 {
 static void mainwindows_resize_smaller(int xdiff, int ydiff)
 {
+        MAIN_WINDOW_REC *rec;
        GSList *sorted, *tmp;
         int space;
 
        GSList *sorted, *tmp;
         int space;
 
-        space = 0;
-       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
+       for (;;) {
+               sorted = mainwindows_get_sorted(TRUE);
+               space = 0;
+               for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+                       rec = tmp->data;
+                       space += MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE;
+               }
 
 
-               space += rec->height-WINDOW_MIN_SIZE;
-       }
+               if (space >= -ydiff)
+                       break;
 
 
-       if (space < -ydiff) {
-               /* not enough space, use different algorithm */
-               mainwindows_resize_too_small(xdiff, ydiff);
-               return;
+               rec = sorted->data;
+               sorted = g_slist_remove(sorted, rec);
+
+               if (sorted != NULL) {
+                       /* terminal is too small - destroy the
+                          uppest window and try again */
+                       mainwindow_destroy(rec);
+               } else {
+                       /* only one window in screen.. just force the resize */
+                       rec->last_line += ydiff;
+                       mainwindow_resize(rec, xdiff, ydiff);
+                        return;
+               }
        }
 
        /* resize windows that have space */
        }
 
        /* resize windows that have space */
-       sorted = mainwindows_get_sorted(TRUE);
        for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) {
        for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
+               rec = tmp->data;
 
 
-               space = rec->height-WINDOW_MIN_SIZE;
+               space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE;
                if (space <= 0) {
                if (space <= 0) {
+                       mainwindow_resize(rec, xdiff, 0);
+
                        rec->first_line += ydiff;
                        rec->last_line += ydiff;
                        signal_emit("mainwindow moved", 1, rec);
                        rec->first_line += ydiff;
                        rec->last_line += ydiff;
                        signal_emit("mainwindow moved", 1, rec);
@@ -359,6 +405,14 @@ static void mainwindows_resize_smaller(int xdiff, int ydiff)
 
                mainwindow_resize(rec, xdiff, -space);
        }
 
                mainwindow_resize(rec, xdiff, -space);
        }
+
+       if (xdiff != 0) {
+               while (tmp != NULL) {
+                       mainwindow_resize(tmp->data, xdiff, 0);
+                       tmp = tmp->next;
+               }
+       }
+
        g_slist_free(sorted);
 }
 
        g_slist_free(sorted);
 }
 
@@ -372,8 +426,9 @@ static void mainwindows_resize_bigger(int xdiff, int ydiff)
        for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
        for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-               space = rec->height-WINDOW_MIN_SIZE;
+               space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE;
                if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) {
                if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) {
+                       mainwindow_resize(rec, xdiff, 0);
                        if (moved > 0) {
                                rec->first_line += moved;
                                rec->last_line += moved;
                        if (moved > 0) {
                                rec->first_line += moved;
                                rec->last_line += moved;
@@ -415,12 +470,11 @@ void mainwindows_resize(int width, int height)
 {
        int xdiff, ydiff;
 
 {
        int xdiff, ydiff;
 
-       xdiff = width-screen_width;
-       ydiff = height-screen_height;
-        screen_width = width;
-        screen_height = height;
+       xdiff = width-old_screen_width;
+       ydiff = height-old_screen_height;
+        old_screen_width = width;
+        old_screen_height = height;
 
 
-       screen_refresh_freeze();
        if (ydiff < 0)
                mainwindows_resize_smaller(xdiff, ydiff);
        else if (ydiff > 0)
        if (ydiff < 0)
                mainwindows_resize_smaller(xdiff, ydiff);
        else if (ydiff > 0)
@@ -428,104 +482,197 @@ void mainwindows_resize(int width, int height)
         else if (xdiff != 0)
                mainwindows_resize_horiz(xdiff);
 
         else if (xdiff != 0)
                mainwindows_resize_horiz(xdiff);
 
+        signal_emit("terminal resized", 0);
+
        irssi_redraw();
        irssi_redraw();
-       screen_refresh_thaw();
 }
 
 }
 
-int mainwindows_reserve_lines(int count, int up)
+int mainwindows_reserve_lines(int top, int bottom)
 {
        MAIN_WINDOW_REC *window;
        int ret;
 
 {
        MAIN_WINDOW_REC *window;
        int ret;
 
-       if (up) {
-               g_return_val_if_fail(count > 0 || reserved_up > count, -1);
+        ret = -1;
+       if (top != 0) {
+               g_return_val_if_fail(top > 0 || screen_reserved_top > top, -1);
 
 
-               ret = reserved_up;
-               reserved_up += count;
+               ret = screen_reserved_top;
+               screen_reserved_top += top;
 
                window = mainwindows_find_lower(-1);
 
                window = mainwindows_find_lower(-1);
-               if (window != NULL) window->first_line += count;
-       } else {
-               g_return_val_if_fail(count > 0 || reserved_down > count, -1);
+               if (window != NULL) {
+                       window->first_line += top;
+                       mainwindow_resize(window, 0, -top);
+               }
+       }
 
 
-               ret = reserved_down;
-               reserved_down += count;
+       if (bottom != 0) {
+               g_return_val_if_fail(bottom > 0 || screen_reserved_bottom > bottom, -1);
 
 
-               window = mainwindows_find_upper(screen_height);
-               if (window != NULL) window->last_line -= count;
-       }
+               ret = screen_reserved_bottom;
+               screen_reserved_bottom += bottom;
 
 
-       if (window != NULL)
-               mainwindow_resize(window, 0, -count);
+               window = mainwindows_find_upper(term_height);
+               if (window != NULL) {
+                       window->last_line -= bottom;
+                       mainwindow_resize(window, 0, -bottom);
+               }
+       }
 
        return ret;
 }
 
 
        return ret;
 }
 
+int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window,
+                                  int top, int bottom)
+{
+       int ret;
+
+        ret = -1;
+       if (top != 0) {
+                ret = window->statusbar_lines_top;
+               window->statusbar_lines_top += top;
+                window->statusbar_lines += top;
+       }
+
+       if (bottom != 0) {
+                ret = window->statusbar_lines_bottom;
+                window->statusbar_lines_bottom += bottom;
+                window->statusbar_lines += bottom;
+       }
+
+       if (top+bottom != 0)
+                window->size_dirty = TRUE;
+
+        return ret;
+}
+
 static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win,
                                   MAIN_WINDOW_REC *shrink_win, int count)
 {
 static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win,
                                   MAIN_WINDOW_REC *shrink_win, int count)
 {
+        irssi_set_dirty();
+
        mainwindow_resize(grow_win, 0, count);
        mainwindow_resize(shrink_win, 0, -count);
        mainwindow_resize(grow_win, 0, count);
        mainwindow_resize(shrink_win, 0, -count);
-       gui_window_redraw(grow_win->active);
-       gui_window_redraw(shrink_win->active);
-       statusbar_redraw(grow_win->statusbar);
-       statusbar_redraw(shrink_win->statusbar);
+       grow_win->dirty = TRUE;
+       shrink_win->dirty = TRUE;
 }
 
 }
 
-static int mainwindow_grow(MAIN_WINDOW_REC *window, int count)
+static int try_shrink_lower(MAIN_WINDOW_REC *window, int count)
 {
        MAIN_WINDOW_REC *shrink_win;
 
 {
        MAIN_WINDOW_REC *shrink_win;
 
-       /* shrink lower window */
        shrink_win = mainwindows_find_lower(window->last_line);
        shrink_win = mainwindows_find_lower(window->last_line);
-       if (shrink_win != NULL && shrink_win->height-count >= WINDOW_MIN_SIZE) {
+       if (shrink_win != NULL &&
+           MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) {
                 window->last_line += count;
                shrink_win->first_line += count;
                 window->last_line += count;
                shrink_win->first_line += count;
-       } else {
-               /* shrink upper window */
-               shrink_win = mainwindows_find_upper(window->first_line);
-               if (shrink_win == NULL ||
-                   shrink_win->height-count < WINDOW_MIN_SIZE)
-                       return FALSE;
+               mainwindows_resize_two(window, shrink_win, count);
+                return TRUE;
+       }
 
 
+        return FALSE;
+}
+
+static int try_shrink_upper(MAIN_WINDOW_REC *window, int count)
+{
+       MAIN_WINDOW_REC *shrink_win;
+
+       shrink_win = mainwindows_find_upper(window->first_line);
+       if (shrink_win != NULL &&
+           MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) {
                window->first_line -= count;
                shrink_win->last_line -= count;
                window->first_line -= count;
                shrink_win->last_line -= count;
+               mainwindows_resize_two(window, shrink_win, count);
+                return TRUE;
+       }
+
+        return FALSE;
+}
+
+static int mainwindow_grow(MAIN_WINDOW_REC *window, int count,
+                          int resize_lower)
+{
+       if (!resize_lower || !try_shrink_lower(window, count)) {
+               if (!try_shrink_upper(window, count)) {
+                        if (resize_lower || !try_shrink_lower(window, count))
+                               return FALSE;
+               }
        }
 
        }
 
-       mainwindows_resize_two(window, shrink_win, count);
         return TRUE;
 }
 
         return TRUE;
 }
 
-static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count)
+static int try_grow_lower(MAIN_WINDOW_REC *window, int count)
 {
        MAIN_WINDOW_REC *grow_win;
 
 {
        MAIN_WINDOW_REC *grow_win;
 
-       if (window->height-count < WINDOW_MIN_SIZE)
-                return FALSE;
-
        grow_win = mainwindows_find_lower(window->last_line);
        if (grow_win != NULL) {
                 window->last_line -= count;
                grow_win->first_line -= count;
        grow_win = mainwindows_find_lower(window->last_line);
        if (grow_win != NULL) {
                 window->last_line -= count;
                grow_win->first_line -= count;
-       } else {
-               grow_win = mainwindows_find_upper(window->first_line);
-               if (grow_win == NULL) return FALSE;
+               mainwindows_resize_two(grow_win, window, count);
+       }
 
 
+        return grow_win != NULL;
+}
+
+static int try_grow_upper(MAIN_WINDOW_REC *window, int count)
+{
+       MAIN_WINDOW_REC *grow_win;
+
+       grow_win = mainwindows_find_upper(window->first_line);
+       if (grow_win != NULL) {
                window->first_line += count;
                grow_win->last_line += count;
                window->first_line += count;
                grow_win->last_line += count;
+               mainwindows_resize_two(grow_win, window, count);
+       }
+
+        return grow_win != NULL;
+}
+
+static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count, int resize_lower)
+{
+       if (MAIN_WINDOW_TEXT_HEIGHT(window)-count < WINDOW_MIN_SIZE)
+                return FALSE;
+
+       if (!resize_lower || !try_grow_lower(window, count)) {
+               if (!try_grow_upper(window, count)) {
+                        if (resize_lower || !try_grow_lower(window, count))
+                               return FALSE;
+               }
        }
 
        }
 
-       mainwindows_resize_two(grow_win, window, count);
         return TRUE;
 }
 
         return TRUE;
 }
 
-void mainwindow_set_size(MAIN_WINDOW_REC *window, int size)
+/* Change the window height - the height includes the lines needed for
+   statusbars. If resize_lower is TRUE, the lower window is first tried
+   to be resized instead of upper window. */
+void mainwindow_set_size(MAIN_WINDOW_REC *window, int height, int resize_lower)
 {
 {
-        size -= window->height;
-       if (size < 0)
-               mainwindow_shrink(window, size);
+        height -= window->height;
+       if (height < 0)
+               mainwindow_shrink(window, -height, resize_lower);
        else
        else
-               mainwindow_grow(window, size);
+               mainwindow_grow(window, height, resize_lower);
+}
+
+void mainwindows_redraw_dirty(void)
+{
+       GSList *tmp;
+
+       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+               MAIN_WINDOW_REC *rec = tmp->data;
+
+               if (rec->size_dirty) {
+                        rec->size_dirty = FALSE;
+                       mainwindow_resize_windows(rec);
+               }
+               if (rec->dirty) {
+                        rec->dirty = FALSE;
+                       gui_window_redraw(rec->active);
+               }
+       }
 }
 
 /* SYNTAX: WINDOW GROW [<lines>] */
 }
 
 /* SYNTAX: WINDOW GROW [<lines>] */
@@ -535,9 +682,9 @@ static void cmd_window_grow(const char *data)
        int count;
 
        count = *data == '\0' ? 1 : atoi(data);
        int count;
 
        count = *data == '\0' ? 1 : atoi(data);
-       window = WINDOW_GUI(active_win)->parent;
+       window = WINDOW_MAIN(active_win);
 
 
-       if (!mainwindow_grow(window, count)) {
+       if (!mainwindow_grow(window, count, FALSE)) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_TOO_SMALL);
        }
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_TOO_SMALL);
        }
@@ -546,13 +693,10 @@ static void cmd_window_grow(const char *data)
 /* SYNTAX: WINDOW SHRINK [<lines>] */
 static void cmd_window_shrink(const char *data)
 {
 /* SYNTAX: WINDOW SHRINK [<lines>] */
 static void cmd_window_shrink(const char *data)
 {
-       MAIN_WINDOW_REC *window;
        int count;
 
        count = *data == '\0' ? 1 : atoi(data);
        int count;
 
        count = *data == '\0' ? 1 : atoi(data);
-       window = WINDOW_GUI(active_win)->parent;
-
-       if (!mainwindow_shrink(window, count)) {
+       if (!mainwindow_shrink(WINDOW_MAIN(active_win), count, FALSE)) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_TOO_SMALL);
        }
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_TOO_SMALL);
        }
@@ -567,7 +711,8 @@ static void cmd_window_size(const char *data)
        if (!is_numeric(data, 0)) return;
        size = atoi(data);
 
        if (!is_numeric(data, 0)) return;
        size = atoi(data);
 
-       size -= WINDOW_GUI(active_win)->parent->height;
+       size -= WINDOW_MAIN(active_win)->height -
+               WINDOW_MAIN(active_win)->statusbar_lines;
        if (size == 0) return;
 
        ltoa(sizestr, size < 0 ? -size : size);
        if (size == 0) return;
 
        ltoa(sizestr, size < 0 ? -size : size);
@@ -587,33 +732,32 @@ static void cmd_window_balance(void)
        windows = g_slist_length(mainwindows);
        if (windows == 1) return;
 
        windows = g_slist_length(mainwindows);
        if (windows == 1) return;
 
-       avail_size = screen_height - reserved_up-reserved_down;
+       avail_size = term_height - screen_reserved_top-screen_reserved_bottom;
        unit_size = avail_size/windows;
        bigger_units = avail_size%windows;
 
        sorted = mainwindows_get_sorted(FALSE);
        unit_size = avail_size/windows;
        bigger_units = avail_size%windows;
 
        sorted = mainwindows_get_sorted(FALSE);
-        last_line = 0;
+        last_line = screen_reserved_top;
        for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
                old_size = rec->height;
        for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
                old_size = rec->height;
-               rec->first_line = last_line+1;
-               rec->last_line = rec->first_line-1 + unit_size -
-                       rec->statusbar_lines;
-               rec->height = rec->last_line-rec->first_line+1;
+               rec->first_line = last_line;
+               rec->last_line = rec->first_line + unit_size-1;
 
                if (bigger_units > 0) {
                        rec->last_line++;
                         bigger_units--;
                }
 
                if (bigger_units > 0) {
                        rec->last_line++;
                         bigger_units--;
                }
-               last_line = rec->last_line + rec->statusbar_lines;
+
+               rec->height = rec->last_line-rec->first_line+1;
+               last_line = rec->last_line+1;
 
                mainwindow_resize(rec, 0, rec->height-old_size);
        }
        g_slist_free(sorted);
 
        mainwindows_redraw();
 
                mainwindow_resize(rec, 0, rec->height-old_size);
        }
        g_slist_free(sorted);
 
        mainwindows_redraw();
-       statusbar_redraw(NULL);
 }
 
 /* SYNTAX: WINDOW HIDE [<number>|<name>] */
 }
 
 /* SYNTAX: WINDOW HIDE [<number>|<name>] */
@@ -642,16 +786,16 @@ static void cmd_window_hide(const char *data)
        if (window == NULL || !is_window_visible(window))
                return;
 
        if (window == NULL || !is_window_visible(window))
                return;
 
-       if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
+       if (WINDOW_MAIN(window)->sticky_windows) {
                printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                   TXT_CANT_HIDE_STICKY_WINDOWS);
                 return;
        }
 
                printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                   TXT_CANT_HIDE_STICKY_WINDOWS);
                 return;
        }
 
-       mainwindow_destroy(WINDOW_GUI(window)->parent);
+       mainwindow_destroy(WINDOW_MAIN(window));
 
        if (active_mainwin == NULL) {
 
        if (active_mainwin == NULL) {
-               active_mainwin = WINDOW_GUI(active_win)->parent;
+               active_mainwin = WINDOW_MAIN(active_win);
                 window_set_active(active_mainwin->active);
        }
 }
                 window_set_active(active_mainwin->active);
        }
 }
@@ -677,20 +821,19 @@ static void cmd_window_show(const char *data)
        if (window == NULL || is_window_visible(window))
                return;
 
        if (window == NULL || is_window_visible(window))
                return;
 
-       if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
+       if (WINDOW_MAIN(window)->sticky_windows) {
                printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                   TXT_CANT_SHOW_STICKY_WINDOWS);
                 return;
        }
 
        parent = mainwindow_create();
                printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                   TXT_CANT_SHOW_STICKY_WINDOWS);
                 return;
        }
 
        parent = mainwindow_create();
-       if (settings_get_bool("autostick_split_windows")) {
-               parent->sticky_windows =
-                       g_slist_append(parent->sticky_windows, window);
-       }
-        parent->active = window;
+       parent->active = window;
         gui_window_reparent(window, parent);
 
         gui_window_reparent(window, parent);
 
+       if (settings_get_bool("autostick_split_windows"))
+                gui_window_set_sticky(window);
+
        active_mainwin = NULL;
        window_set_active(window);
 }
        active_mainwin = NULL;
        window_set_active(window);
 }
@@ -701,6 +844,8 @@ static void cmd_window_up(void)
        MAIN_WINDOW_REC *rec;
 
        rec = mainwindows_find_upper(active_mainwin->first_line);
        MAIN_WINDOW_REC *rec;
 
        rec = mainwindows_find_upper(active_mainwin->first_line);
+       if (rec == NULL)
+               rec = mainwindows_find_upper(term_height);
        if (rec != NULL)
                window_set_active(rec->active);
 }
        if (rec != NULL)
                window_set_active(rec->active);
 }
@@ -711,171 +856,220 @@ static void cmd_window_down(void)
        MAIN_WINDOW_REC *rec;
 
        rec = mainwindows_find_lower(active_mainwin->last_line);
        MAIN_WINDOW_REC *rec;
 
        rec = mainwindows_find_lower(active_mainwin->last_line);
+       if (rec == NULL)
+               rec = mainwindows_find_lower(-1);
        if (rec != NULL)
                window_set_active(rec->active);
 }
 
        if (rec != NULL)
                window_set_active(rec->active);
 }
 
+#define WINDOW_STICKY_MATCH(window, sticky_parent) \
+       ((!WINDOW_GUI(window)->sticky && (sticky_parent) == NULL) || \
+        (WINDOW_GUI(window)->sticky && \
+         WINDOW_MAIN(window) == (sticky_parent)))
+
+static int window_refnum_left(int refnum, int wrap)
+{
+        MAIN_WINDOW_REC *find_sticky;
+       WINDOW_REC *window;
+
+       window = window_find_refnum(refnum);
+       g_return_val_if_fail(window != NULL, -1);
+
+       find_sticky = WINDOW_MAIN(window)->sticky_windows ?
+               WINDOW_MAIN(window) : NULL;
+
+       do {
+               refnum = window_refnum_prev(refnum, wrap);
+               if (refnum < 0)
+                       break;
+
+               window = window_find_refnum(refnum);
+       } while (!WINDOW_STICKY_MATCH(window, find_sticky));
+
+        return refnum;
+}
+
+static int window_refnum_right(int refnum, int wrap)
+{
+        MAIN_WINDOW_REC *find_sticky;
+       WINDOW_REC *window;
+
+       window = window_find_refnum(refnum);
+       g_return_val_if_fail(window != NULL, -1);
+
+       find_sticky = WINDOW_MAIN(window)->sticky_windows ?
+               WINDOW_MAIN(window) : NULL;
+
+       do {
+               refnum = window_refnum_next(refnum, wrap);
+               if (refnum < 0)
+                       break;
+
+               window = window_find_refnum(refnum);
+       } while (!WINDOW_STICKY_MATCH(window, find_sticky));
+
+        return refnum;
+}
+
 /* SYNTAX: WINDOW LEFT */
 static void cmd_window_left(const char *data, SERVER_REC *server, void *item)
 {
 /* SYNTAX: WINDOW LEFT */
 static void cmd_window_left(const char *data, SERVER_REC *server, void *item)
 {
-        MAIN_WINDOW_REC *parent;
-        WINDOW_REC *window;
-       int pos, num;
-
-        window = NULL;
-       if (active_mainwin->sticky_windows == NULL) {
-               /* no sticky windows, go to previous non-sticky window */
-                num = active_win->refnum;
-               do {
-                       num = window_refnum_prev(num, TRUE);
-                       if (num < 0) {
-                                window = NULL;
-                               break;
-                       }
-                        window = window_find_refnum(num);
-                       parent = WINDOW_GUI(window)->parent;
-               } while (g_slist_find(parent->sticky_windows, window) != NULL);
-       } else {
-               pos = g_slist_index(active_mainwin->sticky_windows,
-                                   active_win);
-               if (pos > 0) {
-                       window = g_slist_nth_data(
-                                       active_mainwin->sticky_windows, pos-1);
-               } else {
-                       window = g_slist_last(
-                                       active_mainwin->sticky_windows)->data;
-               }
-       }
+       int refnum;
 
 
-        if (window != NULL)
-               window_set_active(window);
+       refnum = window_refnum_left(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_active(window_find_refnum(refnum));
 }
 
 /* SYNTAX: WINDOW RIGHT */
 static void cmd_window_right(void)
 {
 }
 
 /* SYNTAX: WINDOW RIGHT */
 static void cmd_window_right(void)
 {
-        MAIN_WINDOW_REC *parent;
-       WINDOW_REC *window;
-        GSList *tmp;
-       int num;
-
-        window = NULL;
-       if (active_mainwin->sticky_windows == NULL) {
-               /* no sticky windows, go to next non-sticky window */
-                num = active_win->refnum;
-               do {
-                       num = window_refnum_next(num, TRUE);
-                       if (num < 0) {
-                                window = NULL;
-                               break;
-                       }
-                        window = window_find_refnum(num);
-                       parent = WINDOW_GUI(window)->parent;
-               } while (g_slist_find(parent->sticky_windows, window) != NULL);
-       } else {
-               tmp = g_slist_find(active_mainwin->sticky_windows, active_win);
-               if (tmp != NULL) {
-                       window = tmp->next != NULL ? tmp->next->data :
-                               active_mainwin->sticky_windows->data;
-               }
-       }
+       int refnum;
 
 
-        if (window != NULL)
-               window_set_active(window);
+       refnum = window_refnum_right(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_active(window_find_refnum(refnum));
 }
 
 }
 
-static void mainwindow_change_window(MAIN_WINDOW_REC *mainwin,
-                                    WINDOW_REC *window)
+static void window_reparent(WINDOW_REC *win, MAIN_WINDOW_REC *mainwin)
 {
 {
-       MAIN_WINDOW_REC *parent;
-       GSList *tmp;
+       MAIN_WINDOW_REC *old_mainwin;
 
 
-       if (mainwin->sticky_windows != NULL) {
-               /* sticky window */
-               window_set_active(mainwin->sticky_windows->data);
-                return;
-       }
+       old_mainwin = WINDOW_MAIN(win);
 
 
-       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-               WINDOW_REC *rec = tmp->data;
+       if (old_mainwin != mainwin) {
+               gui_window_set_unsticky(win);
 
 
-                parent = WINDOW_GUI(rec)->parent;
-               if (rec != window &&
-                   g_slist_find(parent->sticky_windows, rec) == NULL) {
-                        window_set_active(rec);
-                        return;
+               if (old_mainwin->active == win) {
+                       mainwindow_change_active(old_mainwin, win);
+                       if (active_mainwin == NULL) {
+                               active_mainwin = mainwin;
+                               window_set_active(mainwin->active);
+                       }
                }
                }
-       }
 
 
-        /* no more non-sticky windows, remove main window */
-        mainwindow_destroy(mainwin);
+               gui_window_reparent(win, mainwin);
+               window_set_active(win);
+       }
 }
 
 }
 
-/* SYNTAX: WINDOW STICK [ON|OFF|<ref#>] */
+/* SYNTAX: WINDOW STICK [<ref#>] [ON|OFF] */
 static void cmd_window_stick(const char *data)
 {
 static void cmd_window_stick(const char *data)
 {
-       MAIN_WINDOW_REC *window = active_mainwin;
+        MAIN_WINDOW_REC *mainwin;
+        WINDOW_REC *win;
 
 
-       if (is_numeric(data, '\0')) {
-               WINDOW_REC *win = window_find_refnum(atoi(data));
+        mainwin = active_mainwin;
+        win = active_mainwin->active;
+
+       if (is_numeric(data, ' ')) {
+               /* ref# specified */
+               win = window_find_refnum(atoi(data));
                if (win == NULL) {
                        printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                           TXT_REFNUM_NOT_FOUND, data);
                        return;
                }
                if (win == NULL) {
                        printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                           TXT_REFNUM_NOT_FOUND, data);
                        return;
                }
-                window = WINDOW_GUI(win)->parent;
+
+               while (*data != ' ' && *data != '\0') data++;
+               while (*data == ' ') data++;
        }
 
        }
 
-       if (g_strncasecmp(data, "OF", 2) == 0 || toupper(*data) == 'N') {
+       if (g_strncasecmp(data, "OF", 2) == 0 || i_toupper(*data) == 'N') {
                /* unset sticky */
                /* unset sticky */
-               if (g_slist_find(window->sticky_windows, active_win) == NULL) {
-                       printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+               if (!WINDOW_GUI(win)->sticky) {
+                       printformat_window(win, MSGLEVEL_CLIENTERROR,
                                           TXT_WINDOW_NOT_STICKY);
                } else {
                                           TXT_WINDOW_NOT_STICKY);
                } else {
-                       window->sticky_windows =
-                               g_slist_remove(window->sticky_windows,
-                                              active_win);
-                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                        gui_window_set_unsticky(win);
+                       printformat_window(win, MSGLEVEL_CLIENTNOTICE,
                                           TXT_WINDOW_UNSET_STICKY);
                }
        } else {
                                           TXT_WINDOW_UNSET_STICKY);
                }
        } else {
-                /* set sticky */
-               active_mainwin->sticky_windows =
-                       g_slist_remove(active_mainwin->sticky_windows,
-                                      active_win);
-
-               if (g_slist_find(window->sticky_windows, active_win) == NULL) {
-                       window->sticky_windows =
-                               g_slist_append(window->sticky_windows,
-                                              active_win);
-               }
-               if (window != active_mainwin) {
-                        WINDOW_REC *movewin;
-
-                        movewin = active_win;
-                       gui_window_reparent(movewin, window);
-                        mainwindow_change_window(active_mainwin, movewin);
-
-                       active_mainwin = window;
-                        window_set_active(movewin);
-               }
+               /* set sticky */
+               window_reparent(win, mainwin);
+                gui_window_set_sticky(win);
 
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_SET_STICKY);
        }
 }
 
 
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_SET_STICKY);
        }
 }
 
+/* SYNTAX: WINDOW MOVE LEFT */
+static void cmd_window_move_left(void)
+{
+       int refnum;
+
+       refnum = window_refnum_left(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_refnum(active_win, refnum);
+}
+
+/* SYNTAX: WINDOW MOVE RIGHT */
+static void cmd_window_move_right(void)
+{
+       int refnum;
+
+       refnum = window_refnum_right(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_refnum(active_win, refnum);
+}
+
+/* SYNTAX: WINDOW MOVE UP */
+static void cmd_window_move_up(void)
+{
+       MAIN_WINDOW_REC *rec;
+
+       rec = mainwindows_find_upper(active_mainwin->first_line);
+        if (rec != NULL)
+               window_reparent(active_win, rec);
+}
+
+/* SYNTAX: WINDOW MOVE DOWN */
+static void cmd_window_move_down(void)
+{
+       MAIN_WINDOW_REC *rec;
+
+       rec = mainwindows_find_lower(active_mainwin->last_line);
+       if (rec != NULL)
+               window_reparent(active_win, rec);
+}
+
+static void windows_print_sticky(MAIN_WINDOW_REC *win)
+{
+        GSList *tmp, *list;
+       GString *str;
+
+        /* convert to string */
+       str = g_string_new(NULL);
+       list = get_sticky_windows_sorted(win);
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               g_string_sprintfa(str, "#%d, ", rec->refnum);
+       }
+        g_string_truncate(str, str->len-2);
+        g_slist_free(list);
+
+       printformat_window(win->active, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_STICKY, str->str);
+        g_string_free(str, TRUE);
+}
+
+static void sig_window_print_info(WINDOW_REC *win)
+{
+       if (WINDOW_MAIN(win)->sticky_windows)
+                windows_print_sticky(WINDOW_MAIN(win));
+}
+
 void mainwindows_init(void)
 {
 void mainwindows_init(void)
 {
-       screen_width = COLS;
-       screen_height = LINES;
+       old_screen_width = term_width;
+       old_screen_height = term_height;
 
        mainwindows = NULL;
        active_mainwin = NULL;
 
        mainwindows = NULL;
        active_mainwin = NULL;
-       reserved_up = reserved_down = 0;
-
-       /* for entry line */
-       mainwindows_reserve_lines(1, FALSE);
+       screen_reserved_top = screen_reserved_bottom = 0;
 
        command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow);
        command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink);
 
        command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow);
        command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink);
@@ -888,6 +1082,11 @@ void mainwindows_init(void)
        command_bind("window left", NULL, (SIGNAL_FUNC) cmd_window_left);
        command_bind("window right", NULL, (SIGNAL_FUNC) cmd_window_right);
        command_bind("window stick", NULL, (SIGNAL_FUNC) cmd_window_stick);
        command_bind("window left", NULL, (SIGNAL_FUNC) cmd_window_left);
        command_bind("window right", NULL, (SIGNAL_FUNC) cmd_window_right);
        command_bind("window stick", NULL, (SIGNAL_FUNC) cmd_window_stick);
+       command_bind("window move left", NULL, (SIGNAL_FUNC) cmd_window_move_left);
+       command_bind("window move right", NULL, (SIGNAL_FUNC) cmd_window_move_right);
+       command_bind("window move up", NULL, (SIGNAL_FUNC) cmd_window_move_up);
+       command_bind("window move down", NULL, (SIGNAL_FUNC) cmd_window_move_down);
+        signal_add("window print info", (SIGNAL_FUNC) sig_window_print_info);
 }
 
 void mainwindows_deinit(void)
 }
 
 void mainwindows_deinit(void)
@@ -906,4 +1105,9 @@ void mainwindows_deinit(void)
        command_unbind("window left", (SIGNAL_FUNC) cmd_window_left);
        command_unbind("window right", (SIGNAL_FUNC) cmd_window_right);
        command_unbind("window stick", (SIGNAL_FUNC) cmd_window_stick);
        command_unbind("window left", (SIGNAL_FUNC) cmd_window_left);
        command_unbind("window right", (SIGNAL_FUNC) cmd_window_right);
        command_unbind("window stick", (SIGNAL_FUNC) cmd_window_stick);
+       command_unbind("window move left", (SIGNAL_FUNC) cmd_window_move_left);
+       command_unbind("window move right", (SIGNAL_FUNC) cmd_window_move_right);
+       command_unbind("window move up", (SIGNAL_FUNC) cmd_window_move_up);
+       command_unbind("window move down", (SIGNAL_FUNC) cmd_window_move_down);
+        signal_remove("window print info", (SIGNAL_FUNC) sig_window_print_info);
 }
 }
index 491449c62c9ad61f12e48abcbca53820114080de..1bca333d128d7d9781716e4372482d581a01ef18 100644 (file)
@@ -2,27 +2,34 @@
 #define __MAINWINDOWS_H
 
 #include "fe-windows.h"
 #define __MAINWINDOWS_H
 
 #include "fe-windows.h"
-#include "screen.h"
+#include "term.h"
 
 #define WINDOW_MIN_SIZE 2
 
 
 #define WINDOW_MIN_SIZE 2
 
+#define MAIN_WINDOW_TEXT_HEIGHT(window) \
+        ((window)->height-(window)->statusbar_lines)
+
 typedef struct {
        WINDOW_REC *active;
 typedef struct {
        WINDOW_REC *active;
-        GSList *sticky_windows; /* list of windows allowed to show only in this mainwindow */
 
 
-#ifdef USE_CURSES_WINDOWS
-       WINDOW *curses_win;
-#else
-#error disable-curses-windows is currently broken /* FIXME */
-#endif
-       int first_line, last_line, width, height;
-       int statusbar_lines;
-       void *statusbar;
-       void *statusbar_window_item;
+       TERM_WINDOW *screen_win;
+        int sticky_windows; /* number of sticky windows */
+
+       int first_line, last_line; /* first/last line used by this window (0..x) (includes statusbars) */
+       int width, height; /* width/height of the window (includes statusbars) */
+
+       GSList *statusbars;
+       int statusbar_lines_top;
+       int statusbar_lines_bottom;
+       int statusbar_lines; /* top+bottom */
+
+       unsigned int dirty:1; /* This window needs a redraw */
+       unsigned int size_dirty:1; /* We'll need to resize the window, but haven't got around doing it just yet. */
 } MAIN_WINDOW_REC;
 
 extern GSList *mainwindows;
 extern MAIN_WINDOW_REC *active_mainwin;
 } MAIN_WINDOW_REC;
 
 extern GSList *mainwindows;
 extern MAIN_WINDOW_REC *active_mainwin;
+extern int screen_reserved_top, screen_reserved_bottom;
 
 void mainwindows_init(void);
 void mainwindows_deinit(void);
 
 void mainwindows_init(void);
 void mainwindows_deinit(void);
@@ -33,10 +40,21 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window);
 void mainwindows_redraw(void);
 void mainwindows_recreate(void);
 
 void mainwindows_redraw(void);
 void mainwindows_recreate(void);
 
-void mainwindow_set_size(MAIN_WINDOW_REC *window, int size);
+/* Change the window height - the height includes the lines needed for
+   statusbars. If resize_lower is TRUE, the lower window is first tried
+   to be resized instead of upper window. */
+void mainwindow_set_size(MAIN_WINDOW_REC *window, int height,
+                        int resize_lower);
 void mainwindows_resize(int width, int height);
 
 void mainwindows_resize(int width, int height);
 
-int mainwindows_reserve_lines(int count, int up);
+void mainwindow_change_active(MAIN_WINDOW_REC *mainwin,
+                             WINDOW_REC *skip_window);
+
+int mainwindows_reserve_lines(int top, int bottom);
+int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window,
+                                  int top, int bottom);
+void mainwindows_redraw_dirty(void);
+
 GSList *mainwindows_get_sorted(int reverse);
 
 #endif
 GSList *mainwindows_get_sorted(int reverse);
 
 #endif
index be3c0eab62d198b2cf44806c08bcc24334379c5a..4a37db25e643f32a9665c878768d75e00ee2db37 100644 (file)
@@ -26,8 +26,10 @@ FORMAT_REC gui_text_formats[] =
        { MODULE_NAME, "Text user interface", 0 },
 
        { "lastlog_too_long", "/LASTLOG would print $0 lines. If you really want to print all these lines use -force option.", 1, { 1 } },
        { MODULE_NAME, "Text user interface", 0 },
 
        { "lastlog_too_long", "/LASTLOG would print $0 lines. If you really want to print all these lines use -force option.", 1, { 1 } },
+       { "lastlog_count", "{hilight Lastlog}: $0 lines", 1, { 1 } },
        { "lastlog_start", "{hilight Lastlog}:", 0 },
        { "lastlog_end", "{hilight End of Lastlog}", 0 },
        { "lastlog_start", "{hilight Lastlog}:", 0 },
        { "lastlog_end", "{hilight End of Lastlog}", 0 },
+       { "lastlog_separator", "--", 0 },
 
        { "refnum_not_found", "Window number $0 not found", 1, { 0 } },
        { "window_too_small", "Not enough room to resize this window", 0 },
 
        { "refnum_not_found", "Window number $0 not found", 1, { 0 } },
        { "window_too_small", "Not enough room to resize this window", 0 },
@@ -37,6 +39,9 @@ FORMAT_REC gui_text_formats[] =
        { "window_not_sticky", "Window is not sticky", 0 },
        { "window_set_sticky", "Window set sticky", 0 },
        { "window_unset_sticky", "Window is not sticky anymore", 0 },
        { "window_not_sticky", "Window is not sticky", 0 },
        { "window_set_sticky", "Window set sticky", 0 },
        { "window_unset_sticky", "Window is not sticky anymore", 0 },
+       { "window_info_sticky", "Sticky  : $0", 1, { 0 } },
+       { "window_scroll", "Window scroll mode is now $0", 1, { 0 } },
+       { "window_scroll_unknown", "Unknown scroll mode $0, must be ON, OFF or DEFAULT", 1, { 0 } },
 
        { NULL, NULL, 0 }
 };
 
        { NULL, NULL, 0 }
 };
index fe33c8e269e34b6021ab8f7088a49a1048f90cd5..2bbf4007260498402e565e5960ed2fa20ae7d133 100644 (file)
@@ -4,8 +4,10 @@ enum {
        TXT_MODULE_NAME,
 
         TXT_LASTLOG_TOO_LONG,
        TXT_MODULE_NAME,
 
         TXT_LASTLOG_TOO_LONG,
+        TXT_LASTLOG_COUNT,
        TXT_LASTLOG_START,
        TXT_LASTLOG_END,
        TXT_LASTLOG_START,
        TXT_LASTLOG_END,
+       TXT_LASTLOG_SEPARATOR,
 
         TXT_REFNUM_NOT_FOUND,
         TXT_WINDOW_TOO_SMALL,
 
         TXT_REFNUM_NOT_FOUND,
         TXT_WINDOW_TOO_SMALL,
@@ -14,7 +16,10 @@ enum {
         TXT_CANT_SHOW_STICKY_WINDOWS,
         TXT_WINDOW_NOT_STICKY,
         TXT_WINDOW_SET_STICKY,
         TXT_CANT_SHOW_STICKY_WINDOWS,
         TXT_WINDOW_NOT_STICKY,
         TXT_WINDOW_SET_STICKY,
-        TXT_WINDOW_UNSET_STICKY
+       TXT_WINDOW_UNSET_STICKY,
+        TXT_WINDOW_INFO_STICKY,
+        TXT_WINDOW_SCROLL,
+        TXT_WINDOW_SCROLL_UNKNOWN
 };
 
 extern FORMAT_REC gui_text_formats[];
 };
 
 extern FORMAT_REC gui_text_formats[];
index ba5888adebc69132cd4f8d06d8842dbbf24f9bad..c66435453654b6ca3de44506af85712a6ea1a68e 100644 (file)
@@ -4,3 +4,4 @@
 
 extern int quitting;
 void irssi_redraw(void);
 
 extern int quitting;
 void irssi_redraw(void);
+void irssi_set_dirty(void);
index 32ec6bd2082f7b281e2d5a72385c17b5515448ef..7e296dccac4edc07a8a6372963a250331611beb4 100644 (file)
 
 #include "module.h"
 #include "module-formats.h"
 
 #include "module.h"
 #include "module-formats.h"
+#include "modules-load.h"
 #include "args.h"
 #include "signals.h"
 #include "levels.h"
 #include "core.h"
 #include "settings.h"
 #include "args.h"
 #include "signals.h"
 #include "levels.h"
 #include "core.h"
 #include "settings.h"
+#include "session.h"
 
 #include "printtext.h"
 #include "fe-common-core.h"
 #include "fe-common-silc.h"
 #include "themes.h"
 
 
 #include "printtext.h"
 #include "fe-common-core.h"
 #include "fe-common-silc.h"
 #include "themes.h"
 
-#include "screen.h"
+#include "term.h"
 #include "gui-entry.h"
 #include "mainwindows.h"
 #include "gui-printtext.h"
 #include "gui-readline.h"
 #include "statusbar.h"
 #include "gui-windows.h"
 #include "gui-entry.h"
 #include "mainwindows.h"
 #include "gui-printtext.h"
 #include "gui-readline.h"
 #include "statusbar.h"
 #include "gui-windows.h"
+#include "textbuffer-reformat.h"
 
 #include <signal.h>
 
 
 #include <signal.h>
 
@@ -64,8 +67,13 @@ void lastlog_deinit(void);
 void mainwindow_activity_init(void);
 void mainwindow_activity_deinit(void);
 
 void mainwindow_activity_init(void);
 void mainwindow_activity_deinit(void);
 
-void mainwindows_save_init(void);
-void mainwindows_save_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 GMainLoop *main_loop;
 int quitting;
 
 static GMainLoop *main_loop;
 int quitting;
@@ -89,25 +97,51 @@ static void sig_exit(void)
 /* redraw irssi's screen.. */
 void irssi_redraw(void)
 {
 /* redraw irssi's screen.. */
 void irssi_redraw(void)
 {
-       clear();
-       refresh();
-
-       /* windows */
-        mainwindows_redraw();
-       /* statusbar */
-       statusbar_redraw(NULL);
-       /* entry line */
-       gui_entry_redraw();
+       dirty = TRUE;
+        full_redraw = TRUE;
+}
+
+void irssi_set_dirty(void)
+{
+        dirty = TRUE;
+}
+
+static void dirty_check(void)
+{
+       if (!dirty || dummy)
+               return;
+
+        term_resize_dirty();
+
+       if (full_redraw) {
+                full_redraw = FALSE;
+
+               /* first clear the screen so curses will be
+                  forced to redraw the screen */
+               term_clear();
+               term_refresh(NULL);
+
+               mainwindows_redraw();
+               statusbar_redraw(NULL, TRUE);
+       }
+
+       mainwindows_redraw_dirty();
+        statusbar_redraw_dirty();
+       term_refresh(NULL);
+
+        dirty = FALSE;
 }
 
 static void textui_init(void)
 {
 }
 
 static void textui_init(void)
 {
-       static struct poptOption options[] = {
-               POPT_AUTOHELP
-               { NULL, '\0', 0, NULL }
-       };
+#ifdef SIGTRAP
+       struct sigaction act;
 
 
-       args_register(options);
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       act.sa_handler = SIG_IGN;
+       sigaction(SIGTRAP, &act, NULL);
+#endif
 
        irssi_gui = IRSSI_GUI_TEXT;
        core_init();
 
        irssi_gui = IRSSI_GUI_TEXT;
        core_init();
@@ -116,36 +150,44 @@ static void textui_init(void)
        fe_common_silc_init();
 
        theme_register(gui_text_formats);
        fe_common_silc_init();
 
        theme_register(gui_text_formats);
-       signal_add("gui exit", (SIGNAL_FUNC) sig_exit);
+       signal_add_last("gui exit", (SIGNAL_FUNC) sig_exit);
 }
 
 static void textui_finish_init(void)
 {
        quitting = FALSE;
 
 }
 
 static void textui_finish_init(void)
 {
        quitting = FALSE;
 
-       screen_refresh_freeze();
-        textbuffer_init();
-        textbuffer_view_init();
-       textbuffer_commands_init();
-       gui_entry_init();
-       gui_expandos_init();
-       gui_printtext_init();
-       gui_readline_init();
-        lastlog_init();
-       mainwindows_init();
-       mainwindow_activity_init();
-       mainwindows_save_init();
-       gui_windows_init();
-       statusbar_init();
+       if (dummy)
+               term_dummy_init();
+       else {
+               term_refresh_freeze();
+               textbuffer_init();
+               textbuffer_view_init();
+               textbuffer_commands_init();
+               textbuffer_reformat_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();
 
        settings_check();
-
-       fe_common_core_finish_init();
+       module_register("core", "fe-text");
 
 #ifdef HAVE_STATIC_PERL
        perl_core_init();
 
 #ifdef HAVE_STATIC_PERL
        perl_core_init();
-        fe_perl_init();
+       fe_perl_init();
 #endif
 #endif
+
+       dirty_check();
+
+       fe_common_core_finish_init();
        signal_emit("irssi init finished", 0);
 
 #if 0
        signal_emit("irssi init finished", 0);
 
 #if 0
@@ -154,42 +196,45 @@ static void textui_finish_init(void)
                                 "%s", firsttimer_text);
        }
 #endif
                                 "%s", firsttimer_text);
        }
 #endif
-
-       screen_refresh_thaw();
 }
 
 static void textui_deinit(void)
 {
        signal(SIGINT, SIG_DFL);
 
 }
 
 static void textui_deinit(void)
 {
        signal(SIGINT, SIG_DFL);
 
-        screen_refresh_freeze();
+        term_refresh_freeze();
        while (modules != NULL)
                module_unload(modules->data);
 
        while (modules != NULL)
                module_unload(modules->data);
 
-       signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
-
-        lastlog_deinit();
-       statusbar_deinit();
-       gui_printtext_deinit();
-       gui_readline_deinit();
-       gui_windows_deinit();
-       mainwindows_save_deinit();
-       mainwindow_activity_deinit();
-       mainwindows_deinit();
-       gui_expandos_deinit();
-       gui_entry_deinit();
-       textbuffer_commands_deinit();
-        textbuffer_view_deinit();
-        textbuffer_deinit();
-
-        screen_refresh_thaw();
-       deinit_screen();
-
 #ifdef HAVE_STATIC_PERL
 #ifdef HAVE_STATIC_PERL
-        fe_perl_deinit();
         perl_core_deinit();
         perl_core_deinit();
+        fe_perl_deinit();
 #endif
 
 #endif
 
+        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_reformat_deinit();
+               textbuffer_commands_deinit();
+               textbuffer_view_deinit();
+               textbuffer_deinit();
+
+               term_refresh_thaw();
+               term_deinit();
+       }
+
        theme_unregister();
 
        fe_common_silc_deinit();
        theme_unregister();
 
        fe_common_silc_deinit();
@@ -205,7 +250,7 @@ static void check_oldcrap(void)
         int found;
 
         /* check that default.theme is up-to-date */
         int found;
 
         /* check that default.theme is up-to-date */
-       path = g_strdup_printf("%s/.silc/default.theme", g_get_home_dir());
+       path = g_strdup_printf("%s/default.theme", get_irssi_dir());
        f = fopen(path, "r+");
        if (f == NULL) {
                g_free(path);
        f = fopen(path, "r+");
        if (f == NULL) {
                g_free(path);
@@ -221,7 +266,7 @@ static void check_oldcrap(void)
                return;
        }
 
                return;
        }
 
-       printf("\nYou seem to have old default.theme in ~/.silc/ directory.\n");
+       printf("\nYou seem to have old default.theme in "IRSSI_DIR_SHORT"/ directory.\n");
         printf("Themeing system has changed a bit since last irssi release,\n");
         printf("you should either delete your old default.theme or manually\n");
         printf("merge it with the new default.theme.\n\n");
         printf("Themeing system has changed a bit since last irssi release,\n");
         printf("you should either delete your old default.theme or manually\n");
         printf("merge it with the new default.theme.\n\n");
@@ -229,7 +274,7 @@ static void check_oldcrap(void)
 
        str[0] = '\0';
        fgets(str, sizeof(str), stdin);
 
        str[0] = '\0';
        fgets(str, sizeof(str), stdin);
-       if (toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0')
+       if (i_toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0')
                 remove(path);
        g_free(path);
 }
                 remove(path);
        g_free(path);
 }
@@ -237,16 +282,13 @@ static void check_oldcrap(void)
 static void check_files(void)
 {
        struct stat statbuf;
 static void check_files(void)
 {
        struct stat statbuf;
-        char *path;
 
 
-        path = g_strdup_printf("%s/.silc", g_get_home_dir());
-       if (stat(path, &statbuf) != 0) {
+       if (stat(get_irssi_dir(), &statbuf) != 0) {
                /* ~/.irssi doesn't exist, first time running irssi */
                display_firsttimer = TRUE;
        } else {
                 check_oldcrap();
        }
                /* ~/.irssi doesn't exist, first time running irssi */
                display_firsttimer = TRUE;
        } else {
                 check_oldcrap();
        }
-        g_free(path);
 }
 
 #ifdef WIN32
 }
 
 #ifdef WIN32
@@ -266,7 +308,17 @@ static void winsock_init(void)
 
 int main(int argc, char **argv)
 {
 
 int main(int argc, char **argv)
 {
+       static struct poptOption options[] = {
+               { "dummy", 'd', POPT_ARG_NONE, &dummy, 0, "Use the dummy terminal mode", NULL },
+               { NULL, '\0', 0, NULL }
+       };
+
+       dummy = FALSE;
+       quitting = FALSE;
+       core_init_paths(argc, argv);
+
        check_files();
        check_files();
+
 #ifdef WIN32
         winsock_init();
 #endif
 #ifdef WIN32
         winsock_init();
 #endif
@@ -279,29 +331,33 @@ int main(int argc, char **argv)
        textdomain(PACKAGE);
 #endif
 
        textdomain(PACKAGE);
 #endif
 
-       quitting = FALSE;
-
        textui_init();
        textui_init();
+       args_register(options);
        args_execute(argc, argv);
        args_execute(argc, argv);
+
+#if 0
        silc_init_finish();
        silc_init_finish();
+#endif
 
 
-       if (!init_screen())
-               g_error("Can't initialize screen handling, quitting.\n");
+       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");
+               return 1;
+       }
 
        textui_finish_init();
        main_loop = g_main_new(TRUE);
 
 
        textui_finish_init();
        main_loop = g_main_new(TRUE);
 
+       /* Does the same as g_main_run(main_loop), except we
+          can call our dirty-checker after each iteration */
        while (!quitting) {
                g_main_iteration(TRUE);
        while (!quitting) {
                g_main_iteration(TRUE);
-                screen_check_resizes();
+                dirty_check();
        }
 
        g_main_destroy(main_loop);
        textui_deinit();
 
        }
 
        g_main_destroy(main_loop);
        textui_deinit();
 
-#ifdef MEM_DEBUG
-       ig_mem_profile();
-#endif
-
+        session_upgrade(); /* if we /UPGRADEd, start the new process */
        return 0;
 }
        return 0;
 }
diff --git a/apps/irssi/src/fe-text/statusbar-config.c b/apps/irssi/src/fe-text/statusbar-config.c
new file mode 100644 (file)
index 0000000..f9ea05f
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ statusbar-config.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "settings.h"
+#include "lib-config/iconfig.h"
+
+#include "statusbar.h"
+
+static void read_statusbar_config_from_node(CONFIG_NODE *node);
+
+static STATUSBAR_CONFIG_REC *
+statusbar_config_create(STATUSBAR_GROUP_REC *group, const char *name)
+{
+       STATUSBAR_CONFIG_REC *bar;
+
+        g_return_val_if_fail(group != NULL, NULL);
+        g_return_val_if_fail(name != NULL, NULL);
+
+       bar = g_new0(STATUSBAR_CONFIG_REC, 1);
+       group->config_bars = g_slist_append(group->config_bars, bar);
+
+       bar->name = g_strdup(name);
+       return bar;
+}
+
+static SBAR_ITEM_CONFIG_REC *
+statusbar_item_config_create(STATUSBAR_CONFIG_REC *bar, const char *name,
+                            int priority, int right_alignment)
+{
+       SBAR_ITEM_CONFIG_REC *rec;
+
+       g_return_val_if_fail(bar != NULL, NULL);
+       g_return_val_if_fail(name != NULL, NULL);
+
+       rec = g_new0(SBAR_ITEM_CONFIG_REC, 1);
+       bar->items = g_slist_append(bar->items, rec);
+
+        rec->name = g_strdup(name);
+       rec->priority = priority;
+        rec->right_alignment = right_alignment;
+
+       return rec;
+}
+
+static void statusbar_config_item_destroy(STATUSBAR_CONFIG_REC *barconfig,
+                                         SBAR_ITEM_CONFIG_REC *itemconfig)
+{
+       barconfig->items = g_slist_remove(barconfig->items, itemconfig);
+
+       g_free(itemconfig->name);
+        g_free(itemconfig);
+}
+
+void statusbar_config_destroy(STATUSBAR_GROUP_REC *group,
+                             STATUSBAR_CONFIG_REC *config)
+{
+       group->config_bars = g_slist_remove(group->config_bars, config);
+
+       while (config->items != NULL)
+               statusbar_config_item_destroy(config, config->items->data);
+
+       g_free(config->name);
+        g_free(config);
+}
+
+static STATUSBAR_CONFIG_REC *
+statusbar_config_find(STATUSBAR_GROUP_REC *group, const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_CONFIG_REC *config = tmp->data;
+
+               if (strcmp(config->name, name) == 0)
+                        return config;
+       }
+
+        return NULL;
+}
+
+static void statusbar_reset_defaults(void)
+{
+       CONFIG_REC *config;
+        CONFIG_NODE *node;
+
+       while (statusbar_groups != NULL)
+               statusbar_group_destroy(statusbar_groups->data);
+       active_statusbar_group = NULL;
+
+        /* read the default statusbar settings from internal config */
+       config = config_open(NULL, -1);
+       config_parse_data(config, default_config, "internal");
+       node = config_node_traverse(config, "statusbar", FALSE);
+        if (node != NULL)
+               read_statusbar_config_from_node(node);
+        config_close(config);
+}
+
+static void statusbar_read_items(CONFIG_NODE *items)
+{
+       GSList *tmp;
+
+       tmp = config_node_first(items->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               CONFIG_NODE *node = tmp->data;
+
+               statusbar_item_register(node->key, node->value, NULL);
+       }
+}
+
+static void statusbar_read_item(STATUSBAR_CONFIG_REC *bar, CONFIG_NODE *node)
+{
+       int priority, right_alignment;
+
+       priority = config_node_get_int(node, "priority", 0);
+       right_alignment = strcmp(config_node_get_str(node, "alignment", ""), "right") == 0;
+       statusbar_item_config_create(bar, node->key,
+                                    priority, right_alignment);
+}
+
+static void statusbar_read(STATUSBAR_GROUP_REC *group, CONFIG_NODE *node)
+{
+       STATUSBAR_CONFIG_REC *bar;
+        GSList *tmp;
+       int visible;
+        const char *visible_str;
+
+       bar = statusbar_config_find(group, node->key);
+       visible = bar == NULL ? STATUSBAR_VISIBLE_ALWAYS : bar->visible;
+
+        /* first make sure we want to see this statusbar */
+        visible_str = config_node_get_str(node, "visible", "");
+       if (strcmp(visible_str, "active") == 0)
+                visible = STATUSBAR_VISIBLE_ACTIVE;
+       else if (strcmp(visible_str, "inactive") == 0)
+               visible = STATUSBAR_VISIBLE_INACTIVE;
+       else if (strcmp(visible_str, "never") == 0) {
+               /* we don't want this statusbar -
+                  destroy it if it already exists */
+               if (bar != NULL)
+                       statusbar_config_destroy(group, bar);
+                return;
+       }
+
+       if (bar == NULL) {
+               bar = statusbar_config_create(group, node->key);
+               bar->type = STATUSBAR_TYPE_ROOT;
+               bar->placement = STATUSBAR_BOTTOM;
+               bar->position = 0;
+       }
+       bar->visible = visible;
+
+       if (strcmp(config_node_get_str(node, "type", ""), "window") == 0)
+                bar->type = STATUSBAR_TYPE_WINDOW;
+       if (strcmp(config_node_get_str(node, "placement", ""), "top") == 0)
+                bar->placement = STATUSBAR_TOP;
+       bar->position = config_node_get_int(node, "position", 0);
+
+       node = config_node_section(node, "items", -1);
+       if (node != NULL) {
+                /* we're overriding the items - destroy the old */
+                while (bar->items != NULL)
+                       statusbar_config_item_destroy(bar, bar->items->data);
+
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       statusbar_read_item(bar, tmp->data);
+       }
+}
+
+static void statusbar_read_group(CONFIG_NODE *node)
+{
+       STATUSBAR_GROUP_REC *group;
+       GSList *tmp;
+
+       group = statusbar_group_find(node->key);
+       if (group == NULL) {
+               group = statusbar_group_create(node->key);
+               if (active_statusbar_group == NULL)
+                       active_statusbar_group = group;
+       }
+
+        tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp))
+               statusbar_read(group, tmp->data);
+}
+
+static void create_root_statusbars(void)
+{
+        STATUSBAR_REC *bar;
+       GSList *tmp;
+
+        tmp = active_statusbar_group->config_bars;
+       for (; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_CONFIG_REC *rec = tmp->data;
+
+               if (rec->type == STATUSBAR_TYPE_ROOT) {
+                       bar = statusbar_create(active_statusbar_group, rec, NULL);
+                        statusbar_redraw(bar, TRUE);
+               }
+       }
+}
+
+static void read_statusbar_config_from_node(CONFIG_NODE *node)
+{
+       CONFIG_NODE *items;
+       GSList *tmp;
+
+       items = config_node_section(node, "items", -1);
+       if (items != NULL)
+               statusbar_read_items(items);
+
+        tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               if (tmp->data != items)
+                       statusbar_read_group(tmp->data);
+       }
+}
+
+static void read_statusbar_config(void)
+{
+       CONFIG_NODE *node;
+
+        statusbar_reset_defaults();
+
+       node = iconfig_node_traverse("statusbar", FALSE);
+       if (node != NULL)
+               read_statusbar_config_from_node(node);
+
+        create_root_statusbars();
+}
+
+void statusbar_config_init(void)
+{
+        read_statusbar_config();
+       signal_add("setup reread", (SIGNAL_FUNC) read_statusbar_config);
+}
+
+void statusbar_config_deinit(void)
+{
+       signal_remove("setup reread", (SIGNAL_FUNC) read_statusbar_config);
+}
diff --git a/apps/irssi/src/fe-text/statusbar-config.h b/apps/irssi/src/fe-text/statusbar-config.h
new file mode 100644 (file)
index 0000000..a2a0b04
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __STATUSBAR_CONFIG_H
+#define __STATUSBAR_CONFIG_H
+
+#include "statusbar.h"
+
+void statusbar_config_destroy(STATUSBAR_GROUP_REC *group,
+                             STATUSBAR_CONFIG_REC *config);
+
+void statusbar_config_init(void);
+void statusbar_config_deinit(void);
+
+#endif
index 137e7417069ad739ff33c529b97a8b29d1645326..ac98c77a8bc46ce29d10ab47d70f342218a8e99f 100644 (file)
 
 #include "module.h"
 #include "signals.h"
 
 #include "module.h"
 #include "signals.h"
-#include "misc.h"
 #include "settings.h"
 #include "settings.h"
-#include "special-vars.h"
-
-#include "window-items.h"
-#include "formats.h"
+#include "servers.h"
 
 
+#include "themes.h"
 #include "statusbar.h"
 #include "statusbar.h"
-#include "gui-printtext.h"
+#include "gui-entry.h"
+#include "gui-windows.h"
 
 /* how often to redraw lagging time (seconds) */
 #define LAG_REFRESH_TIME 10
 
 
 /* how often to redraw lagging time (seconds) */
 #define LAG_REFRESH_TIME 10
 
-/* how often to check for new mail (seconds) */
-#define MAIL_REFRESH_TIME 60
-
-/* If we haven't been able to check lag for this long, "(??)" is added after
-   the lag */
-#define MAX_LAG_UNKNOWN_TIME 30
-
-static STATUSBAR_REC *mainbar;
-static MAIN_WINDOW_REC *mainbar_window;
-static int use_colors;
-
-/* clock */
-static SBAR_ITEM_REC *clock_item;
-static int clock_timetag;
-static time_t clock_last;
-
-/* nick */
-static SBAR_ITEM_REC *nick_item;
-
-/* channel */
-static SBAR_ITEM_REC *window_item;
-
-/* activity */
-static SBAR_ITEM_REC *activity_item;
 static GList *activity_list;
 static GList *activity_list;
+static GSList *more_visible; /* list of MAIN_WINDOW_RECs which have --more-- */
+static GHashTable *input_entries;
+static int last_lag, last_lag_unknown, lag_timeout_tag;
 
 
-/* more */
-static SBAR_ITEM_REC *more_item;
-
-/* lag */
-static SBAR_ITEM_REC *lag_item;
-static int lag_timetag, lag_min_show;
-static time_t lag_last_draw;
-
-/* mbox counter */
-static SBAR_ITEM_REC *mail_item;
-static int mail_timetag, mail_last_count;
-static time_t mail_last_mtime = -1;
-static off_t mail_last_size = -1;
-
-/* topic */
-static SBAR_ITEM_REC *topic_item;
-static STATUSBAR_REC *topic_bar;
-
-static void item_default(SBAR_ITEM_REC *item, int get_size_only,
-                        const char *str, const char *data)
-{
-       SERVER_REC *server;
-       WI_ITEM_REC *wiitem;
-        char *tmpstr, *tmpstr2;
-       int len;
-
-       if (active_win == NULL) {
-               server = NULL;
-                wiitem = NULL;
-       } else {
-               server = active_win->active_server;
-                wiitem = active_win->active;
-       }
-
-       /* expand $variables */
-       tmpstr = parse_special_string(str, server, wiitem, data, NULL,
-                                     PARSE_FLAG_ESCAPE_VARS);
-
-       /* expand templates */
-        str = tmpstr;
-       tmpstr2 = theme_format_expand_data(current_theme, &str,
-                                          'n', '0' + item->bar->color,
-                                          NULL, NULL,
-                                          EXPAND_FLAG_ROOT |
-                                          EXPAND_FLAG_IGNORE_REPLACES |
-                                          EXPAND_FLAG_IGNORE_EMPTY);
-       g_free(tmpstr);
-
-       /* remove color codes */
-       tmpstr = strip_codes(tmpstr2);
-        g_free(tmpstr2);
-
-       if (get_size_only) {
-               item->min_size = item->max_size = format_get_length(tmpstr);
-       } else {
-               if (item->size < item->min_size) {
-                        /* they're forcing us smaller than minimum size.. */
-                       len = format_real_length(tmpstr, item->size);
-                        tmpstr[len] = '\0';
-               }
-
-               tmpstr2 = g_strconcat(item->bar->color_string, tmpstr, NULL);
-               gui_printtext(item->xpos, item->bar->ypos, tmpstr2);
-                g_free(tmpstr2);
-       }
-       g_free(tmpstr);
-}
-
-/* redraw clock */
-static void statusbar_clock(SBAR_ITEM_REC *item, int get_size_only)
+static void item_window_active(SBAR_ITEM_REC *item, int get_size_only)
 {
 {
-       item_default(item, get_size_only, "{sb $Z}", "");
-}
-
-/* check if we need to redraw clock.. */
-static int statusbar_clock_timeout(void)
-{
-       struct tm *tm;
-       time_t t;
-       int min;
-
-       tm = localtime(&clock_last);
-       min = tm->tm_min;
+       WINDOW_REC *window;
 
 
-       t = time(NULL);
-       tm = localtime(&t);
+        window = active_win;
+       if (item->bar->parent_window != NULL)
+               window = item->bar->parent_window->active;
 
 
-       if (tm->tm_min != min) {
-               /* minute changed, redraw! */
-                clock_last = t;
-               statusbar_item_redraw(clock_item);
+       if (window != NULL && window->active != NULL) {
+               statusbar_item_default_handler(item, get_size_only,
+                                              NULL, "", TRUE);
+       } else if (get_size_only) {
+                item->min_size = item->max_size = 0;
        }
        }
-       return 1;
 }
 
 }
 
-/* redraw nick */
-static void statusbar_nick(SBAR_ITEM_REC *item, int get_size_only)
+static void item_window_empty(SBAR_ITEM_REC *item, int get_size_only)
 {
 {
-       item_default(item, get_size_only,
-                    "{sb $P$N{sbmode $usermode}{sbaway $A}}", "");
-}
+       WINDOW_REC *window;
 
 
-static void sig_statusbar_nick_redraw(void)
-{
-       statusbar_item_redraw(nick_item);
-}
+        window = active_win;
+       if (item->bar->parent_window != NULL)
+               window = item->bar->parent_window->active;
 
 
-/* redraw window */
-static void statusbar_window(SBAR_ITEM_REC *item, int get_size_only)
-{
-       if (active_win->active != NULL) {
-               item_default(item, get_size_only,
-                            "{sb $winref:$T{sbmode $M}}", "");
-       } else {
-               item_default(item, get_size_only,
-                            "{sb $winref{sbservertag $tag}}", "");
+       if (window != NULL && window->active == NULL) {
+               statusbar_item_default_handler(item, get_size_only,
+                                              NULL, "", TRUE);
+       } else if (get_size_only) {
+                item->min_size = item->max_size = 0;
        }
 }
 
        }
 }
 
-static void sig_statusbar_window_redraw(void)
-{
-       statusbar_item_redraw(window_item);
-}
-
-static void sig_statusbar_window_redraw_window(WINDOW_REC *window)
-{
-       if (is_window_visible(window))
-               statusbar_item_redraw(window_item);
-}
-
-static void sig_statusbar_window_redraw_window_item(WI_ITEM_REC *item)
-{
-       WINDOW_REC *window;
-
-        window = window_item_window(item);
-       if (window->active == item && is_window_visible(window))
-               statusbar_item_redraw(window_item);
-}
-
-static char *get_activity_list(int normal, int hilight)
+static char *get_activity_list(MAIN_WINDOW_REC *window, int normal, int hilight)
 {
 {
+        THEME_REC *theme;
        GString *str;
        GList *tmp;
        GString *str;
        GList *tmp;
-        char *ret;
+        char *ret, *name, *format, *value;
         int is_det;
 
        str = g_string_new(NULL);
 
         int is_det;
 
        str = g_string_new(NULL);
 
+       theme = window != NULL && window->active != NULL &&
+               window->active->theme != NULL ?
+               window->active->theme : current_theme;
+
        for (tmp = activity_list; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *window = tmp->data;
 
        for (tmp = activity_list; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *window = tmp->data;
 
@@ -214,26 +89,41 @@ static char *get_activity_list(int normal, int hilight)
                if ((!is_det && !normal) || (is_det && !hilight))
                         continue;
 
                if ((!is_det && !normal) || (is_det && !hilight))
                         continue;
 
-               g_string_append(str, "%c");
-                if (str->len > 2)
-                       g_string_append_c(str, ',');
+                /* comma separator */
+               if (str->len > 0) {
+                       value = theme_format_expand(theme, "{sb_act_sep ,}");
+                       g_string_append(str, value);
+                       g_free(value);
+               }
 
                switch (window->data_level) {
                case DATA_LEVEL_NONE:
                case DATA_LEVEL_TEXT:
 
                switch (window->data_level) {
                case DATA_LEVEL_NONE:
                case DATA_LEVEL_TEXT:
+                       name = "{sb_act_text %d}";
                        break;
                case DATA_LEVEL_MSG:
                        break;
                case DATA_LEVEL_MSG:
-                        g_string_append(str, "%W");
+                       name = "{sb_act_msg %d}";
                        break;
                default:
                        break;
                default:
-                       g_string_append(str, window->hilight_color == NULL ?
-                                       "%M" : window->hilight_color);
+                        if (window->hilight_color == NULL)
+                               name = "{sb_act_hilight %d}";
+                       else
+                                name = NULL;
                        break;
                }
                        break;
                }
-               g_string_sprintfa(str, "%d", window->refnum);
 
 
-                /* make sure the background is returned to default */
-               g_string_append(str, "%n");
+               if (name != NULL)
+                       format = g_strdup_printf(name, window->refnum);
+               else
+                       format = g_strdup_printf("{sb_act_hilight_color %s %d}",
+                                                window->hilight_color,
+                                                window->refnum);
+
+               value = theme_format_expand(theme, format);
+               g_string_append(str, value);
+                g_free(value);
+
+               g_free(format);
        }
 
        ret = str->len == 0 ? NULL : str->str;
        }
 
        ret = str->len == 0 ? NULL : str->str;
@@ -244,31 +134,21 @@ static char *get_activity_list(int normal, int hilight)
 /* redraw activity, FIXME: if we didn't get enough size, this gets buggy.
    At least "Det:" isn't printed properly. also we should rearrange the
    act list so that the highest priority items comes first. */
 /* redraw activity, FIXME: if we didn't get enough size, this gets buggy.
    At least "Det:" isn't printed properly. also we should rearrange the
    act list so that the highest priority items comes first. */
-static void statusbar_activity(SBAR_ITEM_REC *item, int get_size_only)
+static void item_act(SBAR_ITEM_REC *item, int get_size_only)
 {
 {
-       char *actlist, *detlist, *data;
+       char *actlist;
 
 
-       if (use_colors) {
-               actlist = get_activity_list(TRUE, TRUE);
-                detlist = NULL;
-       } else {
-                actlist = get_activity_list(TRUE, FALSE);
-                detlist = get_activity_list(FALSE, TRUE);
-       }
-
-       if (actlist == NULL && detlist == NULL) {
+       actlist = get_activity_list(item->bar->parent_window, TRUE, TRUE);
+       if (actlist == NULL) {
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
-       data = g_strconcat("{sbact ", actlist != NULL ? actlist : "",
-                          " ", detlist != NULL ? detlist : "", "}", NULL);
-       item_default(item, get_size_only, data, "");
-        g_free(data);
+       statusbar_item_default_handler(item, get_size_only,
+                                      NULL, actlist, FALSE);
 
        g_free_not_null(actlist);
 
        g_free_not_null(actlist);
-        g_free_not_null(detlist);
 }
 
 static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel)
 }
 
 static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel)
@@ -284,7 +164,7 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel
                        activity_list = g_list_remove(activity_list, window);
                if (window->data_level != 0)
                        activity_list = g_list_prepend(activity_list, window);
                        activity_list = g_list_remove(activity_list, window);
                if (window->data_level != 0)
                        activity_list = g_list_prepend(activity_list, window);
-               statusbar_item_redraw(activity_item);
+               statusbar_items_redraw("act");
                return;
        }
 
                return;
        }
 
@@ -293,12 +173,12 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel
                if (window->data_level == 0) {
                        /* remove from activity list */
                        activity_list = g_list_remove(activity_list, window);
                if (window->data_level == 0) {
                        /* remove from activity list */
                        activity_list = g_list_remove(activity_list, window);
-                       statusbar_item_redraw(activity_item);
+                       statusbar_items_redraw("act");
                } else if (window->data_level != GPOINTER_TO_INT(oldlevel) ||
                         window->hilight_color != 0) {
                        /* different level as last time (or maybe different
                           hilight color?), just redraw it. */
                } else if (window->data_level != GPOINTER_TO_INT(oldlevel) ||
                         window->hilight_color != 0) {
                        /* different level as last time (or maybe different
                           hilight color?), just redraw it. */
-                       statusbar_item_redraw(activity_item);
+                       statusbar_items_redraw("act");
                }
                return;
        }
                }
                return;
        }
@@ -320,7 +200,7 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel
        if (tmp == NULL)
                activity_list = g_list_append(activity_list, window);
 
        if (tmp == NULL)
                activity_list = g_list_append(activity_list, window);
 
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
 static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window)
 }
 
 static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window)
@@ -329,437 +209,250 @@ static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window)
 
        if (g_list_find(activity_list, window) != NULL)
                activity_list = g_list_remove(activity_list, window);
 
        if (g_list_find(activity_list, window) != NULL)
                activity_list = g_list_remove(activity_list, window);
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
 static void sig_statusbar_activity_updated(void)
 {
 }
 
 static void sig_statusbar_activity_updated(void)
 {
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
 }
 
-/* redraw -- more -- */
-static void statusbar_more(SBAR_ITEM_REC *item, int get_size_only)
+static void item_more(SBAR_ITEM_REC *item, int get_size_only)
 {
 {
-       item_default(item, get_size_only, "{sbmore}", "");
-}
-
-static void sig_statusbar_more_check_remove(WINDOW_REC *window)
-{
-       g_return_if_fail(window != NULL);
-
-       if (!is_window_visible(window))
-               return;
+        MAIN_WINDOW_REC *mainwin;
+       int visible;
 
 
-       if (more_item != NULL && WINDOW_GUI(window)->view->bottom) {
-               statusbar_item_remove(more_item);
-               more_item = NULL;
+       if (active_win == NULL) {
+                mainwin = NULL;
+               visible = FALSE;
+       } else {
+               mainwin = WINDOW_MAIN(active_win);
+               visible = WINDOW_GUI(active_win)->view->more_text;
        }
        }
-}
 
 
-static void sig_statusbar_more_check(WINDOW_REC *window)
-{
-       if (window == NULL || !is_window_visible(window))
-               return;
-
-       if (!WINDOW_GUI(window)->view->bottom) {
-               if (more_item == NULL) {
-                       more_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_more);
-                       statusbar_redraw(mainbar);
-               }
-       } else if (more_item != NULL) {
-               statusbar_item_remove(more_item);
-               more_item = NULL;
-       }
-}
-
-static void statusbar_lag(SBAR_ITEM_REC *item, int get_size_only)
-{
-       SERVER_REC *server;
-       GString *str;
-       int lag_unknown;
-       time_t now;
-
-       server = active_win == NULL ? NULL : active_win->active_server;
-       if (server == NULL || server->lag_last_check == 0) {
-                /* No lag information */
+       if (!visible) {
+               if (mainwin != NULL)
+                       more_visible = g_slist_remove(more_visible, mainwin);
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
-       now = time(NULL);
-       str = g_string_new(NULL);
-
-       /* FIXME: ugly ugly.. */
-       if (server->lag_sent == 0 || now-server->lag_sent < 5) {
-               lag_unknown = now-server->lag_last_check >
-                       MAX_LAG_UNKNOWN_TIME+settings_get_int("lag_check_time");
-
-               if (lag_min_show < 0 || (server->lag < lag_min_show && !lag_unknown)) {
-                        /* small lag, don't display */
-               } else {
-                       g_string_sprintfa(str, "%d.%02d", server->lag/1000,
-                                         (server->lag % 1000)/10);
-                       if (lag_unknown)
-                               g_string_append(str, " (??)");
-               }
-       } else {
-               /* big lag, still waiting .. */
-               g_string_sprintfa(str, "%ld (??)",
-                                 (long) (now-server->lag_sent));
-       }
-
-       item_default(item, get_size_only, "{sblag $0-}", str->str);
-
-       g_string_free(str, TRUE);
+       more_visible = g_slist_prepend(more_visible, mainwin);
+       statusbar_item_default_handler(item, get_size_only, NULL, "", FALSE);
 }
 
 }
 
-static void sig_statusbar_lag_redraw(void)
+static void sig_statusbar_more_updated(void)
 {
 {
-       statusbar_item_redraw(lag_item);
+       int visible;
+
+        visible = g_slist_find(more_visible, WINDOW_MAIN(active_win)) != NULL;
+       if (WINDOW_GUI(active_win)->view->more_text != visible)
+                statusbar_items_redraw("more");
 }
 
 }
 
-static int statusbar_lag_timeout(void)
+/* Returns the lag in milliseconds. If we haven't been able to ask the lag
+   for a while, unknown is set to TRUE. */
+static int get_lag(SERVER_REC *server, int *unknown)
 {
 {
-       /* refresh statusbar every 10 seconds */
-       if (time(NULL)-lag_last_draw < LAG_REFRESH_TIME)
-               return 1;
+       long lag;
 
 
-       statusbar_item_redraw(lag_item);
-       return 1;
-}
+        *unknown = FALSE;
 
 
-/* FIXME: this isn't very good.. it handles only mbox mailboxes.
-   this whole mail feature should really be in it's own module with lots
-   of other mail formats supported and people who don't want to use it
-   wouldn't need to.. */
-static int get_mail_count(void)
-{
-       struct stat statbuf;
-       FILE *f;
-       char str[512], *fname;
-       int count;
-
-       fname = g_getenv("MAIL");
-       if (fname == NULL) return 0;
-
-       if (stat(fname, &statbuf) != 0) {
-               mail_last_mtime = -1;
-               mail_last_size = -1;
-                mail_last_count = 0;
+       if (server == NULL || server->lag_last_check == 0) {
+                /* lag has not been asked even once yet */
                return 0;
        }
 
                return 0;
        }
 
-       if (statbuf.st_mtime == mail_last_mtime &&
-           statbuf.st_size == mail_last_size)
-               return mail_last_count;
-       mail_last_mtime = statbuf.st_mtime;
-       mail_last_size = statbuf.st_size;
-
-       f = fopen(fname, "r");
-       if (f == NULL) {
-                mail_last_count = 0;
-               return 0;
+       if (server->lag_sent.tv_sec == 0) {
+               /* no lag queries going on currently */
+                return server->lag;
        }
 
        }
 
-       count = 0;
-       while (fgets(str, sizeof(str), f) != NULL) {
-               if (strncmp(str, "From ", 5) == 0)
-                       count++;
-               if (strncmp(str, "Subject: ", 9) == 0 &&
-                   strstr(str, "FOLDER INTERNAL DATA")) {
-                       /* don't count these. */
-                       count--;
-               }
+        /* we're not sure about our current lag.. */
+       *unknown = TRUE;
+
+        lag = (long) (time(NULL)-server->lag_sent.tv_sec);
+       if (server->lag/1000 > lag) {
+               /* we've been waiting the lag reply less time than
+                  what last known lag was -> use the last known lag */
+               return server->lag;
        }
 
        }
 
-       fclose(f);
-       mail_last_count = count;
-       return count;
+        /* return how long we have been waiting for lag reply */
+        return lag*1000;
 }
 
 }
 
-static void statusbar_mail(SBAR_ITEM_REC *item, int get_size_only)
+static void item_lag(SBAR_ITEM_REC *item, int get_size_only)
 {
 {
-       char countstr[MAX_INT_STRLEN];
-       int mail_count;
+       SERVER_REC *server;
+        char str[MAX_INT_STRLEN+10];
+       int lag, lag_unknown;
 
 
-       mail_count = settings_get_bool("mail_counter") ? get_mail_count() : 0;
+       server = active_win == NULL ? NULL : active_win->active_server;
+       lag = get_lag(server, &lag_unknown)/10;
 
 
-       if (mail_count <= 0) {
+       if (lag <= 0 || lag < settings_get_int("lag_min_show")) {
+               /* don't print the lag item */
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
-       ltoa(countstr, mail_count);
-       item_default(item, get_size_only, "{sbmail $0-}", countstr);
-}
-
-static int statusbar_mail_timeout(void)
-{
-       statusbar_item_redraw(mail_item);
-       return 1;
-}
-
-static void statusbar_topic(SBAR_ITEM_REC *item, int get_size_only)
-{
-        item_default(item, get_size_only, "$topic", "");
-}
-
-static void sig_statusbar_topic_redraw(void)
-{
-       if (topic_item != NULL) statusbar_item_redraw(topic_item);
-}
-
-static void sig_sidebars_redraw(void)
-{
-       GSList *tmp;
+       last_lag = lag;
+       last_lag_unknown = lag_unknown;
 
 
-       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
-
-               if (rec->statusbar_window_item != NULL)
-                        statusbar_item_redraw(rec->statusbar_window_item);
+       if (lag_unknown) {
+               g_snprintf(str, sizeof(str), "%d (?""?)", lag/100);
+       } else {
+               g_snprintf(str, sizeof(str),
+                          lag%100 == 0 ? "%d" : "%d.%02d", lag/100, lag%100);
        }
        }
-}
 
 
-static void topicbar_create(void)
-{
-       if (topic_bar != NULL)
-               return;
-
-       topic_bar = statusbar_create(STATUSBAR_POS_UP, 0);
-       topic_item = statusbar_item_create(topic_bar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_topic);
-       topic_item->max_size = TRUE;
-       statusbar_redraw(topic_bar);
-
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_add("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_add("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
+       statusbar_item_default_handler(item, get_size_only,
+                                      NULL, str, TRUE);
 }
 
 }
 
-static void topicbar_destroy(void)
+static void lag_check_update(void)
 {
 {
-       if (topic_bar == NULL)
-               return;
+       SERVER_REC *server;
+       int lag, lag_unknown;
 
 
-       statusbar_destroy(topic_bar);
-       topic_item = NULL;
-       topic_bar = NULL;
+       server = active_win == NULL ? NULL : active_win->active_server;
+       lag = get_lag(server, &lag_unknown)/10;
 
 
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_remove("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_remove("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-}
+       if (lag < settings_get_int("lag_min_show"))
+                lag = 0;
 
 
-static void mainbar_remove_items(void)
-{
-        statusbar_item_remove(clock_item);
-        statusbar_item_remove(nick_item);
-        statusbar_item_remove(window_item);
-       statusbar_item_remove(mail_item);
-       statusbar_item_remove(lag_item);
-        statusbar_item_remove(activity_item);
+       if (lag != last_lag || (lag > 0 && lag_unknown != last_lag_unknown))
+                statusbar_items_redraw("lag");
 }
 
 }
 
-static void mainbar_add_items(MAIN_WINDOW_REC *window)
+static void sig_server_lag_updated(SERVER_REC *server)
 {
 {
-       mainbar = window->statusbar;
-       mainbar_window = window;
-
-       clock_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_clock);
-       nick_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_nick);
-       window_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window);
-       mail_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_mail);
-       lag_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_lag);
-       activity_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_activity);
+       if (active_win != NULL && active_win->active_server == server)
+                lag_check_update();
 }
 
 }
 
-static void sidebar_add_items(MAIN_WINDOW_REC *window)
+static int sig_lag_timeout(void)
 {
 {
-       window->statusbar_window_item =
-               statusbar_item_create(window->statusbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window);
+        lag_check_update();
+        return 1;
 }
 
 }
 
-static void sidebar_remove_items(MAIN_WINDOW_REC *window)
+static void item_input(SBAR_ITEM_REC *item, int get_size_only)
 {
 {
-       if (window->statusbar_window_item != NULL) {
-               statusbar_item_remove(window->statusbar_window_item);
-               window->statusbar_window_item = NULL;
-       }
-}
+       GUI_ENTRY_REC *rec;
 
 
-static void sig_mainwindow_created(MAIN_WINDOW_REC *window)
-{
-       window->statusbar =
-               statusbar_create(STATUSBAR_POS_MIDDLE,
-                                window->first_line+window->height);
-        ((STATUSBAR_REC *) window->statusbar)->window = window;
-       sidebar_add_items(window);
-}
+       rec = g_hash_table_lookup(input_entries, item);
+       if (rec == NULL) {
+               rec = gui_entry_create(item->xpos, item->bar->real_ypos,
+                                      item->size,
+                                      settings_get_bool("term_utf8"));
+               gui_entry_set_active(rec);
+               g_hash_table_insert(input_entries, item, rec);
+       }
 
 
-static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window)
-{
-       if (window == mainbar_window) {
-               mainbar = NULL;
-               mainbar_window = NULL;
+       if (get_size_only) {
+               item->min_size = 2+term_width/10;
+                item->max_size = term_width;
+                return;
        }
 
        }
 
-       if (window->statusbar != NULL)
-               statusbar_destroy(window->statusbar);
+       gui_entry_move(rec, item->xpos, item->bar->real_ypos,
+                      item->size);
+       gui_entry_redraw(rec); /* FIXME: this is only necessary with ^L.. */
 }
 
 }
 
-static void sig_main_statusbar_changed(WINDOW_REC *window)
+static void sig_statusbar_item_destroyed(SBAR_ITEM_REC *item)
 {
 {
-       MAIN_WINDOW_REC *parent;
-
-       if (window == NULL)
-               return;
-
-       parent = WINDOW_GUI(window)->parent;
-       if (mainbar == parent->statusbar)
-               return;
+       GUI_ENTRY_REC *rec;
 
 
-       if (mainbar != NULL) {
-               mainbar_remove_items();
-               sidebar_add_items(mainbar_window);
+       rec = g_hash_table_lookup(input_entries, item);
+       if (rec != NULL) {
+               gui_entry_destroy(rec);
+               g_hash_table_remove(input_entries, item);
        }
        }
-       sidebar_remove_items(parent);
-        mainbar_add_items(parent);
 }
 
 static void read_settings(void)
 {
 }
 
 static void read_settings(void)
 {
-       use_colors = settings_get_bool("colors") && has_colors();
-       if (settings_get_bool("topicbar"))
-               topicbar_create();
-       else
-               topicbar_destroy();
-
-       lag_min_show = settings_get_int("lag_min_show")*10;
-       statusbar_redraw(NULL);
+       if (active_entry != NULL) {
+               gui_entry_set_utf8(active_entry,
+                                  settings_get_bool("term_utf8"));
+       }
 }
 
 void statusbar_items_init(void)
 {
 }
 
 void statusbar_items_init(void)
 {
-       GSList *tmp;
-
        settings_add_int("misc", "lag_min_show", 100);
        settings_add_int("misc", "lag_min_show", 100);
-       settings_add_bool("lookandfeel", "topicbar", TRUE);
        settings_add_bool("lookandfeel", "actlist_moves", FALSE);
        settings_add_bool("lookandfeel", "actlist_moves", FALSE);
-       settings_add_bool("misc", "mail_counter", TRUE);
-
-       /* clock */
-       clock_timetag = g_timeout_add(1000, (GSourceFunc) statusbar_clock_timeout, NULL);
-
-       /* nick */
-       signal_add("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-
-       /* channel */
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw);
-       signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_add("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item);
-       signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-
-       /* activity */
+
+       statusbar_item_register("window", NULL, item_window_active);
+       statusbar_item_register("window_empty", NULL, item_window_empty);
+       statusbar_item_register("prompt", NULL, item_window_active);
+       statusbar_item_register("prompt_empty", NULL, item_window_empty);
+       statusbar_item_register("lag", NULL, item_lag);
+       statusbar_item_register("act", NULL, item_act);
+       statusbar_item_register("more", NULL, item_more);
+       statusbar_item_register("input", NULL, item_input);
+
+        /* activity */
        activity_list = NULL;
        signal_add("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight);
        signal_add("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed);
        signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated);
 
        activity_list = NULL;
        signal_add("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight);
        signal_add("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed);
        signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated);
 
-       /* more */
-       more_item = NULL;
-       signal_add("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove);
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_more_check);
-       signal_add("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check);
-
-       /* lag */
-       lag_timetag = g_timeout_add(1000*LAG_REFRESH_TIME, (GSourceFunc) statusbar_lag_timeout, NULL);
-       signal_add("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-       signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-
-       /* mail */
-       mail_timetag = g_timeout_add(1000*MAIL_REFRESH_TIME, (GSourceFunc) statusbar_mail_timeout, NULL);
-
-       /* topic */
-       topic_item = NULL; topic_bar = NULL;
-       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+        /* more */
+        more_visible = NULL;
+       signal_add("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add_last("gui print text finished", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add_last("command clear", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add_last("command scrollback", (SIGNAL_FUNC) sig_statusbar_more_updated);
+
+        /* lag */
+       last_lag = 0; last_lag_unknown = FALSE;
+       signal_add("server lag", (SIGNAL_FUNC) sig_server_lag_updated);
+       signal_add("window changed", (SIGNAL_FUNC) lag_check_update);
+       signal_add("window server changed", (SIGNAL_FUNC) lag_check_update);
+        lag_timeout_tag = g_timeout_add(5000, (GSourceFunc) sig_lag_timeout, NULL);
+
+        /* input */
+       input_entries = g_hash_table_new((GHashFunc) g_direct_hash,
+                                        (GCompareFunc) g_direct_equal);
+       signal_add("statusbar item destroyed", (SIGNAL_FUNC) sig_statusbar_item_destroyed);
 
        read_settings();
 
        read_settings();
-       statusbar_redraw(NULL);
-
-       /* middle bars */
-        signal_add("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created);
-        signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
-       signal_add("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed);
-       signal_add("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw);
-
-       /* add statusbars to existing windows */
-       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next)
-               sig_mainwindow_created(tmp->data);
-       sig_main_statusbar_changed(active_win);
+        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void statusbar_items_deinit(void)
 {
 }
 
 void statusbar_items_deinit(void)
 {
-       /* clock */
-       g_source_remove(clock_timetag);
-
-       /* nick */
-       signal_remove("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-
-       /* channel */
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw);
-       signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_remove("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item);
-       signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-
-       /* activity */
+        /* activity */
        signal_remove("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight);
        signal_remove("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed);
        signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated);
        g_list_free(activity_list);
        signal_remove("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight);
        signal_remove("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed);
        signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated);
        g_list_free(activity_list);
-
-       /* more */
-       signal_remove("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove);
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_more_check);
-       signal_remove("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check);
-
-       /* lag */
-       g_source_remove(lag_timetag);
-       signal_remove("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-       signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-
-       /* mail */
-       g_source_remove(mail_timetag);
-
-       /* topic */
-       topicbar_destroy();
-       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
-
-       /* middle bars */
-        signal_remove("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created);
-        signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
-       signal_remove("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed);
-       signal_remove("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw);
+        activity_list = NULL;
+
+        /* more */
+        g_slist_free(more_visible);
+       signal_remove("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("gui print text finished", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("command clear", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("command scrollback", (SIGNAL_FUNC) sig_statusbar_more_updated);
+
+        /* lag */
+       signal_remove("server lag", (SIGNAL_FUNC) sig_server_lag_updated);
+       signal_remove("window changed", (SIGNAL_FUNC) lag_check_update);
+       signal_remove("window server changed", (SIGNAL_FUNC) lag_check_update);
+        g_source_remove(lag_timeout_tag);
+
+        /* input */
+       signal_remove("statusbar item destroyed", (SIGNAL_FUNC) sig_statusbar_item_destroyed);
+        g_hash_table_destroy(input_entries);
+
+        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
 }
index 8e99c4fe5634a1e80515c911aea820e317e2a481..a9271b3cf7808a6fb42c47f65c2da3698d66e5ed 100644 (file)
 
 #include "module.h"
 #include "signals.h"
 
 #include "module.h"
 #include "signals.h"
+#include "expandos.h"
+#include "special-vars.h"
 
 #include "themes.h"
 
 #include "statusbar.h"
 
 #include "themes.h"
 
 #include "statusbar.h"
+#include "statusbar-config.h"
 #include "gui-windows.h"
 #include "gui-windows.h"
-
-static int backs[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* FIXME: should be in some more generic place.. */
+#include "gui-printtext.h"
 
 void statusbar_items_init(void);
 void statusbar_items_deinit(void);
 
 
 void statusbar_items_init(void);
 void statusbar_items_deinit(void);
 
-static GSList *statusbars;
-static int sbar_uppest, sbar_lowest, sbars_up, sbars_down;
+GSList *statusbar_groups;
+STATUSBAR_GROUP_REC *active_statusbar_group;
+
+/*
+   sbar_item_defs: char *name => char *value
+   sbar_item_funcs: char *name => STATUSBAR_FUNC func
+   sbar_signal_items: int signal_id => GSList *(SBAR_ITEM_REC *items)
+   sbar_item_signals: SBAR_ITEM_REC *item => GSList *(int *signal_ids)
+   named_sbar_items: const char *name => GSList *(SBAR_ITEM_REC *items)
+*/
+static GHashTable *sbar_item_defs, *sbar_item_funcs;
+static GHashTable *sbar_signal_items, *sbar_item_signals;
+static GHashTable *named_sbar_items;
+static int statusbar_need_recreate_items;
+
+void statusbar_item_register(const char *name, const char *value,
+                            STATUSBAR_FUNC func)
+{
+       gpointer hkey, hvalue;
+
+       statusbar_need_recreate_items = TRUE;
+       if (value != NULL) {
+               if (g_hash_table_lookup_extended(sbar_item_defs,
+                                                name, &hkey, &hvalue)) {
+                       g_hash_table_remove(sbar_item_defs, name);
+                       g_free(hkey);
+                        g_free(hvalue);
+               }
+               g_hash_table_insert(sbar_item_defs,
+                                   g_strdup(name), g_strdup(value));
+       }
+
+       if (func != NULL) {
+               if (g_hash_table_lookup(sbar_item_funcs, name) == NULL) {
+                       g_hash_table_insert(sbar_item_funcs,
+                                           g_strdup(name), (void *) func);
+               }
+       }
+}
+
+void statusbar_item_unregister(const char *name)
+{
+       gpointer key, value;
+
+       statusbar_need_recreate_items = TRUE;
+       if (g_hash_table_lookup_extended(sbar_item_defs,
+                                        name, &key, &value)) {
+               g_hash_table_remove(sbar_item_defs, key);
+               g_free(key);
+                g_free(value);
+       }
+
+       if (g_hash_table_lookup_extended(sbar_item_funcs,
+                                        name, &key, &value)) {
+               g_hash_table_remove(sbar_item_funcs, key);
+               g_free(key);
+       }
+}
+
+STATUSBAR_GROUP_REC *statusbar_group_create(const char *name)
+{
+       STATUSBAR_GROUP_REC *rec;
+
+       rec = g_new0(STATUSBAR_GROUP_REC, 1);
+       rec->name = g_strdup(name);
+
+        statusbar_groups = g_slist_append(statusbar_groups, rec);
+       return rec;
+}
 
 
-static void statusbar_item_destroy(SBAR_ITEM_REC *rec)
+void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec)
 {
 {
-       rec->bar->items = g_slist_remove(rec->bar->items, rec);
-       g_free(rec);
+       statusbar_groups = g_slist_remove(statusbar_groups, rec);
+
+       while (rec->bars != NULL)
+               statusbar_destroy(rec->bars->data);
+       while (rec->config_bars != NULL)
+                statusbar_config_destroy(rec, rec->config_bars->data);
+
+        g_free(rec->name);
+        g_free(rec);
+}
+
+STATUSBAR_GROUP_REC *statusbar_group_find(const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = statusbar_groups; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_GROUP_REC *rec = tmp->data;
+
+               if (strcmp(rec->name, name) == 0)
+                        return rec;
+       }
+
+        return NULL;
 }
 
 static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2)
 {
 }
 
 static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2)
 {
-       return item1->priority == item2->priority ? 0 :
-               item1->priority < item2->priority ? -1 : 1;
+       return item1->config->priority == item2->config->priority ? 0 :
+               item1->config->priority < item2->config->priority ? -1 : 1;
 }
 
 }
 
+static int sbar_cmp_position(STATUSBAR_REC *bar1, STATUSBAR_REC *bar2)
+{
+       return bar1->config->position < bar2->config->position ? -1 : 1;
+}
+
+/* Shink all items in statusbar to their minimum requested size.
+   The items list should be sorted by priority, highest first. */
 static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
 {
        GSList *tmp;
 static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
 {
        GSList *tmp;
@@ -71,6 +168,9 @@ static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
         return size;
 }
 
         return size;
 }
 
+/* shink the items in statusbar, even if their size gets smaller than
+   their minimum requested size. The items list should be sorted by
+   priority, highest first. */
 static void statusbar_shrink_forced(GSList *items, int size, int max_width)
 {
        GSList *tmp;
 static void statusbar_shrink_forced(GSList *items, int size, int max_width)
 {
        GSList *tmp;
@@ -80,7 +180,7 @@ static void statusbar_shrink_forced(GSList *items, int size, int max_width)
 
                if (size-rec->size > max_width) {
                        /* remove the whole item */
 
                if (size-rec->size > max_width) {
                        /* remove the whole item */
-                        size -= rec->size+1; /* +1 == the marginal */
+                        size -= rec->size;
                        rec->size = 0;
                } else {
                        /* shrink the item */
                        rec->size = 0;
                } else {
                        /* shrink the item */
@@ -90,14 +190,14 @@ static void statusbar_shrink_forced(GSList *items, int size, int max_width)
        }
 }
 
        }
 }
 
-static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width)
+static void statusbar_resize_items(STATUSBAR_REC *bar, int max_width)
 {
        GSList *tmp, *prior_sorted;
         int width;
 
         /* first give items their max. size */
        prior_sorted = NULL;
 {
        GSList *tmp, *prior_sorted;
         int width;
 
         /* first give items their max. size */
        prior_sorted = NULL;
-       width = -1; /* -1 because of the marginals */
+       width = 0;
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
@@ -105,8 +205,7 @@ static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width)
                rec->size = rec->max_size;
 
                if (rec->size > 0) {
                rec->size = rec->max_size;
 
                if (rec->size > 0) {
-                        /* +1 == marginal between items */
-                       width += rec->max_size+1;
+                       width += rec->max_size;
 
                        prior_sorted = g_slist_insert_sorted(prior_sorted, rec,
                                                             (GCompareFunc)
 
                        prior_sorted = g_slist_insert_sorted(prior_sorted, rec,
                                                             (GCompareFunc)
@@ -131,239 +230,908 @@ static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width)
        g_slist_free(prior_sorted);
 }
 
        g_slist_free(prior_sorted);
 }
 
-static void statusbar_redraw_line(STATUSBAR_REC *bar)
+#define SBAR_ITEM_REDRAW_NEEDED(_bar, _item, _xpos) \
+       (((_bar)->dirty_xpos != -1 && \
+         (_xpos) >= (_bar)->dirty_xpos) || \
+        (_item)->xpos != (_xpos) || (_item)->current_size != (_item)->size)
+
+static void statusbar_calc_item_positions(STATUSBAR_REC *bar)
 {
         WINDOW_REC *old_active_win;
 {
         WINDOW_REC *old_active_win;
-       GSList *tmp;
+       GSList *tmp, *right_items;
        int xpos, rxpos;
 
        old_active_win = active_win;
        int xpos, rxpos;
 
        old_active_win = active_win;
-        if (bar->window != NULL)
-               active_win = bar->window->active;
+        if (bar->parent_window != NULL)
+               active_win = bar->parent_window->active;
 
 
-       statusbar_get_sizes(bar, COLS-2);
+       statusbar_resize_items(bar, term_width);
 
 
-       xpos = 1;
+        /* left-aligned items */
+       xpos = 0;
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
-               if (!rec->right_justify && rec->size > 0) {
-                       rec->xpos = xpos;
-                        xpos += rec->size+1;
-                        rec->func(rec, FALSE);
+               if (!rec->config->right_alignment &&
+                   (rec->size > 0 || rec->current_size > 0)) {
+                       if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, xpos)) {
+                                /* redraw the item */
+                               rec->dirty = TRUE;
+                               if (bar->dirty_xpos == -1 ||
+                                   xpos < bar->dirty_xpos) {
+                                        irssi_set_dirty();
+                                       bar->dirty = TRUE;
+                                       bar->dirty_xpos = xpos;
+                               }
+
+                               rec->xpos = xpos;
+                       }
+                       xpos += rec->size;
                }
        }
 
                }
        }
 
-       rxpos = COLS-1;
+       /* right-aligned items - first copy them to a new list backwards,
+          easier to draw them in right order */
+        right_items = NULL;
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
-               if (rec->right_justify && rec->size > 0) {
-                        rxpos -= rec->size+1;
-                       rec->xpos = rxpos+1;
-                       rec->func(rec, FALSE);
+               if (rec->config->right_alignment) {
+                        if (rec->size > 0)
+                               right_items = g_slist_prepend(right_items, rec);
+                       else if (rec->current_size > 0) {
+                               /* item was hidden - set the dirty position
+                                  to begin from the item's old xpos */
+                               irssi_set_dirty();
+                               bar->dirty = TRUE;
+                                bar->dirty_xpos = rec->xpos;
+                       }
+               }
+       }
+
+       rxpos = term_width;
+       for (tmp = right_items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_REC *rec = tmp->data;
+
+               rxpos -= rec->size;
+               if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, rxpos)) {
+                       rec->dirty = TRUE;
+                       if (bar->dirty_xpos == -1 ||
+                           rxpos < bar->dirty_xpos) {
+                               irssi_set_dirty();
+                               bar->dirty = TRUE;
+                               bar->dirty_xpos = rxpos;
+                       }
+                       rec->xpos = rxpos;
                }
        }
                }
        }
+        g_slist_free(right_items);
 
        active_win = old_active_win;
 }
 
 
        active_win = old_active_win;
 }
 
-static void statusbar_redraw_all(void)
+void statusbar_redraw(STATUSBAR_REC *bar, int force)
 {
 {
-       screen_refresh_freeze();
-       g_slist_foreach(statusbars, (GFunc) statusbar_redraw, NULL);
-       screen_refresh_thaw();
+       if (statusbar_need_recreate_items)
+               return; /* don't bother yet */
+
+       if (bar != NULL) {
+               if (force) {
+                       irssi_set_dirty();
+                       bar->dirty = TRUE;
+                        bar->dirty_xpos = 0;
+               }
+               statusbar_calc_item_positions(bar);
+       } else if (active_statusbar_group != NULL) {
+               g_slist_foreach(active_statusbar_group->bars,
+                               (GFunc) statusbar_redraw,
+                               GINT_TO_POINTER(force));
+       }
 }
 
 }
 
-STATUSBAR_REC *statusbar_find(int pos, int line)
+void statusbar_item_redraw(SBAR_ITEM_REC *item)
 {
 {
-       GSList *tmp;
+        WINDOW_REC *old_active_win;
 
 
-       for (tmp = statusbars; tmp != NULL; tmp = tmp->next) {
-               STATUSBAR_REC *rec = tmp->data;
+       g_return_if_fail(item != NULL);
+
+       old_active_win = active_win;
+        if (item->bar->parent_window != NULL)
+               active_win = item->bar->parent_window->active;
+
+       item->func(item, TRUE);
+
+       item->dirty = TRUE;
+       item->bar->dirty = TRUE;
+       irssi_set_dirty();
 
 
-               if (rec->pos == pos && rec->line == line)
-                       return rec;
+       if (item->max_size != item->size) {
+               /* item wants a new size - we'll need to redraw
+                  the statusbar to see if this is allowed */
+               statusbar_redraw(item->bar, FALSE);
        }
 
        }
 
-       return NULL;
+       active_win = old_active_win;
 }
 
 }
 
-void statusbar_redraw(STATUSBAR_REC *bar)
+void statusbar_items_redraw(const char *name)
 {
 {
-       if (bar == NULL) {
-               statusbar_redraw_all();
+       g_slist_foreach(g_hash_table_lookup(named_sbar_items, name),
+                       (GFunc) statusbar_item_redraw, NULL);
+}
+
+static void statusbars_recalc_ypos(STATUSBAR_REC *bar)
+{
+       GSList *tmp, *bar_group;
+        int ypos;
+
+       /* get list of statusbars with same type and placement,
+          sorted by position */
+        bar_group = NULL;
+       tmp = bar->config->type == STATUSBAR_TYPE_ROOT ? bar->group->bars :
+                bar->parent_window->statusbars;
+
+        for (; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *rec = tmp->data;
+
+               if (rec->config->type == bar->config->type &&
+                   rec->config->placement == bar->config->placement) {
+                       bar_group = g_slist_insert_sorted(bar_group, rec,
+                                                         (GCompareFunc)
+                                                         sbar_cmp_position);
+               }
+       }
+
+       if (bar_group == NULL) {
+               /* we just destroyed the last statusbar in this
+                  type/placement group */
                return;
        }
 
                return;
        }
 
-       set_bg(stdscr, backs[bar->color] << 4);
-       move(bar->ypos, 0); clrtoeol();
-       set_bg(stdscr, 0);
+        /* get the Y-position for the first statusbar */
+       if (bar->config->type == STATUSBAR_TYPE_ROOT) {
+               ypos = bar->config->placement == STATUSBAR_TOP ? 0 :
+                       term_height - g_slist_length(bar_group);
+       } else {
+               ypos = bar->config->placement == STATUSBAR_TOP ?
+                       bar->parent_window->first_line :
+                       bar->parent_window->last_line -
+                       (g_slist_length(bar_group)-1);
+       }
 
 
-       statusbar_redraw_line(bar);
+        /* set the Y-positions */
+       while (bar_group != NULL) {
+               bar = bar_group->data;
+
+               if (bar->real_ypos != ypos) {
+                       bar->real_ypos = ypos;
+                        statusbar_redraw(bar, TRUE);
+               }
 
 
-        screen_refresh(NULL);
+                ypos++;
+                bar_group = g_slist_remove(bar_group, bar_group->data);
+       }
 }
 
 }
 
-void statusbar_item_redraw(SBAR_ITEM_REC *item)
+static void sig_terminal_resized(void)
 {
 {
-       g_return_if_fail(item != NULL);
+       GSList *tmp;
 
 
-       item->func(item, TRUE);
-       if (item->max_size != item->size)
-               statusbar_redraw(item->bar);
-       else {
-               item->func(item, FALSE);
-                screen_refresh(NULL);
+       for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *bar = tmp->data;
+
+               if (bar->config->type == STATUSBAR_TYPE_ROOT &&
+                   bar->config->placement == STATUSBAR_BOTTOM) {
+                       statusbars_recalc_ypos(bar);
+                        break;
+               }
        }
 }
 
        }
 }
 
-static int get_last_bg(const char *str)
+static void mainwindow_recalc_ypos(MAIN_WINDOW_REC *window, int placement)
 {
 {
-       int last = -1;
+       GSList *tmp;
 
 
-       while (*str != '\0') {
-               if (*str == '%' && str[1] != '\0') {
-                        str++;
-                       if (*str >= '0' && *str <= '7')
-                               last = *str-'0';
+       for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *bar = tmp->data;
+
+               if (bar->config->placement == placement) {
+                       statusbars_recalc_ypos(bar);
+                        break;
                }
                }
-                str++;
        }
        }
+}
 
 
-        return last;
+static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
+{
+        mainwindow_recalc_ypos(window, STATUSBAR_TOP);
+        mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM);
 }
 
 }
 
-/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */
-STATUSBAR_REC *statusbar_create(int pos, int ypos)
+STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group,
+                                STATUSBAR_CONFIG_REC *config,
+                                MAIN_WINDOW_REC *parent_window)
 {
 {
-       STATUSBAR_REC *rec;
-        char *str;
+       STATUSBAR_REC *bar;
+       THEME_REC *theme;
+        GSList *tmp;
+       char *name, *value;
+
+        g_return_val_if_fail(group != NULL, NULL);
+        g_return_val_if_fail(config != NULL, NULL);
+       g_return_val_if_fail(config->type != STATUSBAR_TYPE_WINDOW ||
+                            parent_window != NULL, NULL);
+
+       bar = g_new0(STATUSBAR_REC, 1);
+       group->bars = g_slist_append(group->bars, bar);
+
+       bar->group = group;
+
+        bar->config = config;
+        bar->parent_window = parent_window;
+
+       irssi_set_dirty();
+       bar->dirty = TRUE;
+        bar->dirty_xpos = 0;
+
+        signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+       signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
 
 
-       rec = g_new0(STATUSBAR_REC, 1);
-       statusbars = g_slist_append(statusbars, rec);
+       if (config->type == STATUSBAR_TYPE_ROOT) {
+               /* top/bottom of the screen */
+               mainwindows_reserve_lines(config->placement == STATUSBAR_TOP,
+                                         config->placement == STATUSBAR_BOTTOM);
+                theme = current_theme;
+       } else {
+               /* top/bottom of the window */
+               parent_window->statusbars =
+                       g_slist_append(parent_window->statusbars, bar);
+               mainwindow_set_statusbar_lines(parent_window,
+                                              config->placement == STATUSBAR_TOP,
+                                              config->placement == STATUSBAR_BOTTOM);
+               theme = parent_window != NULL && parent_window->active != NULL &&
+                       parent_window->active->theme != NULL ?
+                       parent_window->active->theme : current_theme;
+       }
 
 
-       rec->pos = pos;
-       rec->line = pos == STATUSBAR_POS_MIDDLE ? ypos :
-               mainwindows_reserve_lines(1, pos == STATUSBAR_POS_UP);
-       rec->ypos = pos == STATUSBAR_POS_MIDDLE ? ypos :
-               pos == STATUSBAR_POS_UP ? rec->line : LINES-1-rec->line;
+        signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+       signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
 
         /* get background color from sb_background abstract */
 
         /* get background color from sb_background abstract */
-       str = theme_format_expand(current_theme, "{sb_background}");
-       if (str == NULL) str = g_strdup("%n%8");
-       rec->color_string = g_strconcat("%n", str, NULL);
-        g_free(str);
+        name = g_strdup_printf("{sb_%s_bg}", config->name);
+       value = theme_format_expand(theme, name);
+       g_free(name);
+
+       if (*value == '\0') {
+                /* try with the statusbar group name */
+               g_free(value);
+
+               name = g_strdup_printf("{sb_%s_bg}", group->name);
+               value = theme_format_expand(theme, name);
+               g_free(name);
+
+               if (*value == '\0') {
+                       /* fallback to default statusbar background
+                          (also provides backwards compatibility..) */
+                        g_free(value);
+                       value = theme_format_expand(theme, "{sb_background}");
+               }
+       }
+
+       if (*value == '\0') {
+                g_free(value);
+               value = g_strdup("%8");
+       }
+       bar->color = g_strconcat("%n", value, NULL);
+       g_free(value);
 
 
-       rec->color = get_last_bg(rec->color_string);
-        if (rec->color < 0) rec->color = current_theme->default_real_color;
+        statusbars_recalc_ypos(bar);
+        signal_emit("statusbar created", 1, bar);
 
 
-       if (pos == STATUSBAR_POS_UP) {
-                if (sbars_up == 0) sbar_uppest = rec->line;
-                sbars_up++;
-               rec->line -= sbar_uppest;
-       } else if (pos == STATUSBAR_POS_DOWN) {
-               if (sbars_down == 0) sbar_lowest = rec->line;
-               sbars_down++;
-               rec->line -= sbar_lowest;
+        /* create the items to statusbar */
+       for (tmp = config->items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_CONFIG_REC *rec = tmp->data;
+
+                statusbar_item_create(bar, rec);
        }
        }
+       return bar;
+}
 
 
-       set_bg(stdscr, backs[rec->color] << 4);
-       move(rec->ypos, 0); clrtoeol();
-       set_bg(stdscr, 0);
+void statusbar_destroy(STATUSBAR_REC *bar)
+{
+       int top;
 
 
-       return rec;
+       g_return_if_fail(bar != NULL);
+
+       bar->group->bars = g_slist_remove(bar->group->bars, bar);
+       if (bar->parent_window != NULL) {
+               bar->parent_window->statusbars =
+                       g_slist_remove(bar->parent_window->statusbars, bar);
+       }
+
+        signal_emit("statusbar destroyed", 1, bar);
+
+       while (bar->items != NULL)
+               statusbar_item_destroy(bar->items->data);
+
+        g_free(bar->color);
+
+       if (bar->config->type != STATUSBAR_TYPE_WINDOW ||
+           bar->parent_window != NULL)
+               statusbars_recalc_ypos(bar);
+
+       top = bar->config->placement == STATUSBAR_TOP;
+       if (bar->config->type == STATUSBAR_TYPE_ROOT) {
+               /* top/bottom of the screen */
+               mainwindows_reserve_lines(top ? -1 : 0, !top ? -1 : 0);
+       } else if (bar->parent_window != NULL) {
+               /* top/bottom of the window */
+               mainwindow_set_statusbar_lines(bar->parent_window,
+                                              top ? -1 : 0, !top ? -1 : 0);
+       }
+
+       g_free(bar);
 }
 
 }
 
-static void statusbars_pack(int pos, int line)
+void statusbar_recreate_items(STATUSBAR_REC *bar)
 {
        GSList *tmp;
 
 {
        GSList *tmp;
 
-       for (tmp = statusbars; tmp != NULL; tmp = tmp->next) {
+       /* destroy */
+       while (bar->items != NULL)
+               statusbar_item_destroy(bar->items->data);
+
+        /* create */
+       for (tmp = bar->config->items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_CONFIG_REC *rec = tmp->data;
+
+                statusbar_item_create(bar, rec);
+       }
+
+        statusbar_redraw(bar, TRUE);
+}
+
+void statusbars_recreate_items(void)
+{
+       if (active_statusbar_group != NULL) {
+               g_slist_foreach(active_statusbar_group->bars,
+                               (GFunc) statusbar_recreate_items, NULL);
+       }
+}
+
+STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
+                             MAIN_WINDOW_REC *window)
+{
+       GSList *tmp;
+
+       for (tmp = group->bars; tmp != NULL; tmp = tmp->next) {
                STATUSBAR_REC *rec = tmp->data;
 
                STATUSBAR_REC *rec = tmp->data;
 
-               if (rec->pos == pos && rec->line > line) {
-                       rec->line--;
-                       rec->ypos += (pos == STATUSBAR_POS_UP ? -1 : 1);
+               if (rec->parent_window == window &&
+                   strcmp(rec->config->name, name) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+static char *update_statusbar_bg(const char *str, const char *color)
+{
+       GString *out;
+        char *ret;
+
+       out = g_string_new(color);
+       while (*str != '\0') {
+               if (*str == '%' && str[1] == 'n') {
+                        g_string_append(out, color);
+                       str += 2;
+                        continue;
                }
                }
+
+               g_string_append_c(out, *str);
+                str++;
        }
        }
+
+        ret = out->str;
+        g_string_free(out, FALSE);
+        return ret;
 }
 
 }
 
-void statusbar_destroy(STATUSBAR_REC *bar)
+const char *statusbar_item_get_value(SBAR_ITEM_REC *item)
 {
 {
-       g_return_if_fail(bar != NULL);
+       const char *value;
 
 
-       if (bar->pos != STATUSBAR_POS_MIDDLE)
-               mainwindows_reserve_lines(-1, bar->pos == STATUSBAR_POS_UP);
+       value = item->config->value;
+       if (value == NULL) {
+               value = g_hash_table_lookup(sbar_item_defs,
+                                           item->config->name);
+       }
 
 
-       if (bar->pos == STATUSBAR_POS_UP) sbars_up--;
-       if (bar->pos == STATUSBAR_POS_DOWN) sbars_down--;
-        statusbars = g_slist_remove(statusbars, bar);
+        return value;
+}
 
 
-       while (bar->items != NULL)
-               statusbar_item_destroy(bar->items->data);
+void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
+                                   const char *str, const char *data,
+                                   int escape_vars)
+{
+       SERVER_REC *server;
+       WI_ITEM_REC *wiitem;
+        char *tmpstr, *tmpstr2;
+       int len;
+
+       if (str == NULL)
+               str = statusbar_item_get_value(item);
+       if (str == NULL || *str == '\0') {
+               item->min_size = item->max_size = 0;
+               return;
+       }
 
 
-       if (bar->pos != STATUSBAR_POS_MIDDLE)
-               statusbars_pack(bar->pos, bar->pos);
-        g_free(bar->color_string);
-       g_free(bar);
+       if (active_win == NULL) {
+               server = NULL;
+                wiitem = NULL;
+       } else {
+               server = active_win->active_server;
+                wiitem = active_win->active;
+       }
+
+       /* expand templates */
+       tmpstr = theme_format_expand_data(current_theme, &str,
+                                         'n', 'n',
+                                         NULL, NULL,
+                                         EXPAND_FLAG_ROOT |
+                                         EXPAND_FLAG_IGNORE_REPLACES |
+                                         EXPAND_FLAG_IGNORE_EMPTY);
+       /* expand $variables */
+       tmpstr2 = parse_special_string(tmpstr, server, wiitem, data, NULL,
+                                      (escape_vars ? PARSE_FLAG_ESCAPE_VARS : 0 ));
+        g_free(tmpstr);
+
+       /* remove color codes (not %formats) */
+       tmpstr = strip_codes(tmpstr2);
+        g_free(tmpstr2);
+
+       if (get_size_only) {
+               item->min_size = item->max_size = format_get_length(tmpstr);
+       } else {
+               if (item->size < item->min_size) {
+                        /* they're forcing us smaller than minimum size.. */
+                       len = format_real_length(tmpstr, item->size);
+                        tmpstr[len] = '\0';
+               }
+
+               tmpstr2 = update_statusbar_bg(tmpstr, item->bar->color);
+               gui_printtext(item->xpos, item->bar->real_ypos, tmpstr2);
+                g_free(tmpstr2);
+       }
+       g_free(tmpstr);
+}
+
+static void statusbar_item_default_func(SBAR_ITEM_REC *item, int get_size_only)
+{
+       statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE);
+}
+
+static void statusbar_update_item(void)
+{
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               statusbar_item_redraw(item);
+               items = items->next;
+       }
+}
+
+static void statusbar_update_server(SERVER_REC *server)
+{
+        SERVER_REC *item_server;
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               item_server = item->bar->parent_window != NULL ?
+                       item->bar->parent_window->active->active_server :
+                       active_win->active_server;
 
 
-       if (!quitting) statusbar_redraw_all();
+               if (item_server == server)
+                       statusbar_item_redraw(item);
+
+               items = items->next;
+       }
+}
+
+static void statusbar_update_window(WINDOW_REC *window)
+{
+        WINDOW_REC *item_window;
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               item_window = item->bar->parent_window != NULL ?
+                       item->bar->parent_window->active : active_win;
+
+               if (item_window == window)
+                       statusbar_item_redraw(item);
+
+               items = items->next;
+       }
+}
+
+static void statusbar_update_window_item(WI_ITEM_REC *wiitem)
+{
+        WI_ITEM_REC *item_wi;
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               item_wi = item->bar->parent_window != NULL ?
+                       item->bar->parent_window->active->active :
+                       active_win->active;
+
+               if (item_wi == wiitem)
+                       statusbar_item_redraw(item);
+
+               items = items->next;
+       }
+}
+
+static void statusbar_item_default_signals(SBAR_ITEM_REC *item)
+{
+       SIGNAL_FUNC func;
+        GSList *list;
+       const char *value;
+        void *signal_id;
+        int *signals, *pos;
+
+       value = statusbar_item_get_value(item);
+       if (value == NULL)
+               return;
+
+       signals = special_vars_get_signals(value);
+       if (signals == NULL)
+               return;
+
+       for (pos = signals; *pos != -1; pos += 2) {
+               /* update signal -> item mappings */
+                signal_id = GINT_TO_POINTER(*pos);
+               list = g_hash_table_lookup(sbar_signal_items, signal_id);
+               if (list == NULL) {
+                       switch (pos[1]) {
+                       case EXPANDO_ARG_NONE:
+                               func = (SIGNAL_FUNC) statusbar_update_item;
+                               break;
+                       case EXPANDO_ARG_SERVER:
+                               func = (SIGNAL_FUNC) statusbar_update_server;
+                               break;
+                       case EXPANDO_ARG_WINDOW:
+                               func = (SIGNAL_FUNC) statusbar_update_window;
+                               break;
+                       case EXPANDO_ARG_WINDOW_ITEM:
+                               func = (SIGNAL_FUNC) statusbar_update_window_item;
+                               break;
+                       default:
+                                func = NULL;
+                                break;
+                       }
+                        if (func != NULL)
+                               signal_add_to_id(MODULE_NAME, 1, *pos, func);
+               }
+
+               if (g_slist_find(list, item) == NULL)
+                       list = g_slist_append(list, item);
+               g_hash_table_insert(sbar_signal_items, signal_id, list);
+
+                /* update item -> signal mappings */
+               list = g_hash_table_lookup(sbar_item_signals, item);
+                if (g_slist_find(list, signal_id) == NULL)
+                       list = g_slist_append(list, signal_id);
+               g_hash_table_insert(sbar_item_signals, item, list);
+       }
+        g_free(signals);
 }
 
 SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
 }
 
 SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
-                                    int priority, int right_justify,
-                                    STATUSBAR_FUNC func)
+                                    SBAR_ITEM_CONFIG_REC *config)
 {
        SBAR_ITEM_REC *rec;
 {
        SBAR_ITEM_REC *rec;
+        GSList *items;
 
        g_return_val_if_fail(bar != NULL, NULL);
 
        g_return_val_if_fail(bar != NULL, NULL);
-       g_return_val_if_fail(func != NULL, NULL);
+       g_return_val_if_fail(config != NULL, NULL);
 
        rec = g_new0(SBAR_ITEM_REC, 1);
 
        rec = g_new0(SBAR_ITEM_REC, 1);
-       rec->bar = bar;
        bar->items = g_slist_append(bar->items, rec);
 
        bar->items = g_slist_append(bar->items, rec);
 
-        rec->priority = priority;
-       rec->right_justify = right_justify;
-       rec->func = func;
+       rec->bar = bar;
+       rec->config = config;
+
+       rec->func = (STATUSBAR_FUNC) g_hash_table_lookup(sbar_item_funcs,
+                                                        config->name);
+       if (rec->func == NULL)
+               rec->func = statusbar_item_default_func;
+       statusbar_item_default_signals(rec);
+
+       items = g_hash_table_lookup(named_sbar_items, config->name);
+       items = g_slist_append(items, rec);
+        g_hash_table_insert(named_sbar_items, config->name, items);
 
 
+       irssi_set_dirty();
+       rec->dirty = TRUE;
+       bar->dirty = TRUE;
+
+        signal_emit("statusbar item created", 1, rec);
        return rec;
 }
 
        return rec;
 }
 
-void statusbar_item_remove(SBAR_ITEM_REC *item)
+static void statusbar_signal_remove(int signal_id)
 {
 {
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_item);
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_server);
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window);
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window_item);
+}
+
+static void statusbar_item_remove_signal(SBAR_ITEM_REC *item, int signal_id)
+{
+       GSList *list;
+
+        /* update signal -> item hash */
+       list = g_hash_table_lookup(sbar_signal_items,
+                                  GINT_TO_POINTER(signal_id));
+       list = g_slist_remove(list, item);
+       if (list != NULL) {
+               g_hash_table_insert(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_id), list);
+       } else {
+               g_hash_table_remove(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_id));
+                statusbar_signal_remove(signal_id);
+       }
+}
+
+void statusbar_item_destroy(SBAR_ITEM_REC *item)
+{
+       GSList *list;
+
        g_return_if_fail(item != NULL);
 
        g_return_if_fail(item != NULL);
 
-       statusbar_item_destroy(item);
-       if (!quitting) statusbar_redraw_all();
+       item->bar->items = g_slist_remove(item->bar->items, item);
+
+       list = g_hash_table_lookup(named_sbar_items, item->config->name);
+       list = g_slist_remove(list, item);
+       if (list == NULL)
+               g_hash_table_remove(named_sbar_items, item->config->name);
+        else
+               g_hash_table_insert(named_sbar_items, item->config->name, list);
+
+        signal_emit("statusbar item destroyed", 1, item);
+
+       list = g_hash_table_lookup(sbar_item_signals, item);
+        g_hash_table_remove(sbar_item_signals, item);
+
+       while (list != NULL) {
+                statusbar_item_remove_signal(item, GPOINTER_TO_INT(list->data));
+               list = g_slist_remove(list, list->data);
+       }
+
+       g_free(item);
 }
 
 }
 
-static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
+static void statusbar_redraw_needed_items(STATUSBAR_REC *bar)
 {
 {
-       STATUSBAR_REC *rec;
+        WINDOW_REC *old_active_win;
+       GSList *tmp;
+       char *str;
 
 
-       rec = window->statusbar;
-        rec->ypos = window->first_line+window->height;
+       old_active_win = active_win;
+        if (bar->parent_window != NULL)
+               active_win = bar->parent_window->active;
+
+       if (bar->dirty_xpos >= 0) {
+               str = g_strconcat(bar->color, "%>", NULL);
+               gui_printtext(bar->dirty_xpos, bar->real_ypos, str);
+               g_free(str);
+       }
+
+       for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_REC *rec = tmp->data;
+
+               if (rec->dirty ||
+                   (bar->dirty_xpos != -1 &&
+                    rec->xpos >= bar->dirty_xpos)) {
+                        rec->current_size = rec->size;
+                       rec->func(rec, FALSE);
+                       rec->dirty = FALSE;
+               }
+       }
+
+        active_win = old_active_win;
 }
 
 }
 
-void statusbar_init(void)
+void statusbar_redraw_dirty(void)
 {
 {
-       statusbars = NULL;
-       sbars_up = sbars_down = 0;
+       GSList *tmp;
 
 
-       statusbar_items_init();
+       if (statusbar_need_recreate_items) {
+               statusbar_need_recreate_items = FALSE;
+               statusbars_recreate_items();
+       }
+
+       for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *rec = tmp->data;
+
+               if (rec->dirty) {
+                        statusbar_redraw_needed_items(rec);
+                       rec->dirty = FALSE;
+                       rec->dirty_xpos = -1;
+               }
+       }
+}
+
+#define STATUSBAR_IS_VISIBLE(bar, window) \
+       ((bar)->visible == STATUSBAR_VISIBLE_ALWAYS || \
+       (active_mainwin == (window) && \
+        (bar)->visible == STATUSBAR_VISIBLE_ACTIVE) || \
+       (active_mainwin != (window) && \
+        (bar)->visible == STATUSBAR_VISIBLE_INACTIVE))
+
+static void statusbars_remove_unvisible(MAIN_WINDOW_REC *window)
+{
+       GSList *tmp, *next;
+
+       for (tmp = window->statusbars; tmp != NULL; tmp = next) {
+               STATUSBAR_REC *bar = tmp->data;
+
+               next = tmp->next;
+                if (!STATUSBAR_IS_VISIBLE(bar->config, window))
+                        statusbar_destroy(bar);
+       }
+}
+
+static void statusbars_add_visible(MAIN_WINDOW_REC *window)
+{
+       STATUSBAR_GROUP_REC *group;
+        STATUSBAR_REC *bar;
+       GSList *tmp;
+
+        group = active_statusbar_group;
+       for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_CONFIG_REC *config = tmp->data;
+
+               if (config->type == STATUSBAR_TYPE_WINDOW &&
+                   STATUSBAR_IS_VISIBLE(config, window) &&
+                   statusbar_find(group, config->name, window) == NULL) {
+                       bar = statusbar_create(group, config, window);
+                       statusbar_redraw(bar, TRUE);
+               }
+       }
+}
+
+static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window)
+{
+       while (window->statusbars != NULL) {
+               STATUSBAR_REC *bar = window->statusbars->data;
+
+               bar->parent_window->statusbars =
+                       g_slist_remove(bar->parent_window->statusbars, bar);
+               bar->parent_window = NULL;
+               statusbar_destroy(bar);
+       }
+}
+
+static void sig_window_changed(void)
+{
+       GSList *tmp;
+
+       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+               MAIN_WINDOW_REC *rec = tmp->data;
+
+               statusbars_remove_unvisible(rec);
+                statusbars_add_visible(rec);
+       }
+}
+
+static void sig_gui_window_created(WINDOW_REC *window)
+{
+        statusbars_add_visible(WINDOW_MAIN(window));
+}
+
+static void statusbar_item_def_destroy(void *key, void *value)
+{
+       g_free(key);
+        g_free(value);
+}
+
+static void statusbar_signal_item_destroy(void *key, GSList *value)
+{
+       while (value != NULL) {
+               statusbar_signal_remove(GPOINTER_TO_INT(value->data));
+                value->data = g_slist_remove(value, value->data);
+       }
+}
+
+static void statusbar_item_signal_destroy(void *key, GSList *value)
+{
+        g_slist_free(value);
+}
+
+static void sig_setup_reload(void)
+{
+       /* statusbar-config.c recreates root statusbars,
+          we need to create window-statusbars */
+        g_slist_foreach(mainwindows, (GFunc) statusbars_add_visible, NULL);
+}
+
+void statusbar_init(void)
+{
+        statusbar_need_recreate_items = FALSE;
+       statusbar_groups = NULL;
+       active_statusbar_group = NULL;
+       sbar_item_defs = g_hash_table_new((GHashFunc) g_str_hash,
+                                         (GCompareFunc) g_str_equal);
+       sbar_item_funcs = g_hash_table_new((GHashFunc) g_str_hash,
+                                          (GCompareFunc) g_str_equal);
+       sbar_signal_items = g_hash_table_new((GHashFunc) g_direct_hash,
+                                            (GCompareFunc) g_direct_equal);
+       sbar_item_signals = g_hash_table_new((GHashFunc) g_direct_hash,
+                                            (GCompareFunc) g_direct_equal);
+       named_sbar_items = g_hash_table_new((GHashFunc) g_str_hash,
+                                           (GCompareFunc) g_str_equal);
+
+        signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
        signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_add("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
+       signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
+       signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
+       signal_add_last("setup reread", (SIGNAL_FUNC) sig_setup_reload);
+
+       statusbar_items_init();
+       statusbar_config_init(); /* signals need to be before this call */
 }
 
 void statusbar_deinit(void)
 {
 }
 
 void statusbar_deinit(void)
 {
-       statusbar_items_deinit();
+       while (statusbar_groups != NULL)
+               statusbar_group_destroy(statusbar_groups->data);
 
 
-       while (statusbars != NULL)
-               statusbar_destroy(statusbars->data);
+       g_hash_table_foreach(sbar_item_defs,
+                            (GHFunc) statusbar_item_def_destroy, NULL);
+       g_hash_table_destroy(sbar_item_defs);
 
 
+       g_hash_table_foreach(sbar_item_funcs, (GHFunc) g_free, NULL);
+       g_hash_table_destroy(sbar_item_funcs);
+
+       g_hash_table_foreach(sbar_signal_items,
+                            (GHFunc) statusbar_signal_item_destroy, NULL);
+       g_hash_table_destroy(sbar_signal_items);
+       g_hash_table_foreach(sbar_item_signals,
+                            (GHFunc) statusbar_item_signal_destroy, NULL);
+       g_hash_table_destroy(sbar_item_signals);
+       g_hash_table_destroy(named_sbar_items);
+
+        signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
        signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_remove("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
+       signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
+       signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
+       signal_remove("setup reread", (SIGNAL_FUNC) sig_setup_reload);
+
+       statusbar_items_deinit();
+       statusbar_config_deinit();
 }
 }
index 32d35b8238687a9f658349d55eb6b63ebf3491b6..0d61c059bad6ade7b67cf22415a53869dc8817c6 100644 (file)
 
 #include "mainwindows.h"
 
 
 #include "mainwindows.h"
 
-#define SBAR_PRIORITY_HIGH     100
-#define SBAR_PRIORITY_NORMAL   0
-#define SBAR_PRIORITY_LOW      -100
-
-enum {
-       STATUSBAR_POS_UP,
-       STATUSBAR_POS_MIDDLE,
-       STATUSBAR_POS_DOWN
-};
+#define STATUSBAR_PRIORITY_HIGH                100
+#define STATUSBAR_PRIORITY_NORMAL      0
+#define STATUSBAR_PRIORITY_LOW         -100
 
 typedef struct SBAR_ITEM_REC SBAR_ITEM_REC;
 typedef void (*STATUSBAR_FUNC) (SBAR_ITEM_REC *item, int get_size_only);
 
 
 typedef struct SBAR_ITEM_REC SBAR_ITEM_REC;
 typedef void (*STATUSBAR_FUNC) (SBAR_ITEM_REC *item, int get_size_only);
 
+/* type */
+#define STATUSBAR_TYPE_ROOT    1
+#define STATUSBAR_TYPE_WINDOW  2
+
+/* placement */
+#define STATUSBAR_TOP          1
+#define STATUSBAR_BOTTOM       2
+
+/* visible */
+#define STATUSBAR_VISIBLE_ALWAYS        1
+#define STATUSBAR_VISIBLE_ACTIVE        2
+#define STATUSBAR_VISIBLE_INACTIVE      3
+
 typedef struct {
 typedef struct {
-       MAIN_WINDOW_REC *window;
+       char *name;
+        GSList *config_bars;
+       GSList *bars;
+} STATUSBAR_GROUP_REC;
 
 
-       int pos;
-       int line;
+typedef struct {
+       char *name;
 
 
-        char *color_string;
-        int color;
+       int type; /* root/window */
+       int placement; /* top/bottom */
+       int position; /* the higher the number, the lower it is in screen */
+       int visible; /* active/inactive/always */
 
 
-       int ypos; /* real position in screen at the moment */
        GSList *items;
        GSList *items;
+} STATUSBAR_CONFIG_REC;
+
+typedef struct {
+       STATUSBAR_GROUP_REC *group;
+       STATUSBAR_CONFIG_REC *config;
+
+       MAIN_WINDOW_REC *parent_window; /* if config->type == STATUSBAR_TYPE_WINDOW */
+        GSList *items;
+
+       char *color; /* background color */
+       int real_ypos; /* real Y-position in screen at the moment */
+
+       int dirty:1;
+        int dirty_xpos; /* -1 = only redraw some items, >= 0 = redraw all items after from xpos */
 } STATUSBAR_REC;
 
 } STATUSBAR_REC;
 
+typedef struct {
+       char *name;
+       char *value; /* if non-NULL, overrides the default */
+
+       int priority;
+       unsigned int right_alignment:1;
+} SBAR_ITEM_CONFIG_REC;
+
 struct SBAR_ITEM_REC {
        STATUSBAR_REC *bar;
 struct SBAR_ITEM_REC {
        STATUSBAR_REC *bar;
-       STATUSBAR_FUNC func;
+       SBAR_ITEM_CONFIG_REC *config;
+        STATUSBAR_FUNC func;
 
        /* what item wants */
 
        /* what item wants */
-        int priority;
        int min_size, max_size;
        int min_size, max_size;
-       unsigned int right_justify:1;
 
        /* what item gets */
 
        /* what item gets */
-        int xpos, size;
+       int xpos, size;
+
+        int current_size; /* item size currently in screen */
+       int dirty:1;
 };
 
 };
 
-/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */
-STATUSBAR_REC *statusbar_create(int pos, int ypos);
-void statusbar_destroy(STATUSBAR_REC *bar);
+extern GSList *statusbar_groups;
+extern STATUSBAR_GROUP_REC *active_statusbar_group;
+
+void statusbar_item_register(const char *name, const char *value,
+                            STATUSBAR_FUNC func);
+void statusbar_item_unregister(const char *name);
 
 
-STATUSBAR_REC *statusbar_find(int pos, int line);
+STATUSBAR_GROUP_REC *statusbar_group_create(const char *name);
+void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec);
+STATUSBAR_GROUP_REC *statusbar_group_find(const char *name);
+
+STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group,
+                                STATUSBAR_CONFIG_REC *config,
+                                MAIN_WINDOW_REC *parent_window);
+void statusbar_destroy(STATUSBAR_REC *bar);
+STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
+                             MAIN_WINDOW_REC *window);
 
 SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
 
 SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
-                                    int priority, int right_justify,
-                                    STATUSBAR_FUNC func);
-void statusbar_item_remove(SBAR_ITEM_REC *item);
+                                    SBAR_ITEM_CONFIG_REC *config);
+void statusbar_item_destroy(SBAR_ITEM_REC *item);
+
+void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
+                                   const char *str, const char *data,
+                                   int escape_vars);
 
 /* redraw statusbar, NULL = all */
 
 /* redraw statusbar, NULL = all */
-void statusbar_redraw(STATUSBAR_REC *bar);
+void statusbar_redraw(STATUSBAR_REC *bar, int force);
 void statusbar_item_redraw(SBAR_ITEM_REC *item);
 void statusbar_item_redraw(SBAR_ITEM_REC *item);
+void statusbar_items_redraw(const char *name);
+
+void statusbar_recreate_items(STATUSBAR_REC *bar);
+void statusbars_recreate_items(void);
+
+void statusbar_redraw_dirty(void);
 
 void statusbar_init(void);
 void statusbar_deinit(void);
 
 void statusbar_init(void);
 void statusbar_deinit(void);
diff --git a/apps/irssi/src/fe-text/term-curses.c b/apps/irssi/src/fe-text/term-curses.c
new file mode 100644 (file)
index 0000000..69e2b92
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.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;
+int term_width, term_height;
+
+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 (!term_use_colors)
+               attr = (color & 0x70) ? A_REVERSE : 0;
+       else if (((color & 0x0f) == 8) && (color & ATTR_BOLD) == 0)
+                attr = (A_DIM | COLOR_PAIR(63));
+       else if ((color & 0x77) == 0)
+               attr = A_NORMAL;
+       else {
+               if (color & ATTR_RESETFG) {
+                       color &= ~0x0f;
+                       color |= settings_get_int("default_color");
+               }
+               attr = (COLOR_PAIR((color&7) + (color&0x70)/2));
+       }
+
+       if ((color & 0x08) || (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;
+        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, int chr)
+{
+        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(), SIGSTOP);
+        term_init_int();
+       irssi_redraw();
+}
+
+int term_gets(unsigned char *buffer, int size)
+{
+       int key, count;
+
+       for (count = 0; count < size; ) {
+               key = getch();
+#ifdef KEY_RESIZE
+               if (key == KEY_RESIZE)
+                       continue;
+#endif
+
+               if (key == ERR)
+                       break;
+
+                buffer[count] = key;
+                count++;
+       }
+
+       return count;
+}
diff --git a/apps/irssi/src/fe-text/term-dummy.c b/apps/irssi/src/fe-text/term-dummy.c
new file mode 100644 (file)
index 0000000..a4f5c09
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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/apps/irssi/src/fe-text/term-terminfo.c b/apps/irssi/src/fe-text/term-terminfo.c
new file mode 100644 (file)
index 0000000..b900e2e
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ term-terminfo.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "term.h"
+#include "terminfo-core.h"
+
+#include <signal.h>
+
+struct _TERM_WINDOW {
+        /* Terminal to use for window */
+       TERM_REC *term;
+
+        /* Area for window in terminal */
+       int x, y;
+       int width, height;
+};
+
+TERM_WINDOW *root_window;
+int term_width, term_height, term_detached;
+
+static char *term_lines_empty; /* 1 if line is entirely empty */
+static int vcmove, vcx, vcy, curs_visible;
+static int crealx, crealy, cforcemove;
+static int curs_x, curs_y;
+static int auto_detach;
+
+static int last_fg, last_bg, last_attrs;
+
+static int redraw_needed, redraw_tag;
+static int freeze_counter;
+
+/* SIGCONT handler */
+static void sig_cont(int p)
+{
+        redraw_needed = TRUE;
+       terminfo_cont(current_term);
+}
+
+static int redraw_timeout(void)
+{
+       if (redraw_needed) {
+               irssi_redraw();
+                redraw_needed = FALSE;
+       }
+
+        return 1;
+}
+
+int term_init(void)
+{
+        struct sigaction act;
+
+       last_fg = last_bg = -1;
+       last_attrs = 0;
+       vcx = vcy = 0; crealx = crealy = -1;
+       vcmove = FALSE; cforcemove = TRUE;
+        curs_visible = TRUE;
+
+       current_term = terminfo_core_init(stdin, stdout);
+       if (current_term == NULL)
+               return FALSE;
+
+        /* grab CONT signal */
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       act.sa_handler = sig_cont;
+       sigaction(SIGCONT, &act, NULL);
+        redraw_tag = g_timeout_add(500, (GSourceFunc) redraw_timeout, NULL);
+
+       curs_x = curs_y = 0;
+       term_width = current_term->width;
+       term_height = current_term->height;
+       root_window = term_window_create(0, 0, term_width, term_height);
+        term_detached = FALSE;
+
+        term_lines_empty = g_new0(char, term_height);
+
+       term_common_init();
+        return TRUE;
+}
+
+void term_deinit(void)
+{
+       g_source_remove(redraw_tag);
+
+       term_common_deinit();
+        terminfo_core_deinit(current_term);
+}
+
+static void term_move_real(void)
+{
+       if (term_detached) return;
+
+       if (vcx != crealx || vcy != crealy || cforcemove) {
+               if (curs_visible) {
+                       terminfo_set_cursor_visible(FALSE);
+                       curs_visible = FALSE;
+               }
+
+               if (cforcemove) {
+                       crealx = crealy = -1;
+                       cforcemove = FALSE;
+               }
+               terminfo_move_relative(crealx, crealy, vcx, vcy);
+                crealx = vcx; crealy = vcy;
+       }
+
+        vcmove = FALSE;
+}
+
+/* Cursor position is unknown - move it immediately to known position */
+static void term_move_reset(int x, int y)
+{
+       if (x >= term_width) x = term_width-1;
+       if (y >= term_height) y = term_height-1;
+
+       vcx = x; vcy = y;
+        cforcemove = TRUE;
+        term_move_real();
+}
+
+/* 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)
+{
+       if (width < 0 || height < 0) {
+               terminfo_resize(current_term);
+               width = current_term->width;
+                height = current_term->height;
+       }
+
+       if (term_width != width || term_height != height) {
+               term_width = current_term->width = width;
+               term_height = current_term->height = height;
+               term_window_move(root_window, 0, 0, term_width, term_height);
+
+                g_free(term_lines_empty);
+               term_lines_empty = g_new0(char, term_height);
+       }
+
+        term_move_reset(0, 0);
+}
+
+void term_resize_final(int width, int height)
+{
+}
+
+/* Returns TRUE if terminal has colors */
+int term_has_colors(void)
+{
+        return current_term->has_colors;
+}
+
+/* Force the colors on any way you can */
+void term_force_colors(int set)
+{
+       if (term_detached) return;
+
+       terminfo_setup_colors(current_term, set);
+}
+
+/* Clear screen */
+void term_clear(void)
+{
+       if (term_detached) return;
+
+        term_set_color(root_window, 0);
+       terminfo_clear();
+        term_move_reset(0, 0);
+
+       memset(term_lines_empty, 1, term_height);
+}
+
+/* Beep */
+void term_beep(void)
+{
+       if (term_detached) return;
+
+        terminfo_beep(current_term);
+}
+
+/* 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->term = current_term;
+       window->x = x; window->y = y;
+       window->width = width; window->height = height;
+        return window;
+}
+
+/* Destroy a terminal window */
+void term_window_destroy(TERM_WINDOW *window)
+{
+        g_free(window);
+}
+
+/* Move/resize a window */
+void term_window_move(TERM_WINDOW *window, int x, int y,
+                     int width, int height)
+{
+       window->x = x;
+       window->y = y;
+       window->width = width;
+        window->height = height;
+}
+
+/* Clear window */
+void term_window_clear(TERM_WINDOW *window)
+{
+       int y;
+
+       if (term_detached) return;
+
+        terminfo_set_normal();
+        if (window->y == 0 && window->height == term_height) {
+               term_clear();
+        } else {
+               for (y = 0; y < window->height; y++) {
+                       term_move(window, 0, y);
+                       term_clrtoeol(window);
+               }
+       }
+}
+
+/* Scroll window up/down */
+void term_window_scroll(TERM_WINDOW *window, int count)
+{
+       int y;
+
+       if (term_detached) return;
+
+       terminfo_scroll(window->y, window->y+window->height-1, count);
+        term_move_reset(vcx, vcy);
+
+        /* set the newly scrolled area dirty */
+       for (y = 0; y < window->height; y++)
+               term_lines_empty[window->y+y] = FALSE;
+}
+
+/* Change active color */
+void term_set_color(TERM_WINDOW *window, int col)
+{
+       int set_normal;
+
+       if (term_detached) return;
+
+        set_normal = ((col & ATTR_RESETFG) && last_fg != -1) ||
+               ((col & ATTR_RESETBG) && last_bg != -1);
+       if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) ||
+           ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) {
+               /* we'll need to get rid of bold/blink - this can only be
+                  done with setting the default color */
+               set_normal = TRUE;
+       }
+
+       if (set_normal) {
+               last_fg = last_bg = -1;
+                last_attrs = 0;
+               terminfo_set_normal();
+       }
+
+       if (!term_use_colors && (col & 0xf0) != 0)
+               col |= ATTR_REVERSE;
+
+       /* reversed text (use standout) */
+       if (col & ATTR_REVERSE) {
+               if ((last_attrs & ATTR_REVERSE) == 0)
+                       terminfo_set_standout(TRUE);
+       } else if (last_attrs & ATTR_REVERSE)
+               terminfo_set_standout(FALSE);
+
+       /* set foreground color */
+       if ((col & 0x0f) != last_fg &&
+           ((col & 0x0f) != 0 || (col & ATTR_RESETFG) == 0)) {
+                if (term_use_colors) {
+                       last_fg = col & 0x0f;
+                       terminfo_set_fg(last_fg);
+               }
+       }
+
+       /* set background color */
+       if (col & ATTR_BLINK)
+               col |= 0x80;
+       else if (col & 0x80)
+               col |= ATTR_BLINK;
+
+       if ((col & 0xf0) >> 4 != last_bg &&
+           ((col & 0xf0) != 0 || (col & ATTR_RESETBG) == 0)) {
+                if (term_use_colors) {
+                       last_bg = (col & 0xf0) >> 4;
+                       terminfo_set_bg(last_bg);
+               }
+       }
+
+       /* bold */
+       if (col & 0x08)
+               col |= ATTR_BOLD;
+       else if (col & ATTR_BOLD)
+               terminfo_set_bold();
+
+       /* underline */
+       if (col & ATTR_UNDERLINE) {
+               if ((last_attrs & ATTR_UNDERLINE) == 0)
+                       terminfo_set_uline(TRUE);
+       } else if (last_attrs & ATTR_UNDERLINE)
+               terminfo_set_uline(FALSE);
+
+        last_attrs = col & ~0xff;
+}
+
+void term_move(TERM_WINDOW *window, int x, int y)
+{
+       vcmove = TRUE;
+       vcx = x+window->x;
+        vcy = y+window->y;
+
+       if (vcx >= term_width)
+               vcx = term_width-1;
+       if (vcy >= term_height)
+                vcy = term_height-1;
+}
+
+static void term_printed_text(int count)
+{
+       term_lines_empty[vcy] = FALSE;
+
+       /* if we continued writing past the line, wrap to next line.
+          However, next term_move() really shouldn't try to cache
+          the move, otherwise terminals would try to combine the
+          last word in upper line with first word in lower line. */
+        cforcemove = TRUE;
+       vcx += count;
+       while (vcx >= term_width) {
+               vcx -= term_width;
+               if (vcy < term_height) vcy++;
+               if (vcx > 0) term_lines_empty[vcy] = FALSE;
+       }
+}
+
+void term_addch(TERM_WINDOW *window, int chr)
+{
+       if (term_detached) return;
+
+       if (vcmove) term_move_real();
+        term_printed_text(1);
+       if (vcy != term_height || vcx != 0)
+               putc(chr, window->term->out);
+}
+
+void term_addstr(TERM_WINDOW *window, const char *str)
+{
+       int len;
+
+       if (term_detached) return;
+
+       if (vcmove) term_move_real();
+       len = strlen(str);
+        term_printed_text(len);
+
+       if (vcy != term_height || vcx != 0)
+               fputs(str, window->term->out);
+       else
+               fwrite(str, 1, len-1, window->term->out);
+}
+
+void term_clrtoeol(TERM_WINDOW *window)
+{
+       if (term_detached) return;
+
+       /* clrtoeol() doesn't necessarily understand colors */
+       if (last_fg == -1 && last_bg == -1 &&
+           (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE)) == 0) {
+               if (!term_lines_empty[vcy]) {
+                       if (vcmove) term_move_real();
+                       terminfo_clrtoeol();
+                       if (vcx == 0) term_lines_empty[vcy] = TRUE;
+               }
+       } else if (vcx < term_width) {
+               /* we'll need to fill the line ourself. */
+               if (vcmove) term_move_real();
+               terminfo_repeat(' ', term_width-vcx);
+               terminfo_move(vcx, vcy);
+                term_lines_empty[vcy] = FALSE;
+       }
+}
+
+void term_move_cursor(int x, int y)
+{
+       curs_x = x;
+        curs_y = y;
+}
+
+void term_refresh(TERM_WINDOW *window)
+{
+       if (term_detached || freeze_counter > 0)
+               return;
+
+       term_move(root_window, curs_x, curs_y);
+       term_move_real();
+
+       if (!curs_visible) {
+               terminfo_set_cursor_visible(TRUE);
+                curs_visible = TRUE;
+       }
+       term_set_color(window, ATTR_RESET);
+       fflush(window != NULL ? window->term->out : current_term->out);
+}
+
+void term_refresh_freeze(void)
+{
+        freeze_counter++;
+
+       if (!term_detached && curs_visible) {
+               terminfo_set_cursor_visible(FALSE);
+                curs_visible = FALSE;
+       }
+}
+
+void term_refresh_thaw(void)
+{
+       if (--freeze_counter == 0)
+                term_refresh(NULL);
+}
+
+void term_auto_detach(int set)
+{
+        auto_detach = set;
+}
+
+void term_detach(void)
+{
+       terminfo_stop(current_term);
+
+        fclose(current_term->in);
+        fclose(current_term->out);
+
+       current_term->in = NULL;
+       current_term->out = NULL;
+        term_detached = TRUE;
+}
+
+void term_attach(FILE *in, FILE *out)
+{
+       current_term->in = in;
+       current_term->out = out;
+        term_detached = FALSE;
+
+       terminfo_cont(current_term);
+       irssi_redraw();
+}
+
+void term_stop(void)
+{
+       if (term_detached) {
+               kill(getpid(), SIGSTOP);
+       } else {
+               terminfo_stop(current_term);
+               kill(getpid(), SIGSTOP);
+               terminfo_cont(current_term);
+               irssi_redraw();
+       }
+}
+
+int term_gets(unsigned char *buffer, int size)
+{
+       int ret;
+
+       if (term_detached)
+               return 0;
+
+        /* fread() doesn't work */
+        ret = read(fileno(current_term->in), buffer, size);
+       if (ret == 0) {
+               /* EOF - terminal got lost */
+               if (auto_detach)
+                        term_detach();
+               ret = -1;
+       } else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
+               ret = 0;
+
+       return ret;
+}
diff --git a/apps/irssi/src/fe-text/term.c b/apps/irssi/src/fe-text/term.c
new file mode 100644 (file)
index 0000000..c32b249
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ term.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "settings.h"
+
+#include "term.h"
+#include "mainwindows.h"
+
+#ifdef HAVE_SYS_IOCTL_H
+#  include <sys/ioctl.h>
+#endif
+#include <signal.h>
+#include <termios.h>
+
+#define MIN_SCREEN_WIDTH 20
+
+int term_use_colors;
+
+static int force_colors;
+static int resize_dirty;
+
+/* Resize the terminal if needed */
+void term_resize_dirty(void)
+{
+#ifdef TIOCGWINSZ
+       struct winsize ws;
+#endif
+        int width, height;
+
+       if (!resize_dirty)
+               return;
+
+        resize_dirty = FALSE;
+
+#ifdef TIOCGWINSZ
+       /* Get new window size */
+       if (ioctl(0, TIOCGWINSZ, &ws) < 0)
+               return;
+
+       if (ws.ws_row == term_height && ws.ws_col == term_width) {
+               /* Same size, abort. */
+               return;
+       }
+
+       if (ws.ws_col < MIN_SCREEN_WIDTH)
+               ws.ws_col = MIN_SCREEN_WIDTH;
+
+       width = ws.ws_col;
+        height = ws.ws_row;
+#else
+        width = height = -1;
+#endif
+        term_resize(width, height);
+       mainwindows_resize(term_width, term_height);
+        term_resize_final(width, height);
+}
+
+#ifdef SIGWINCH
+static void sig_winch(int p)
+{
+        irssi_set_dirty();
+        resize_dirty = TRUE;
+}
+#endif
+
+static void cmd_resize(void)
+{
+       resize_dirty = TRUE;
+        term_resize_dirty();
+}
+
+static void read_settings(void)
+{
+       int old_colors = term_use_colors;
+
+        term_auto_detach(settings_get_bool("term_auto_detach"));
+
+       if (force_colors != settings_get_bool("term_force_colors")) {
+               force_colors = settings_get_bool("term_force_colors");
+               term_force_colors(force_colors);
+       }
+
+       term_use_colors = settings_get_bool("colors") &&
+               (force_colors || term_has_colors());
+
+       if (term_use_colors != old_colors)
+               irssi_redraw();
+}
+
+void term_common_init(void)
+{
+#ifdef SIGWINCH
+       struct sigaction act;
+#endif
+       settings_add_bool("lookandfeel", "colors", TRUE);
+       settings_add_bool("lookandfeel", "term_force_colors", FALSE);
+        settings_add_bool("lookandfeel", "term_auto_detach", FALSE);
+        settings_add_bool("lookandfeel", "term_utf8", FALSE);
+
+       force_colors = FALSE;
+       term_use_colors = term_has_colors() && settings_get_bool("colors");
+        read_settings();
+
+       signal_add("beep", (SIGNAL_FUNC) term_beep);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+       command_bind("resize", NULL, (SIGNAL_FUNC) cmd_resize);
+
+#ifdef SIGWINCH
+       sigemptyset (&act.sa_mask);
+       act.sa_flags = 0;
+       act.sa_handler = sig_winch;
+       sigaction(SIGWINCH, &act, NULL);
+#endif
+}
+
+void term_common_deinit(void)
+{
+       command_unbind("resize", (SIGNAL_FUNC) cmd_resize);
+       signal_remove("beep", (SIGNAL_FUNC) term_beep);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-text/term.h b/apps/irssi/src/fe-text/term.h
new file mode 100644 (file)
index 0000000..9cd1b15
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __TERM_H
+#define __TERM_H
+
+typedef struct _TERM_WINDOW TERM_WINDOW;
+
+#define ATTR_RESETFG   0x0100
+#define ATTR_RESETBG   0x0200
+#define ATTR_BOLD      0x0400
+#define ATTR_BLINK      0x0800
+#define ATTR_UNDERLINE 0x1000
+#define ATTR_REVERSE   0x2000
+
+#define ATTR_RESET     (ATTR_RESETFG|ATTR_RESETBG)
+
+#define ATTR_NOCOLORS (ATTR_UNDERLINE|ATTR_REVERSE)
+
+#ifdef WANT_BIG5
+/* XXX I didn't check the encoding range of big5+. This is standard big5. */
+#  define is_big5_los(lo) (((char)0x40<=lo)&&(lo<=(char)0x7E)) /* standard */
+#  define is_big5_lox(lo) (((char)0x80<=lo)&&(lo<=(char)0xFE)) /* extended */
+#  define is_big5_hi(hi)  (((char)0x81<=hi)&&(hi<=(char)0xFE))
+#  define is_big5(hi,lo) is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo))
+#endif
+
+extern TERM_WINDOW *root_window;
+extern int term_width, term_height, term_use_colors, term_detached;
+
+/* Initialize / deinitialize terminal */
+int term_init(void);
+void term_deinit(void);
+
+/* 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);
+void term_resize_final(int width, int height);
+/* Resize the terminal if needed */
+void term_resize_dirty(void);
+
+/* Returns TRUE if terminal has colors */
+int term_has_colors(void);
+/* Force the colors on any way you can */
+void term_force_colors(int set);
+
+/* Clear screen */
+void term_clear(void);
+/* Beep */
+void term_beep(void);
+
+/* Create a new window in terminal */
+TERM_WINDOW *term_window_create(int x, int y, int width, int height);
+/* Destroy a terminal window */
+void term_window_destroy(TERM_WINDOW *window);
+
+/* Move/resize window */
+void term_window_move(TERM_WINDOW *window, int x, int y,
+                     int width, int height);
+/* Clear window */
+void term_window_clear(TERM_WINDOW *window);
+/* Scroll window up/down */
+void term_window_scroll(TERM_WINDOW *window, int count);
+
+void term_set_color(TERM_WINDOW *window, int col);
+
+void term_move(TERM_WINDOW *window, int x, int y);
+void term_addch(TERM_WINDOW *window, int chr);
+void term_addstr(TERM_WINDOW *window, const char *str);
+void term_clrtoeol(TERM_WINDOW *window);
+
+void term_move_cursor(int x, int y);
+
+void term_refresh_freeze(void);
+void term_refresh_thaw(void);
+void term_refresh(TERM_WINDOW *window);
+
+/* Automatically detach irssi when terminal is lost */
+void term_auto_detach(int set);
+void term_detach(void);
+void term_attach(FILE *in, FILE *out);
+
+void term_stop(void);
+int term_gets(unsigned char *buffer, int size);
+
+/* internal */
+void term_common_init(void);
+void term_common_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-text/terminfo-core.c b/apps/irssi/src/fe-text/terminfo-core.c
new file mode 100644 (file)
index 0000000..21a7b0a
--- /dev/null
@@ -0,0 +1,658 @@
+#include "module.h"
+#include "signals.h"
+#include "terminfo-core.h"
+
+#ifndef _POSIX_VDISABLE
+#  define _POSIX_VDISABLE 0
+#endif
+
+#define tput(s) tputs(s, 0, term_putchar)
+inline static int term_putchar(int c)
+{
+        return fputc(c, current_term->out);
+}
+
+/* Don't bother including curses.h because of these -
+   they might not even be defined there */
+char *tparm();
+int tputs();
+
+#ifdef HAVE_TERMINFO
+int setupterm();
+char *tigetstr();
+int tigetnum();
+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
+#define CAP_TYPE_STR   2
+
+typedef struct {
+        const char *ti_name; /* terminfo name */
+       const char *tc_name; /* termcap name */
+       int type;
+        void *ptr;
+} TERMINFO_REC;
+
+TERM_REC *current_term;
+static TERM_REC temp_term; /* not really used for anything */
+
+/* Define only what we might need */
+static TERMINFO_REC tcaps[] = {
+        /* Terminal size */
+       { "cols",       "co",   CAP_TYPE_INT,   &temp_term.width },
+       { "lines",      "li",   CAP_TYPE_INT,   &temp_term.height },
+
+        /* Cursor movement */
+       { "smcup",      "ti",   CAP_TYPE_STR,   &temp_term.TI_smcup },
+       { "rmcup",      "te",   CAP_TYPE_STR,   &temp_term.TI_rmcup },
+       { "cup",        "cm",   CAP_TYPE_STR,   &temp_term.TI_cup },
+       { "hpa",        "ch",   CAP_TYPE_STR,   &temp_term.TI_hpa },
+       { "vpa",        "vh",   CAP_TYPE_STR,   &temp_term.TI_vpa },
+       { "cub1",       "le",   CAP_TYPE_STR,   &temp_term.TI_cub1 },
+       { "cuf1",       "nd",   CAP_TYPE_STR,   &temp_term.TI_cuf1 },
+       { "civis",      "vi",   CAP_TYPE_STR,   &temp_term.TI_civis },
+       { "cnorm",      "ve",   CAP_TYPE_STR,   &temp_term.TI_cnorm },
+
+        /* Scrolling */
+       { "csr",        "cs",   CAP_TYPE_STR,   &temp_term.TI_csr },
+       { "wind",       "wi",   CAP_TYPE_STR,   &temp_term.TI_wind },
+       { "ri",         "sr",   CAP_TYPE_STR,   &temp_term.TI_ri },
+       { "rin",        "SR",   CAP_TYPE_STR,   &temp_term.TI_rin },
+       { "ind",        "sf",   CAP_TYPE_STR,   &temp_term.TI_ind },
+       { "indn",       "SF",   CAP_TYPE_STR,   &temp_term.TI_indn },
+       { "il",         "AL",   CAP_TYPE_STR,   &temp_term.TI_il },
+       { "il1",        "al",   CAP_TYPE_STR,   &temp_term.TI_il1 },
+       { "dl",         "DL",   CAP_TYPE_STR,   &temp_term.TI_dl },
+       { "dl1",        "dl",   CAP_TYPE_STR,   &temp_term.TI_dl1 },
+
+       /* Clearing screen */
+       { "clear",      "cl",   CAP_TYPE_STR,   &temp_term.TI_clear },
+       { "ed",         "cd",   CAP_TYPE_STR,   &temp_term.TI_ed },
+
+        /* Clearing to end of line */
+       { "el",         "ce",   CAP_TYPE_STR,   &temp_term.TI_el },
+
+        /* Repeating character */
+       { "rep",        "rp",   CAP_TYPE_STR,   &temp_term.TI_rep },
+
+       /* Colors */
+       { "sgr0",       "me",   CAP_TYPE_STR,   &temp_term.TI_sgr0 },
+       { "smul",       "us",   CAP_TYPE_STR,   &temp_term.TI_smul },
+       { "rmul",       "ue",   CAP_TYPE_STR,   &temp_term.TI_rmul },
+       { "smso",       "so",   CAP_TYPE_STR,   &temp_term.TI_smso },
+       { "rmso",       "se",   CAP_TYPE_STR,   &temp_term.TI_rmso },
+       { "bold",       "md",   CAP_TYPE_STR,   &temp_term.TI_bold },
+       { "blink",      "mb",   CAP_TYPE_STR,   &temp_term.TI_blink },
+       { "setaf",      "AF",   CAP_TYPE_STR,   &temp_term.TI_setaf },
+       { "setab",      "AB",   CAP_TYPE_STR,   &temp_term.TI_setab },
+       { "setf",       "Sf",   CAP_TYPE_STR,   &temp_term.TI_setf },
+       { "setb",       "Sb",   CAP_TYPE_STR,   &temp_term.TI_setb },
+
+        /* Beep */
+       { "bel",        "bl",   CAP_TYPE_STR,   &temp_term.TI_bel },
+};
+
+/* Move cursor (cursor_address / cup) */
+static void _move_cup(TERM_REC *term, int x, int y)
+{
+       tput(tparm(term->TI_cup, y, x));
+}
+
+/* Move cursor (column_address+row_address / hpa+vpa) */
+static void _move_pa(TERM_REC *term, int x, int y)
+{
+       tput(tparm(term->TI_hpa, x));
+       tput(tparm(term->TI_vpa, y));
+}
+
+/* Move cursor from a known position */
+static void _move_relative(TERM_REC *term, int oldx, int oldy, int x, int y)
+{
+       if (oldx == 0 && x == 0 && y == oldy+1) {
+               /* move to beginning of next line -
+                  hope this works everywhere */
+               tput("\r\n");
+                return;
+       }
+
+       if (oldx > 0 && y == oldy) {
+                /* move cursor left/right */
+               if (x == oldx-1 && term->TI_cub1) {
+                       tput(tparm(term->TI_cub1));
+                        return;
+               }
+               if (x == oldx+1 && y == oldy && term->TI_cuf1) {
+                       tput(tparm(term->TI_cuf1));
+                        return;
+               }
+       }
+
+        /* fallback to absolute positioning */
+       if (term->TI_cup) {
+               tput(tparm(term->TI_cup, y, x));
+                return;
+       }
+
+       if (oldy != y)
+               tput(tparm(term->TI_vpa, y));
+        if (oldx != x)
+               tput(tparm(term->TI_hpa, x));
+}
+
+/* Set cursor visible/invisible */
+static void _set_cursor_visible(TERM_REC *term, int set)
+{
+       tput(tparm(set ? term->TI_cnorm : term->TI_civis));
+}
+
+#define scroll_region_setup(term, y1, y2) \
+       if ((term)->TI_csr != NULL) \
+               tput(tparm((term)->TI_csr, y1, y2)); \
+       else if ((term)->TI_wind != NULL) \
+               tput(tparm((term)->TI_wind, y1, y2, 0, (term)->width-1));
+
+/* Scroll (change_scroll_region+parm_rindex+parm_index / csr+rin+indn) */
+static void _scroll_region(TERM_REC *term, int y1, int y2, int count)
+{
+        /* setup the scrolling region to wanted area */
+        scroll_region_setup(term, y1, y2);
+
+       term->move(term, 0, y1);
+       if (count > 0) {
+               term->move(term, 0, y2);
+               tput(tparm(term->TI_indn, count, count));
+       } else if (count < 0) {
+               term->move(term, 0, y1);
+               tput(tparm(term->TI_rin, -count, -count));
+       }
+
+        /* reset the scrolling region to full screen */
+        scroll_region_setup(term, 0, term->height-1);
+}
+
+/* Scroll (change_scroll_region+scroll_reverse+scroll_forward / csr+ri+ind) */
+static void _scroll_region_1(TERM_REC *term, int y1, int y2, int count)
+{
+       int i;
+
+        /* setup the scrolling region to wanted area */
+        scroll_region_setup(term, y1, y2);
+
+       if (count > 0) {
+               term->move(term, 0, y2);
+               for (i = 0; i < count; i++)
+                       tput(tparm(term->TI_ind));
+       } else if (count < 0) {
+               term->move(term, 0, y1);
+               for (i = count; i < 0; i++)
+                       tput(tparm(term->TI_ri));
+               tput(tparm(term->TI_rin, -count, -count));
+       }
+
+        /* reset the scrolling region to full screen */
+        scroll_region_setup(term, 0, term->height-1);
+}
+
+/* Scroll (parm_insert_line+parm_delete_line / il+dl) */
+static void _scroll_line(TERM_REC *term, int y1, int y2, int count)
+{
+       /* setup the scrolling region to wanted area -
+          this might not necessarily work with il/dl, but at least it
+          looks better if it does */
+        scroll_region_setup(term, y1, y2);
+
+       if (count > 0) {
+               term->move(term, 0, y1);
+               tput(tparm(term->TI_dl, count, count));
+               term->move(term, 0, y2-count+1);
+               tput(tparm(term->TI_il, count, count));
+       } else if (count < 0) {
+               term->move(term, 0, y2+count+1);
+               tput(tparm(term->TI_dl, -count, -count));
+               term->move(term, 0, y1);
+               tput(tparm(term->TI_il, -count, -count));
+       }
+
+        /* reset the scrolling region to full screen */
+        scroll_region_setup(term, 0, term->height-1);
+}
+
+/* Scroll (insert_line+delete_line / il1+dl1) */
+static void _scroll_line_1(TERM_REC *term, int y1, int y2, int count)
+{
+       int i;
+
+       if (count > 0) {
+               term->move(term, 0, y1);
+                for (i = 0; i < count; i++)
+                       tput(tparm(term->TI_dl1));
+               term->move(term, 0, y2-count+1);
+                for (i = 0; i < count; i++)
+                       tput(tparm(term->TI_il1));
+       } else if (count < 0) {
+               term->move(term, 0, y2+count+1);
+               for (i = count; i < 0; i++)
+                       tput(tparm(term->TI_dl1));
+               term->move(term, 0, y1);
+               for (i = count; i < 0; i++)
+                       tput(tparm(term->TI_il1));
+       }
+}
+
+/* Clear screen (clear_screen / clear) */
+static void _clear_screen(TERM_REC *term)
+{
+       tput(tparm(term->TI_clear));
+}
+
+/* Clear screen (clr_eos / ed) */
+static void _clear_eos(TERM_REC *term)
+{
+        term->move(term, 0, 0);
+       tput(tparm(term->TI_ed));
+}
+
+/* Clear screen (parm_delete_line / dl) */
+static void _clear_del(TERM_REC *term)
+{
+        term->move(term, 0, 0);
+       tput(tparm(term->TI_dl, term->height, term->height));
+}
+
+/* Clear screen (delete_line / dl1) */
+static void _clear_del_1(TERM_REC *term)
+{
+       int i;
+
+       term->move(term, 0, 0);
+        for (i = 0; i < term->height; i++)
+               tput(tparm(term->TI_dl1));
+}
+
+/* Clear to end of line (clr_eol / el) */
+static void _clrtoeol(TERM_REC *term)
+{
+       tput(tparm(term->TI_el));
+}
+
+/* Repeat character (rep / rp) */
+static void _repeat(TERM_REC *term, int chr, int count)
+{
+       tput(tparm(term->TI_rep, chr, count));
+}
+
+/* Repeat character (manual) */
+static void _repeat_manual(TERM_REC *term, int chr, int count)
+{
+       while (count > 0) {
+               putc(chr, term->out);
+               count--;
+       }
+}
+
+/* Reset all terminal attributes */
+static void _set_normal(TERM_REC *term)
+{
+       tput(tparm(term->TI_normal));
+}
+
+/* Bold on */
+static void _set_bold(TERM_REC *term)
+{
+       tput(tparm(term->TI_bold));
+}
+
+/* Underline on/off */
+static void _set_uline(TERM_REC *term, int set)
+{
+       tput(tparm(set ? term->TI_smul : term->TI_rmul));
+}
+
+/* Standout on/off */
+static void _set_standout(TERM_REC *term, int set)
+{
+       tput(tparm(set ? term->TI_smso : term->TI_rmso));
+}
+
+/* Change foreground color */
+static void _set_fg(TERM_REC *term, int color)
+{
+       tput(tparm(term->TI_fg[color & 0x0f]));
+}
+
+/* Change background color */
+static void _set_bg(TERM_REC *term, int color)
+{
+       tput(tparm(term->TI_bg[color & 0x0f]));
+}
+
+/* Beep */
+static void _beep(TERM_REC *term)
+{
+       tput(tparm(term->TI_bel));
+}
+
+static void _ignore(TERM_REC *term)
+{
+}
+
+static void _ignore_parm(TERM_REC *term, int param)
+{
+}
+
+static void term_fill_capabilities(TERM_REC *term)
+{
+       int i, ival;
+       char *sval;
+        void *ptr;
+
+#ifndef HAVE_TERMINFO
+       char *tptr = term->buffer2;
+#endif
+       for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) {
+               ptr = (char *) term + (int) ((char *) tcaps[i].ptr - (char *) &temp_term);
+
+               switch (tcaps[i].type) {
+               case CAP_TYPE_FLAG:
+                       ival = term_getflag(tcaps[i]);
+                        *(int *)ptr = ival;
+                        break;
+               case CAP_TYPE_INT:
+                       ival = term_getnum(tcaps[i]);
+                        *(int *)ptr = ival;
+                        break;
+               case CAP_TYPE_STR:
+                       sval = term_getstr(tcaps[i], tptr);
+                       if (sval == (char *) -1)
+                               *(char **)ptr = NULL;
+                       else
+                               *(char **)ptr = sval;
+                        break;
+               }
+       }
+}
+
+/* Terminal was resized - ask the width/height from terminfo again */
+void terminfo_resize(TERM_REC *term)
+{
+       /* FIXME: is this possible? */
+}
+
+static void terminfo_colors_deinit(TERM_REC *term)
+{
+       int i;
+
+       if (terminfo_is_colors_set(term)) {
+               for (i = 0; i < 16; i++) {
+                       g_free(term->TI_fg[i]);
+                       g_free(term->TI_bg[i]);
+               }
+
+                memset(term->TI_fg, 0, sizeof(term->TI_fg));
+                memset(term->TI_bg, 0, sizeof(term->TI_fg));
+       }
+}
+
+/* Setup colors - if force is set, use ANSI-style colors if
+   terminal capabilities don't contain color codes */
+void terminfo_setup_colors(TERM_REC *term, int force)
+{
+       static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+       const char *bold, *blink;
+       int i;
+
+       terminfo_colors_deinit(term);
+        term->has_colors = term->TI_setf || term->TI_setaf;
+
+       if (term->TI_setf) {
+               for (i = 0; i < 8; i++)
+                        term->TI_fg[i] = g_strdup(tparm(term->TI_setf, i, 0));
+       } else if (term->TI_setaf) {
+               for (i = 0; i < 8; i++)
+                        term->TI_fg[i] = g_strdup(tparm(term->TI_setaf, ansitab[i], 0));
+       } else if (force) {
+               for (i = 0; i < 8; i++)
+                        term->TI_fg[i] = g_strdup_printf("\033[%dm", 30+ansitab[i]);
+       }
+
+       if (term->TI_setb) {
+               for (i = 0; i < 8; i++)
+                        term->TI_bg[i] = g_strdup(tparm(term->TI_setb, i, 0));
+       } else if (term->TI_setab) {
+               for (i = 0; i < 8; i++)
+                        term->TI_bg[i] = g_strdup(tparm(term->TI_setab, ansitab[i], 0));
+       } else if (force) {
+               for (i = 0; i < 8; i++)
+                        term->TI_bg[i] = g_strdup_printf("\033[%dm", 40+ansitab[i]);
+       }
+
+       if (term->TI_setf || term->TI_setaf || force) {
+                term->set_fg = _set_fg;
+                term->set_bg = _set_bg;
+
+               /* bold fg, blink bg */
+               bold = term->TI_bold ? term->TI_bold : "";
+               for (i = 0; i < 8; i++)
+                       term->TI_fg[i+8] = g_strconcat(bold, term->TI_fg[i], NULL);
+
+               blink = term->TI_blink ? term->TI_blink : "";
+               for (i = 0; i < 8; i++)
+                       term->TI_bg[i+8] = g_strconcat(blink, term->TI_bg[i], NULL);
+       } else {
+               /* no colors */
+                term->set_fg = term->set_bg = _ignore_parm;
+       }
+}
+
+static void terminfo_input_init(TERM_REC *term)
+{
+       tcgetattr(fileno(term->in), &term->old_tio);
+       memcpy(&term->tio, &term->old_tio, sizeof(term->tio));
+
+       term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */
+       term->tio.c_cc[VMIN] = 1; /* read() is satisfied after 1 char */
+       term->tio.c_cc[VTIME] = 0; /* No timer */
+
+        /* Disable INTR, QUIT, VDSUSP and SUSP keys */
+       term->tio.c_cc[VINTR] = _POSIX_VDISABLE;
+       term->tio.c_cc[VQUIT] = _POSIX_VDISABLE;
+#ifdef VDSUSP
+       term->tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+#ifdef VSUSP
+       term->tio.c_cc[VSUSP] = _POSIX_VDISABLE;
+#endif
+
+        tcsetattr(fileno(term->in), TCSADRAIN, &term->tio);
+
+}
+
+static void terminfo_input_deinit(TERM_REC *term)
+{
+        tcsetattr(fileno(term->in), TCSADRAIN, &term->old_tio);
+}
+
+void terminfo_cont(TERM_REC *term)
+{
+       if (term->TI_smcup)
+                tput(tparm(term->TI_smcup));
+        terminfo_input_init(term);
+}
+
+void terminfo_stop(TERM_REC *term)
+{
+        /* reset colors */
+       terminfo_set_normal();
+        /* move cursor to bottom of the screen */
+       terminfo_move(0, term->height-1);
+
+       /* stop cup-mode */
+       if (term->TI_rmcup)
+               tput(tparm(term->TI_rmcup));
+
+        /* reset input settings */
+       terminfo_input_deinit(term);
+        fflush(term->out);
+}
+
+static int term_setup(TERM_REC *term)
+{
+       GString *str;
+#ifdef HAVE_TERMINFO
+       int err;
+#endif
+        char *term_env;
+
+       term_env = getenv("TERM");
+       if (term_env == NULL) {
+               fprintf(term->out, "TERM environment not set\n");
+                return 0;
+       }
+
+#ifdef HAVE_TERMINFO
+       if (setupterm(term_env, 1, &err) != 0) {
+               fprintf(term->out, "setupterm() failed for TERM=%s: %d\n", term_env, err);
+               return 0;
+       }
+#else
+       if (tgetent(term->buffer1, term_env) < 1)
+       {
+               fprintf(term->out, "Termcap not found for TERM=%s\n", term_env);
+               return -1;
+       }
+#endif
+
+        term_fill_capabilities(term);
+
+       /* Cursor movement */
+       if (term->TI_cup)
+               term->move = _move_cup;
+       else if (term->TI_hpa && term->TI_vpa)
+               term->move = _move_pa;
+       else {
+                fprintf(term->out, "Terminal doesn't support cursor movement\n");
+               return 0;
+       }
+       term->move_relative = _move_relative;
+       term->set_cursor_visible = term->TI_civis && term->TI_cnorm ?
+               _set_cursor_visible : _ignore_parm;
+
+        /* Scrolling */
+       if ((term->TI_csr || term->TI_wind) && term->TI_rin && term->TI_indn)
+               term->scroll = _scroll_region;
+       else if (term->TI_il && term->TI_dl)
+               term->scroll = _scroll_line;
+       else if ((term->TI_csr || term->TI_wind) && term->TI_ri && term->TI_ind)
+               term->scroll = _scroll_region_1;
+       else if (term->scroll == NULL && (term->TI_il1 && term->TI_dl1))
+               term->scroll = _scroll_line_1;
+       else if (term->scroll == NULL) {
+                fprintf(term->out, "Terminal doesn't support scrolling\n");
+               return 0;
+       }
+
+       /* Clearing screen */
+       if (term->TI_clear)
+               term->clear = _clear_screen;
+       else if (term->TI_ed)
+               term->clear = _clear_eos;
+       else if (term->TI_dl)
+               term->clear = _clear_del;
+       else if (term->TI_dl1)
+               term->clear = _clear_del_1;
+       else {
+               /* we could do this by line inserts as well, but don't
+                  bother - if some terminal has insert line it most probably
+                  has delete line as well, if not a regular clear screen */
+                fprintf(term->out, "Terminal doesn't support clearing screen\n");
+               return 0;
+       }
+
+       /* Clearing to end of line */
+       if (term->TI_el)
+               term->clrtoeol = _clrtoeol;
+       else {
+                fprintf(term->out, "Terminal doesn't support clearing to end of line\n");
+               return 0;
+       }
+
+       /* Repeating character */
+       if (term->TI_rep)
+               term->repeat = _repeat;
+       else
+               term->repeat = _repeat_manual;
+
+       /* Bold, underline, standout */
+       term->set_bold = term->TI_bold ? _set_bold : _ignore;
+       term->set_uline = term->TI_smul && term->TI_rmul ?
+               _set_uline : _ignore_parm;
+       term->set_standout = term->TI_smso && term->TI_rmso ?
+               _set_standout : _ignore_parm;
+
+        /* Create a string to set all attributes off */
+        str = g_string_new(NULL);
+       if (term->TI_sgr0)
+               g_string_append(str, term->TI_sgr0);
+       if (term->TI_rmul && (term->TI_sgr0 == NULL || strcmp(term->TI_rmul, term->TI_sgr0) != 0))
+               g_string_append(str, term->TI_rmul);
+       if (term->TI_rmso && (term->TI_sgr0 == NULL || strcmp(term->TI_rmso, term->TI_sgr0) != 0))
+               g_string_append(str, term->TI_rmso);
+        term->TI_normal = str->str;
+       g_string_free(str, FALSE);
+        term->set_normal = _set_normal;
+
+       term->beep = term->TI_bel ? _beep : _ignore;
+
+       terminfo_setup_colors(term, FALSE);
+        terminfo_cont(term);
+        return 1;
+}
+
+TERM_REC *terminfo_core_init(FILE *in, FILE *out)
+{
+       TERM_REC *old_term, *term;
+
+        old_term = current_term;
+       current_term = term = g_new0(TERM_REC, 1);
+
+       term->in = in;
+       term->out = out;
+
+       if (!term_setup(term)) {
+               g_free(term);
+                term = NULL;
+       }
+
+       current_term = old_term;
+        return term;
+}
+
+void terminfo_core_deinit(TERM_REC *term)
+{
+       TERM_REC *old_term;
+
+       old_term = current_term;
+        current_term = term;
+       term->set_normal(term);
+        current_term = old_term;
+
+        terminfo_stop(term);
+
+       g_free(term->TI_normal);
+       terminfo_colors_deinit(term);
+
+        g_free(term);
+}
diff --git a/apps/irssi/src/fe-text/terminfo-core.h b/apps/irssi/src/fe-text/terminfo-core.h
new file mode 100644 (file)
index 0000000..93afa78
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef __TERMINFO_CORE_H
+#define __TERMINFO_CORE_H
+
+#include <termios.h>
+
+#define terminfo_move(x, y) current_term->move(current_term, x, y)
+#define terminfo_move_relative(oldx, oldy, x, y) current_term->move_relative(current_term, oldx, oldy, x, y)
+#define terminfo_set_cursor_visible(set) current_term->set_cursor_visible(current_term, set)
+#define terminfo_scroll(y1, y2, count) current_term->scroll(current_term, y1, y2, count)
+#define terminfo_clear() current_term->clear(current_term)
+#define terminfo_clrtoeol() current_term->clrtoeol(current_term)
+#define terminfo_repeat(chr, count) current_term->repeat(current_term, chr, count)
+#define terminfo_set_fg(color) current_term->set_fg(current_term, color)
+#define terminfo_set_bg(color) current_term->set_bg(current_term, color)
+#define terminfo_set_normal() current_term->set_normal(current_term)
+#define terminfo_set_bold() current_term->set_bold(current_term)
+#define terminfo_set_uline(set) current_term->set_uline(current_term, set)
+#define terminfo_set_standout(set) current_term->set_standout(current_term, set)
+#define terminfo_is_colors_set(term) (term->TI_fg[0] != NULL)
+#define terminfo_beep(term) current_term->beep(current_term)
+
+typedef struct _TERM_REC TERM_REC;
+
+struct _TERM_REC {
+        /* Functions */
+       void (*move)(TERM_REC *term, int x, int y);
+       void (*move_relative)(TERM_REC *term, int oldx, int oldy, int x, int y);
+       void (*set_cursor_visible)(TERM_REC *term, int set);
+       void (*scroll)(TERM_REC *term, int y1, int y2, int count);
+
+        void (*clear)(TERM_REC *term);
+       void (*clrtoeol)(TERM_REC *term);
+       void (*repeat)(TERM_REC *term, int chr, int count);
+
+       void (*set_fg)(TERM_REC *term, int color);
+       void (*set_bg)(TERM_REC *term, int color);
+       void (*set_normal)(TERM_REC *term);
+       void (*set_bold)(TERM_REC *term);
+       void (*set_uline)(TERM_REC *term, int set);
+       void (*set_standout)(TERM_REC *term, int set);
+
+        void (*beep)(TERM_REC *term);
+
+#ifndef HAVE_TERMINFO
+       char buffer1[1024], buffer2[1024];
+#endif
+       FILE *in, *out;
+       struct termios tio, old_tio;
+
+        /* Terminal size */
+        int width, height;
+
+        /* Cursor movement */
+       const char *TI_smcup, *TI_rmcup, *TI_cup;
+       const char *TI_hpa, *TI_vpa, *TI_cub1, *TI_cuf1;
+        const char *TI_civis, *TI_cnorm;
+
+       /* Scrolling */
+       const char *TI_csr, *TI_wind;
+       const char *TI_ri, *TI_rin, *TI_ind, *TI_indn;
+       const char *TI_il, *TI_il1, *TI_dl, *TI_dl1;
+
+       /* Clearing screen */
+       const char *TI_clear, *TI_ed; /* + *TI_dl, *TI_dl1; */
+
+        /* Clearing to end of line */
+       const char *TI_el;
+
+       /* Repeating character */
+       const char *TI_rep;
+
+       /* Colors */
+        int has_colors;
+       const char *TI_sgr0; /* turn off all attributes */
+       const char *TI_smul, *TI_rmul; /* underline on/off */
+        const char *TI_smso, *TI_rmso; /* standout on/off */
+        const char *TI_bold, *TI_blink;
+       const char *TI_setaf, *TI_setab, *TI_setf, *TI_setb;
+
+        /* Colors - generated and dynamically allocated */
+       char *TI_fg[16], *TI_bg[16], *TI_normal;
+
+       /* Beep */
+        char *TI_bel;
+};
+
+extern TERM_REC *current_term;
+
+TERM_REC *terminfo_core_init(FILE *in, FILE *out);
+void terminfo_core_deinit(TERM_REC *term);
+
+/* Setup colors - if force is set, use ANSI-style colors if
+   terminal capabilities don't contain color codes */
+void terminfo_setup_colors(TERM_REC *term, int force);
+
+/* Terminal was resized - ask the width/height from terminfo again */
+void terminfo_resize(TERM_REC *term);
+
+void terminfo_cont(TERM_REC *term);
+void terminfo_stop(TERM_REC *term);
+
+#endif
index 92ec70bdddfe80aeb2455d3a90d2df69426de1c7..db5bc782f93aef69e9db49cf742ad40fd60a52d9 100644 (file)
 */
 
 #include "module.h"
 */
 
 #include "module.h"
+#include "module-formats.h"
 #include "signals.h"
 #include "commands.h"
 #include "misc.h"
 #include "levels.h"
 #include "signals.h"
 #include "commands.h"
 #include "misc.h"
 #include "levels.h"
+#include "settings.h"
+#include "servers.h"
 
 #include "printtext.h"
 #include "gui-windows.h"
 
 #include "printtext.h"
 #include "gui-windows.h"
+#include "textbuffer-reformat.h"
 
 /* SYNTAX: CLEAR */
 static void cmd_clear(const char *data)
 
 /* SYNTAX: CLEAR */
 static void cmd_clear(const char *data)
@@ -54,6 +58,32 @@ static void cmd_clear(const char *data)
        cmd_params_free(free_arg);
 }
 
        cmd_params_free(free_arg);
 }
 
+static void cmd_window_scroll(const char *data)
+{
+       GUI_WINDOW_REC *gui;
+
+       gui = WINDOW_GUI(active_win);
+       if (g_strcasecmp(data, "default") == 0) {
+                gui->use_scroll = FALSE;
+       } else if (g_strcasecmp(data, "on") == 0) {
+               gui->use_scroll = TRUE;
+               gui->scroll = TRUE;
+       } else if (g_strcasecmp(data, "off") == 0) {
+               gui->use_scroll = TRUE;
+               gui->scroll = FALSE;
+       } else if (*data != '\0') {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_WINDOW_SCROLL_UNKNOWN, data);
+                return;
+       }
+
+       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                          TXT_WINDOW_SCROLL, !gui->use_scroll ? "DEFAULT" :
+                          gui->scroll ? "ON" : "OFF");
+       textbuffer_view_set_scroll(gui->view, gui->use_scroll ?
+                                  gui->scroll : settings_get_bool("scroll"));
+}
+
 static void cmd_scrollback(const char *data, SERVER_REC *server,
                           WI_ITEM_REC *item)
 {
 static void cmd_scrollback(const char *data, SERVER_REC *server,
                           WI_ITEM_REC *item)
 {
@@ -74,13 +104,13 @@ static void scrollback_goto_line(int linenum)
        if (view->buffer->lines_count == 0)
                return;
 
        if (view->buffer->lines_count == 0)
                return;
 
-       textbuffer_view_scroll_line(view, view->buffer->lines->data);
+       textbuffer_view_scroll_line(view, view->buffer->first_line);
        gui_window_scroll(active_win, linenum);
 }
 
 static void scrollback_goto_time(const char *datearg, const char *timearg)
 {
        gui_window_scroll(active_win, linenum);
 }
 
 static void scrollback_goto_time(const char *datearg, const char *timearg)
 {
-        GList *tmp;
+        LINE_REC *line;
        struct tm tm;
        time_t now, stamp;
        int day, month;
        struct tm tm;
        time_t now, stamp;
        int day, month;
@@ -145,12 +175,10 @@ static void scrollback_goto_time(const char *datearg, const char *timearg)
        }
 
        /* scroll to first line after timestamp */
        }
 
        /* scroll to first line after timestamp */
-        tmp = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view);
-       for (; tmp != NULL; tmp = tmp->next) {
-               LINE_REC *rec = tmp->data;
-
-               if (rec->info.time >= stamp) {
-                       gui_window_scroll_line(active_win, rec);
+       line = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view);
+       for (; line != NULL; line = line->next) {
+               if (line->info.time >= stamp) {
+                       gui_window_scroll_line(active_win, line);
                        break;
                }
        }
                        break;
                }
        }
@@ -188,7 +216,7 @@ static void cmd_scrollback_home(const char *data)
 
        buffer = WINDOW_GUI(active_win)->view->buffer;
        if (buffer->lines_count > 0)
 
        buffer = WINDOW_GUI(active_win)->view->buffer;
        if (buffer->lines_count > 0)
-               gui_window_scroll_line(active_win, buffer->lines->data);
+               gui_window_scroll_line(active_win, buffer->first_line);
 }
 
 /* SYNTAX: SCROLLBACK END */
 }
 
 /* SYNTAX: SCROLLBACK END */
@@ -200,28 +228,28 @@ static void cmd_scrollback_end(const char *data)
        if (view->bottom_startline == NULL)
                return;
 
        if (view->bottom_startline == NULL)
                return;
 
-       textbuffer_view_scroll_line(view, view->bottom_startline->data);
+       textbuffer_view_scroll_line(view, view->bottom_startline);
        gui_window_scroll(active_win, view->bottom_subline);
 }
 
 /* SYNTAX: SCROLLBACK REDRAW */
 static void cmd_scrollback_redraw(void)
 {
        gui_window_scroll(active_win, view->bottom_subline);
 }
 
 /* SYNTAX: SCROLLBACK REDRAW */
 static void cmd_scrollback_redraw(void)
 {
-#if 0
        GUI_WINDOW_REC *gui;
        GUI_WINDOW_REC *gui;
-       GList *tmp, *next;
+       LINE_REC *line, *next;
 
        gui = WINDOW_GUI(active_win);
 
 
        gui = WINDOW_GUI(active_win);
 
-       screen_refresh_freeze();
-       for (tmp = gui->lines; tmp != NULL; tmp = next) {
-               next = tmp->next;
-               gui_window_reformat_line(active_win, tmp->data);
+       term_refresh_freeze();
+       line = textbuffer_view_get_lines(gui->view);
+       while (line != NULL) {
+               next = line->next;
+               textbuffer_reformat_line(active_win, line);
+                line = next;
        }
 
        gui_window_redraw(active_win);
        }
 
        gui_window_redraw(active_win);
-       screen_refresh_thaw();
-#endif
+       term_refresh_thaw();
 }
 
 static void cmd_scrollback_status(void)
 }
 
 static void cmd_scrollback_status(void)
@@ -269,6 +297,7 @@ static void sig_away_changed(SERVER_REC *server)
 void textbuffer_commands_init(void)
 {
        command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
 void textbuffer_commands_init(void)
 {
        command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
+       command_bind("window scroll", NULL, (SIGNAL_FUNC) cmd_window_scroll);
        command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback);
        command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear);
        command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto);
        command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback);
        command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear);
        command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto);
@@ -285,6 +314,7 @@ void textbuffer_commands_init(void)
 void textbuffer_commands_deinit(void)
 {
        command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
 void textbuffer_commands_deinit(void)
 {
        command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
+       command_unbind("window scroll", (SIGNAL_FUNC) cmd_window_scroll);
        command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback);
        command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear);
        command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto);
        command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback);
        command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear);
        command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto);
diff --git a/apps/irssi/src/fe-text/textbuffer-reformat.c b/apps/irssi/src/fe-text/textbuffer-reformat.c
new file mode 100644 (file)
index 0000000..da75a5a
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ textbuffer-reformat.c : Reformatting lines in text buffer
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "settings.h"
+
+#include "formats.h"
+
+#include "gui-windows.h"
+#include "gui-printtext.h"
+#include "textbuffer.h"
+
+static GString *format;
+static int scrollback_save_formats;
+
+/* Read one block between \0<format>s */
+static char *line_read_format(unsigned const char **text)
+{
+       GString *str;
+       char *ret;
+
+       str = g_string_new(NULL);
+       for (;;) {
+               if (**text == '\0') {
+                       if ((*text)[1] == LINE_CMD_EOL) {
+                               /* leave text at \0<eof> */
+                               break;
+                       }
+                       if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
+                               /* leave text at \0<format_cont> */
+                               break;
+                       }
+                       (*text)++;
+
+                        if (**text == LINE_CMD_FORMAT) {
+                               /* move text to start after \0<format> */
+                               (*text)++;
+                               break;
+                       }
+
+                       if (**text == LINE_CMD_CONTINUE) {
+                               unsigned char *tmp;
+
+                               memcpy(&tmp, (*text)+1, sizeof(char *));
+                               *text = tmp;
+                               continue;
+                       } else if (**text & 0x80)
+                               (*text)++;
+                       continue;
+               }
+
+               g_string_append_c(str, (char) **text);
+               (*text)++;
+       }
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
+                                       GString *raw)
+{
+       const unsigned char *text;
+       char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
+       TEXT_DEST_REC dest;
+       int formatnum, argcount;
+
+       text = (const unsigned char *) line->text;
+
+       /* skip the beginning of the line until we find the format */
+       g_free(line_read_format(&text));
+       if (text[1] == LINE_CMD_FORMAT_CONT) {
+               if (raw != NULL) {
+                       g_string_append_c(raw, '\0');
+                       g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT);
+               }
+               return NULL;
+       }
+
+       /* read format information */
+        module = line_read_format(&text);
+       format_name = line_read_format(&text);
+
+       if (raw != NULL) {
+               g_string_append_c(raw, '\0');
+               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
+
+               g_string_append(raw, module);
+
+               g_string_append_c(raw, '\0');
+               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
+
+               g_string_append(raw, format_name);
+       }
+
+       formatnum = format_find_tag(module, format_name);
+       if (formatnum == -1)
+               ret = NULL;
+       else {
+                argcount = 0;
+                memset(args, 0, sizeof(args));
+               while (*text != '\0' || text[1] != LINE_CMD_EOL) {
+                       args[argcount] = line_read_format(&text);
+                       if (raw != NULL) {
+                               g_string_append_c(raw, '\0');
+                               g_string_append_c(raw,
+                                                 (char)LINE_CMD_FORMAT);
+
+                               g_string_append(raw, args[argcount]);
+                       }
+                       argcount++;
+               }
+
+               /* get the format text */
+               format_create_dest(&dest, NULL, NULL, line->info.level, window);
+               ret = format_get_text_theme_charargs(current_theme,
+                                                    module, &dest,
+                                                    formatnum, args);
+               while (argcount > 0)
+                       g_free(args[--argcount]);
+       }
+
+       g_free(module);
+       g_free(format_name);
+
+       return ret;
+}
+
+void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line)
+{
+        GUI_WINDOW_REC *gui;
+       TEXT_DEST_REC dest;
+       LINE_REC *line_prev;
+        LINE_INFO_REC line_info;
+       GString *raw;
+       char *str, *tmp, *prestr, *linestart, *leveltag;
+
+        gui = WINDOW_GUI(window);
+
+       raw = g_string_new(NULL);
+       str = textbuffer_line_get_format(window, line, raw);
+
+        if (str == NULL && raw->len == 2 &&
+            raw->str[1] == (char)LINE_CMD_FORMAT_CONT) {
+                /* multiline format, format explained in one the
+                   following lines. remove this line. */
+                textbuffer_view_remove_line(gui->view, line);
+       } else if (str != NULL) {
+                /* FIXME: ugly ugly .. and this can't handle
+                   unformatted lines.. */
+               g_string_append_c(raw, '\0');
+               g_string_append_c(raw, (char)LINE_CMD_EOL);
+
+               line_prev = line->prev;
+                memcpy(&line_info, &line->info, sizeof(line_info));
+                textbuffer_view_remove_line(gui->view, line); line = NULL;
+
+               format_create_dest(&dest, NULL, NULL, line_info.level, window);
+
+               linestart = format_get_line_start(current_theme, &dest, line_info.time);
+               leveltag = format_get_level_tag(current_theme, &dest);
+
+               prestr = g_strconcat(linestart == NULL ? "" : linestart,
+                                    leveltag, NULL);
+               g_free_not_null(linestart);
+               g_free_not_null(leveltag);
+
+               tmp = format_add_linestart(str, prestr);
+               g_free(str);
+               g_free(prestr);
+
+                gui_printtext_after(&dest, line_prev, tmp);
+               g_free(tmp);
+
+                line = textbuffer_insert(gui->view->buffer, gui->insert_after,
+                                        (unsigned char *) raw->str,
+                                        raw->len, &line_info);
+               textbuffer_view_insert_line(gui->view, line);
+       }
+       g_string_free(raw, TRUE);
+}
+
+static void sig_print_format(THEME_REC *theme, const char *module,
+                            TEXT_DEST_REC *dest, void *formatnump,
+                            char **args)
+{
+       FORMAT_REC *formats;
+       int formatnum, n;
+
+       if (!scrollback_save_formats)
+               return;
+
+       formatnum = GPOINTER_TO_INT(formatnump);
+       formats = g_hash_table_lookup(default_formats, module);
+
+       /* <module><format_name><arg...> */
+       g_string_truncate(format, 0);
+
+       g_string_append_c(format, '\0');
+       g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+        g_string_append(format, module);
+
+       g_string_append_c(format, '\0');
+       g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+       g_string_append(format, formats[formatnum].tag);
+
+       for (n = 0; n < formats[formatnum].params; n++) {
+               g_string_append_c(format, '\0');
+               g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+               if (args[n] != NULL)
+                       g_string_append(format, args[n]);
+       }
+}
+
+static void sig_gui_printtext_finished(WINDOW_REC *window)
+{
+       GUI_WINDOW_REC *gui;
+        LINE_REC *insert_after;
+
+       if (format->len == 0)
+                return;
+
+       /* save format of the line */
+        gui = WINDOW_GUI(window);
+       insert_after = gui->use_insert_after ?
+               gui->insert_after : gui->view->buffer->cur_line;
+
+       textbuffer_insert(gui->view->buffer, insert_after,
+                         (unsigned char *) format->str,
+                         format->len, NULL);
+
+       g_string_truncate(format, 0);
+}
+
+static void read_settings(void)
+{
+        scrollback_save_formats = settings_get_bool("scrollback_save_formats");
+}
+
+void textbuffer_reformat_init(void)
+{
+       format = g_string_new(NULL);
+       settings_add_bool("history", "scrollback_save_formats", FALSE);
+
+        read_settings();
+       signal_add("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_add_first("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void textbuffer_reformat_deinit(void)
+{
+       g_string_free(format, TRUE);
+
+       signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_remove("print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-text/textbuffer-reformat.h b/apps/irssi/src/fe-text/textbuffer-reformat.h
new file mode 100644 (file)
index 0000000..2818ec6
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __TEXTBUFFER_REFORMAT_H
+#define __TEXTBUFFER_REFORMAT_H
+
+void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line);
+
+void textbuffer_reformat_init(void);
+void textbuffer_reformat_deinit(void);
+
+#endif
index 449f20a9197860488861cd7351e68a2e61fc355d..a5d7f2ed1d6b556d2a3351cfb71268d995275f6c 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "module.h"
 #include "textbuffer-view.h"
 
 #include "module.h"
 #include "textbuffer-view.h"
-#include "screen.h"
+#include "utf8.h"
 
 typedef struct {
        char *name;
 
 typedef struct {
        char *name;
@@ -102,21 +102,64 @@ static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache)
                 textbuffer_cache_destroy(cache);
 }
 
                 textbuffer_cache_destroy(cache);
 }
 
+#define FGATTR (ATTR_NOCOLORS | ATTR_RESETFG | ATTR_BOLD | 0x0f)
+#define BGATTR (ATTR_NOCOLORS | ATTR_RESETBG | ATTR_BLINK | 0xf0)
+
+static void update_cmd_color(unsigned char cmd, int *color)
+{
+       if ((cmd & 0x80) == 0) {
+               if (cmd & LINE_COLOR_BG) {
+                       /* set background color */
+                       *color &= FGATTR;
+                       if ((cmd & LINE_COLOR_DEFAULT) == 0)
+                               *color |= (cmd & 0x0f) << 4;
+                       else {
+                               *color |= ATTR_RESETBG;
+                                if (cmd & LINE_COLOR_BLINK)
+                                       *color |= ATTR_BLINK;
+                       }
+               } else {
+                       /* set foreground color */
+                       *color &= BGATTR;
+                       if ((cmd & LINE_COLOR_DEFAULT) == 0)
+                               *color |= cmd & 0x0f;
+                       else {
+                               *color |= ATTR_RESETFG;
+                                if (cmd & LINE_COLOR_BOLD)
+                                       *color |= ATTR_BOLD;
+                       }
+               }
+       } else switch (cmd) {
+       case LINE_CMD_UNDERLINE:
+               *color ^= ATTR_UNDERLINE;
+               break;
+       case LINE_CMD_REVERSE:
+               *color ^= ATTR_REVERSE;
+               break;
+       case LINE_CMD_COLOR0:
+               *color &= BGATTR;
+               break;
+       }
+}
+
 static LINE_CACHE_REC *
 view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
 static LINE_CACHE_REC *
 view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
+        INDENT_FUNC indent_func;
        LINE_CACHE_REC *rec;
        LINE_CACHE_SUB_REC *sub;
        GSList *lines;
         unsigned char cmd;
        LINE_CACHE_REC *rec;
        LINE_CACHE_SUB_REC *sub;
        GSList *lines;
         unsigned char cmd;
-       char *ptr, *last_space_ptr;
+       const unsigned char *ptr, *last_space_ptr;
        int xpos, pos, indent_pos, last_space, last_color, color, linecount;
 
        g_return_val_if_fail(line->text != NULL, NULL);
 
        int xpos, pos, indent_pos, last_space, last_color, color, linecount;
 
        g_return_val_if_fail(line->text != NULL, NULL);
 
-       xpos = 0; color = 0; indent_pos = view->default_indent;
+       color = ATTR_RESETFG | ATTR_RESETBG;
+       xpos = 0; indent_pos = view->default_indent;
        last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
 
        last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
 
+        indent_func = view->default_indent_func;
         linecount = 1;
        lines = NULL;
        for (ptr = line->text;;) {
         linecount = 1;
        lines = NULL;
        for (ptr = line->text;;) {
@@ -130,49 +173,38 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                                break;
 
                        if (cmd == LINE_CMD_CONTINUE) {
                                break;
 
                        if (cmd == LINE_CMD_CONTINUE) {
-                               char *tmp;
+                               unsigned char *tmp;
 
                                memcpy(&tmp, ptr, sizeof(char *));
                                ptr = tmp;
                                continue;
                        }
 
 
                                memcpy(&tmp, ptr, sizeof(char *));
                                ptr = tmp;
                                continue;
                        }
 
-                       if ((cmd & 0x80) == 0) {
-                               /* set color */
-                               color = (color & ATTR_UNDERLINE) | cmd;
-                       } else switch (cmd) {
-                       case LINE_CMD_UNDERLINE:
-                               color ^= ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR0:
-                               color = color & ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR8:
-                               color &= 0xfff0;
-                               color |= 8|ATTR_COLOR8;
-                               break;
-                       case LINE_CMD_BLINK:
-                               color |= 0x80;
-                               break;
-                       case LINE_CMD_INDENT:
+                       if (cmd == LINE_CMD_INDENT) {
                                /* set indentation position here - don't do
                                   it if we're too close to right border */
                                if (xpos < view->width-5) indent_pos = xpos;
                                /* set indentation position here - don't do
                                   it if we're too close to right border */
                                if (xpos < view->width-5) indent_pos = xpos;
-                               break;
-                       }
+                       } else if (cmd == LINE_CMD_INDENT_FUNC) {
+                               memcpy(&indent_func, ptr, sizeof(INDENT_FUNC));
+                               ptr += sizeof(INDENT_FUNC);
+                               if (indent_func == NULL)
+                                        indent_func = view->default_indent_func;
+                       } else
+                               update_cmd_color(cmd, &color);
                        continue;
                }
 
                if (xpos == view->width && sub != NULL &&
                    (last_space <= indent_pos || last_space <= 10) &&
                        continue;
                }
 
                if (xpos == view->width && sub != NULL &&
                    (last_space <= indent_pos || last_space <= 10) &&
-                   !view->longword_noindent) {
+                   view->longword_noindent) {
                         /* long word, remove the indentation from this line */
                        xpos -= sub->indent;
                         sub->indent = 0;
                }
 
                if (xpos == view->width) {
                         /* long word, remove the indentation from this line */
                        xpos -= sub->indent;
                         sub->indent = 0;
                }
 
                if (xpos == view->width) {
-                       xpos = indent_pos;
+                       xpos = indent_func == NULL ? indent_pos :
+                               indent_func(view, line, -1);
 
                        sub = g_new0(LINE_CACHE_SUB_REC, 1);
                        if (last_space > indent_pos && last_space > 10) {
 
                        sub = g_new0(LINE_CACHE_SUB_REC, 1);
                        if (last_space > indent_pos && last_space > 10) {
@@ -180,7 +212,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                                 color = last_color;
                                ptr = last_space_ptr;
                                while (*ptr == ' ') ptr++;
                                 color = last_color;
                                ptr = last_space_ptr;
                                while (*ptr == ' ') ptr++;
-                       } else if (!view->longword_noindent) {
+                       } else if (view->longword_noindent) {
                                /* long word, no indentation in next line */
                                xpos = 0;
                                sub->continues = TRUE;
                                /* long word, no indentation in next line */
                                xpos = 0;
                                sub->continues = TRUE;
@@ -188,6 +220,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 
                        sub->start = ptr;
                        sub->indent = xpos;
 
                        sub->start = ptr;
                        sub->indent = xpos;
+                        sub->indent_func = indent_func;
                        sub->color = color;
 
                        lines = g_slist_append(lines, sub);
                        sub->color = color;
 
                        lines = g_slist_append(lines, sub);
@@ -197,6 +230,9 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                        continue;
                }
 
                        continue;
                }
 
+               if (view->utf8)
+                       get_utf8_char(&ptr);
+
                xpos++;
                if (*ptr++ == ' ') {
                        last_space = xpos-1;
                xpos++;
                if (*ptr++ == ' ') {
                        last_space = xpos-1;
@@ -224,23 +260,76 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        return rec;
 }
 
        return rec;
 }
 
+static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+                             unsigned char update_counter)
+{
+       LINE_CACHE_REC *cache;
+
+       if (view->cache->update_counter == update_counter)
+               return;
+       view->cache->update_counter = update_counter;
+
+       cache = g_hash_table_lookup(view->cache->line_cache, line);
+       if (cache != NULL) {
+                g_free(cache);
+               g_hash_table_remove(view->cache->line_cache, line);
+       }
+}
+
+static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+                             unsigned char update_counter)
+{
+       view_remove_cache(view, line, update_counter);
+
+       if (view->buffer->cur_line == line)
+               view->cache->last_linecount = view_get_linecount(view, line);
+}
+
+static void view_reset_cache(TEXT_BUFFER_VIEW_REC *view)
+{
+       GSList *tmp;
+
+       /* destroy line caches - note that you can't do simultaneously
+          unrefs + cache_get()s or it will keep using the old caches */
+       textbuffer_cache_unref(view->cache);
+        g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL);
+
+       view->cache = textbuffer_cache_get(view->siblings, view->width);
+       for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
+               TEXT_BUFFER_VIEW_REC *rec = tmp->data;
+
+               rec->cache = textbuffer_cache_get(rec->siblings, rec->width);
+       }
+}
+
 static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                          int subline, int ypos, int max)
 {
 static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                          int subline, int ypos, int max)
 {
+        INDENT_FUNC indent_func;
        LINE_CACHE_REC *cache;
         const unsigned char *text, *text_newline;
        LINE_CACHE_REC *cache;
         const unsigned char *text, *text_newline;
-       char *tmp;
-       int xpos, color, drawcount, first;
+       unsigned char *tmp;
+       int xpos, color, drawcount, first, need_move, need_clrtoeol;
+
+       if (view->dirty) /* don't bother drawing anything - redraw is coming */
+                return 0;
 
        cache = textbuffer_view_get_line_cache(view, line);
        if (subline >= cache->count)
                 return 0;
 
 
        cache = textbuffer_view_get_line_cache(view, line);
        if (subline >= cache->count)
                 return 0;
 
-       xpos = color = drawcount = 0; first = TRUE;
+        color = ATTR_RESET;
+        need_move = TRUE; need_clrtoeol = FALSE;
+       xpos = drawcount = 0; first = TRUE;
        text_newline = text =
                subline == 0 ? line->text : cache->lines[subline-1].start;
        for (;;) {
                if (text == text_newline) {
        text_newline = text =
                subline == 0 ? line->text : cache->lines[subline-1].start;
        for (;;) {
                if (text == text_newline) {
+                       if (need_clrtoeol && xpos < term_width) {
+                               term_set_color(view->window, ATTR_RESET);
+                               term_clrtoeol(view->window);
+                       }
+
                        if (first)
                                first = FALSE;
                        else {
                        if (first)
                                first = FALSE;
                        else {
@@ -250,21 +339,37 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                        }
 
                        if (subline > 0) {
                        }
 
                        if (subline > 0) {
-                               xpos = cache->lines[subline-1].indent;
+                                /* continuing previous line - indent it */
+                               indent_func = cache->lines[subline-1].indent_func;
+                               xpos = indent_func != NULL ?
+                                       indent_func(view, line, ypos) :
+                                       cache->lines[subline-1].indent;
                                 color = cache->lines[subline-1].color;
                        }
 
                                 color = cache->lines[subline-1].color;
                        }
 
-                       set_color(view->window, 0);
-                       wmove(view->window, ypos, 0);
-                       wclrtoeol(view->window);
+                       if (xpos == 0)
+                                need_clrtoeol = TRUE;
+                       else {
+                               /* line was indented - need to clear the
+                                   indented area first */
+                               term_set_color(view->window, ATTR_RESET);
+                               term_move(view->window, 0, ypos);
+                               term_clrtoeol(view->window);
+                       }
 
 
-                       wmove(view->window, ypos, xpos);
-                       set_color(view->window, color);
+                       if (need_move || xpos > 0)
+                               term_move(view->window, xpos, ypos);
 
 
-                       /* get the beginning of the next subline */
-                       text_newline = subline == cache->count-1 ? NULL :
-                                cache->lines[subline].start;
+                       term_set_color(view->window, color);
 
 
+                       if (subline == cache->count-1) {
+                               text_newline = NULL;
+                               need_move = FALSE;
+                       } else {
+                               /* get the beginning of the next subline */
+                               text_newline = cache->lines[subline].start;
+                               need_move = !cache->lines[subline].continues;
+                       }
                         drawcount++;
                        subline++;
                }
                         drawcount++;
                        subline++;
                }
@@ -275,43 +380,45 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                        if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT)
                                 break;
 
                        if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT)
                                 break;
 
-                       if ((*text & 0x80) == 0) {
-                               /* set color */
-                               color = (color & ATTR_UNDERLINE) | *text;
-                       } else if (*text == LINE_CMD_CONTINUE) {
+                       if (*text == LINE_CMD_CONTINUE) {
                                 /* jump to next block */
                                memcpy(&tmp, text+1, sizeof(unsigned char *));
                                text = tmp;
                                continue;
                                 /* jump to next block */
                                memcpy(&tmp, text+1, sizeof(unsigned char *));
                                text = tmp;
                                continue;
-                       } else switch (*text) {
-                       case LINE_CMD_UNDERLINE:
-                               color ^= ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR0:
-                               color = color & ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR8:
-                               color &= 0xfff0;
-                               color |= 8|ATTR_COLOR8;
-                               break;
-                       case LINE_CMD_BLINK:
-                               color |= 0x80;
-                                break;
+                       } else if (*text == LINE_CMD_INDENT_FUNC) {
+                               text += sizeof(INDENT_FUNC);
+                       } else {
+                               update_cmd_color(*text, &color);
+                               term_set_color(view->window, color);
                        }
                        }
-                       set_color(view->window, color);
                        text++;
                        continue;
                }
 
                        text++;
                        continue;
                }
 
-               if ((*text & 127) >= 32)
-                       waddch(view->window, *text);
-               else {
-                       /* low-ascii */
-                       set_color(view->window, ATTR_REVERSE);
-                       waddch(view->window, (*text & 127)+'A'-1);
-                       set_color(view->window, color);
+               if (xpos < term_width) {
+                       const unsigned char *end = text;
+                       if (view->utf8)
+                               get_utf8_char(&end);
+
+                       if (*text >= 32 &&
+                           (end != text || (*text & 127) >= 32)) {
+                               for (; text < end; text++)
+                                       term_addch(view->window, *text);
+                               term_addch(view->window, *text);
+                       } else {
+                               /* low-ascii */
+                               term_set_color(view->window, ATTR_RESET|ATTR_REVERSE);
+                               term_addch(view->window, (*text & 127)+'A'-1);
+                               term_set_color(view->window, color);
+                       }
                }
                text++;
                }
                text++;
+               xpos++;
+       }
+
+       if (need_clrtoeol && xpos < term_width) {
+               term_set_color(view->window, ATTR_RESET);
+               term_clrtoeol(view->window);
        }
 
         return drawcount;
        }
 
         return drawcount;
@@ -321,7 +428,7 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
    original if possible */
 static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
 {
    original if possible */
 static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
 {
-       GList *tmp;
+        LINE_REC *line;
         int linecount, total;
 
        if (view->empty_linecount == 0) {
         int linecount, total;
 
        if (view->empty_linecount == 0) {
@@ -331,12 +438,10 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
        }
 
        total = 0;
        }
 
        total = 0;
-       tmp = g_list_last(view->buffer->lines);
-       for (; tmp != NULL; tmp = tmp->prev) {
-               LINE_REC *line = tmp->data;
-
+        line = textbuffer_line_last(view->buffer);
+       for (; line != NULL; line = line->prev) {
                linecount = view_get_linecount(view, line);
                linecount = view_get_linecount(view, line);
-               if (tmp == view->bottom_startline) {
+               if (line == view->bottom_startline) {
                        /* keep the old one, make sure that subline is ok */
                        if (view->bottom_subline > linecount)
                                view->bottom_subline = linecount;
                        /* keep the old one, make sure that subline is ok */
                        if (view->bottom_subline > linecount)
                                view->bottom_subline = linecount;
@@ -347,7 +452,7 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
 
                 total += linecount;
                if (total >= view->height) {
 
                 total += linecount;
                if (total >= view->height) {
-                       view->bottom_startline = tmp;
+                       view->bottom_startline = line;
                        view->bottom_subline = total - view->height;
                         view->empty_linecount = 0;
                         return;
                        view->bottom_subline = total - view->height;
                         view->empty_linecount = 0;
                         return;
@@ -355,27 +460,26 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
        }
 
         /* not enough lines so we must be at the beginning of the buffer */
        }
 
         /* not enough lines so we must be at the beginning of the buffer */
-       view->bottom_startline = view->buffer->lines;
+       view->bottom_startline = view->buffer->first_line;
        view->bottom_subline = 0;
        view->empty_linecount = view->height - total;
 }
 
 static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view)
 {
        view->bottom_subline = 0;
        view->empty_linecount = view->height - total;
 }
 
 static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view)
 {
-       GList *tmp;
+        LINE_REC *line;
 
        g_return_if_fail(view != NULL);
 
        view->ypos = -view->subline-1;
 
        g_return_if_fail(view != NULL);
 
        view->ypos = -view->subline-1;
-       for (tmp = view->startline; tmp != NULL; tmp = tmp->next)
-               view->ypos += view_get_linecount(view, tmp->data);
+       for (line = view->startline; line != NULL; line = line->next)
+               view->ypos += view_get_linecount(view, line);
 }
 
 /* Create new view. */
 TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
                                             int width, int height,
 }
 
 /* Create new view. */
 TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
                                             int width, int height,
-                                            int default_indent,
-                                            int longword_noindent)
+                                            int scroll, int utf8)
 {
        TEXT_BUFFER_VIEW_REC *view;
 
 {
        TEXT_BUFFER_VIEW_REC *view;
 
@@ -388,8 +492,8 @@ TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
 
        view->width = width;
         view->height = height;
 
        view->width = width;
         view->height = height;
-       view->default_indent = default_indent;
-        view->longword_noindent = longword_noindent;
+       view->scroll = scroll;
+        view->utf8 = utf8;
 
        view->cache = textbuffer_cache_get(view->siblings, width);
        textbuffer_view_init_bottom(view);
 
        view->cache = textbuffer_cache_get(view->siblings, width);
        textbuffer_view_init_bottom(view);
@@ -439,60 +543,130 @@ void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view)
 /* Change the default indent position */
 void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
                                        int default_indent,
 /* Change the default indent position */
 void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
                                        int default_indent,
-                                       int longword_noindent)
+                                       int longword_noindent,
+                                       INDENT_FUNC indent_func)
 {
 {
-       view->default_indent = default_indent;
-        view->longword_noindent = longword_noindent;
+        if (default_indent != -1)
+               view->default_indent = default_indent;
+        if (longword_noindent != -1)
+               view->longword_noindent = longword_noindent;
+
+       view->default_indent_func = indent_func;
 }
 
 }
 
-static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, GList *lines)
+static void view_unregister_indent_func(TEXT_BUFFER_VIEW_REC *view,
+                                       INDENT_FUNC indent_func)
+{
+        INDENT_FUNC func;
+       LINE_REC *line;
+        const unsigned char *text, *tmp;
+
+       if (view->default_indent_func == indent_func)
+               view->default_indent_func = NULL;
+
+       /* recreate cache so it won't contain references
+          to the indent function */
+       view_reset_cache(view);
+       view->cache = textbuffer_cache_get(view->siblings, view->width);
+
+        /* remove all references to the indent function from buffer */
+       line = view->buffer->first_line;
+       while (line != NULL) {
+               text = line->text;
+
+               for (text = line->text;; text++) {
+                       if (*text != '\0')
+                               continue;
+
+                        text++;
+                       if (*text == LINE_CMD_EOL)
+                               break;
+
+                       if (*text == LINE_CMD_INDENT_FUNC) {
+                               text++;
+                               memcpy(&func, text, sizeof(INDENT_FUNC));
+                               if (func == indent_func)
+                                        memset(&func, 0, sizeof(INDENT_FUNC));
+                               text += sizeof(INDENT_FUNC);
+                       } else if (*text == LINE_CMD_CONTINUE) {
+                               memcpy(&tmp, text+1, sizeof(char *));
+                               text = tmp-1;
+                       }
+               }
+
+               line = line->next;
+       }
+}
+
+void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func)
+{
+       g_slist_foreach(views, (GFunc) view_unregister_indent_func,
+                       (void *) indent_func);
+}
+
+void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll)
+{
+        view->scroll = scroll;
+}
+
+void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8)
+{
+        view->utf8 = utf8;
+}
+
+static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
        int linecount;
 
         linecount = 0;
 {
        int linecount;
 
         linecount = 0;
-       while (lines != NULL) {
-               linecount += view_get_linecount(view, lines->data);
-                lines = lines->next;
+       while (line != NULL) {
+               linecount += view_get_linecount(view, line);
+                line = line->next;
        }
 
         return linecount;
 }
 
        }
 
         return linecount;
 }
 
-static void view_draw(TEXT_BUFFER_VIEW_REC *view, GList *line,
-                     int subline, int ypos, int lines)
+static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+                     int subline, int ypos, int lines, int fill_bottom)
 {
        int linecount;
 
 {
        int linecount;
 
-       while (line != NULL && lines > 0) {
-               LINE_REC *rec = line->data;
+       if (view->dirty) /* don't bother drawing anything - redraw is coming */
+                return;
 
 
-                linecount = view_line_draw(view, rec, subline, ypos, lines);
+       while (line != NULL && lines > 0) {
+                linecount = view_line_draw(view, line, subline, ypos, lines);
                ypos += linecount; lines -= linecount;
 
                subline = 0;
                 line = line->next;
        }
 
                ypos += linecount; lines -= linecount;
 
                subline = 0;
                 line = line->next;
        }
 
-        /* clear the rest of the view */
-       while (lines > 0) {
-               wmove(view->window, ypos, 0);
-               wclrtoeol(view->window);
-               ypos++; lines--;
+       if (fill_bottom) {
+               /* clear the rest of the view */
+               term_set_color(view->window, ATTR_RESET);
+               while (lines > 0) {
+                       term_move(view->window, 0, ypos);
+                       term_clrtoeol(view->window);
+                       ypos++; lines--;
+               }
        }
 }
 
        }
 }
 
-#define view_draw_top(view, lines) \
-       view_draw(view, (view)->startline, (view)->subline, 0, lines)
+#define view_draw_top(view, lines, fill_bottom) \
+       view_draw(view, (view)->startline, (view)->subline, \
+                 0, lines, fill_bottom)
 
 static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
 {
 
 static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
 {
-       GList *line;
+       LINE_REC *line;
        int ypos, maxline, subline, linecount;
 
        maxline = view->height-lines;
        line = view->startline; ypos = -view->subline; subline = 0;
        while (line != NULL && ypos < maxline) {
        int ypos, maxline, subline, linecount;
 
        maxline = view->height-lines;
        line = view->startline; ypos = -view->subline; subline = 0;
        while (line != NULL && ypos < maxline) {
-                linecount = view_get_linecount(view, line->data);
+                linecount = view_get_linecount(view, line);
                ypos += linecount;
                if (ypos > maxline) {
                        subline = maxline-(ypos-linecount);
                ypos += linecount;
                if (ypos > maxline) {
                        subline = maxline-(ypos-linecount);
@@ -501,12 +675,12 @@ static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
                 line = line->next;
        }
 
                 line = line->next;
        }
 
-        view_draw(view, line, subline, maxline, lines);
+        view_draw(view, line, subline, maxline, lines, TRUE);
 }
 
 /* Returns number of lines actually scrolled */
 }
 
 /* Returns number of lines actually scrolled */
-static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
-                      int scrollcount, int draw_nonclean)
+static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines,
+                      int *subline, int scrollcount, int draw_nonclean)
 {
        int linecount, realcount, scroll_visible;
 
 {
        int linecount, realcount, scroll_visible;
 
@@ -520,7 +694,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
        scrollcount += *subline;
         *subline = 0;
        while (scrollcount > 0) {
        scrollcount += *subline;
         *subline = 0;
        while (scrollcount > 0) {
-               linecount = view_get_linecount(view, (*lines)->data);
+               linecount = view_get_linecount(view, *lines);
 
                if ((scroll_visible && *lines == view->bottom_startline) &&
                    (scrollcount >= view->bottom_subline)) {
 
                if ((scroll_visible && *lines == view->bottom_startline) &&
                    (scrollcount >= view->bottom_subline)) {
@@ -545,7 +719,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
         /* scroll up */
        while (scrollcount < 0 && (*lines)->prev != NULL) {
                *lines = (*lines)->prev;
         /* scroll up */
        while (scrollcount < 0 && (*lines)->prev != NULL) {
                *lines = (*lines)->prev;
-               linecount = view_get_linecount(view, (*lines)->data);
+               linecount = view_get_linecount(view, *lines);
 
                 realcount -= linecount;
                scrollcount += linecount;
 
                 realcount -= linecount;
                scrollcount += linecount;
@@ -562,18 +736,17 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
                           whole view */
                         textbuffer_view_redraw(view);
                } else {
                           whole view */
                         textbuffer_view_redraw(view);
                } else {
-                       scrollok(view->window, TRUE);
-                       wscrl(view->window, realcount);
-                       scrollok(view->window, FALSE);
+                       term_set_color(view->window, ATTR_RESET);
+                       term_window_scroll(view->window, realcount);
 
                        if (draw_nonclean) {
                                if (realcount < 0)
 
                        if (draw_nonclean) {
                                if (realcount < 0)
-                                        view_draw_top(view, -realcount);
+                                        view_draw_top(view, -realcount, TRUE);
                                else
                                        view_draw_bottom(view, realcount);
                        }
 
                                else
                                        view_draw_bottom(view, realcount);
                        }
 
-                       screen_refresh(view->window);
+                       term_refresh(view->window);
                }
        }
 
                }
        }
 
@@ -594,20 +767,19 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
                view->cache = textbuffer_cache_get(view->siblings, width);
        }
 
                view->cache = textbuffer_cache_get(view->siblings, width);
        }
 
-       view->width = width;
-       view->height = height;
-
+       view->width = width > 10 ? width : 10;
+       view->height = height > 1 ? height : 1;
 
 
-        if (view->buffer->lines == NULL) {
-                 view->empty_linecount = height;
-                 return;
-        }
+       if (view->buffer->first_line == NULL) {
+                view->empty_linecount = height;
+               return;
+       }
 
        textbuffer_view_init_bottom(view);
 
        /* check that we didn't scroll lower than bottom startline.. */
 
        textbuffer_view_init_bottom(view);
 
        /* check that we didn't scroll lower than bottom startline.. */
-       if (g_list_find(view->bottom_startline->next,
-                       view->startline->data) != NULL) {
+       if (textbuffer_line_exists_after(view->bottom_startline->next,
+                                        view->startline)) {
                view->startline = view->bottom_startline;
                 view->subline = view->bottom_subline;
        } else if (view->startline == view->bottom_startline &&
                view->startline = view->bottom_startline;
                 view->subline = view->bottom_subline;
        } else if (view->startline == view->bottom_startline &&
@@ -615,7 +787,7 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
                 view->subline = view->bottom_subline;
        } else {
                /* make sure the subline is still in allowed range */
                 view->subline = view->bottom_subline;
        } else {
                /* make sure the subline is still in allowed range */
-               linecount = view_get_linecount(view, view->startline->data);
+               linecount = view_get_linecount(view, view->startline);
                if (view->subline > linecount)
                         view->subline = linecount;
        }
                if (view->subline > linecount)
                         view->subline = linecount;
        }
@@ -640,9 +812,10 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
                        view->subline;
                 if (view->empty_linecount < view->height-linecount)
                        view->empty_linecount = view->height-linecount;
                        view->subline;
                 if (view->empty_linecount < view->height-linecount)
                        view->empty_linecount = view->height-linecount;
+                view->more_text = FALSE;
        }
 
        }
 
-        textbuffer_view_redraw(view);
+       view->dirty = TRUE;
 }
 
 /* Clear the view, don't actually remove any lines from buffer. */
 }
 
 /* Clear the view, don't actually remove any lines from buffer. */
@@ -652,12 +825,13 @@ void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view)
 
        view->ypos = -1;
        view->bottom_startline = view->startline =
 
        view->ypos = -1;
        view->bottom_startline = view->startline =
-               g_list_last(view->buffer->lines);
+               textbuffer_line_last(view->buffer);
        view->bottom_subline = view->subline =
                view->buffer->cur_line == NULL ? 0 :
                view_get_linecount(view, view->buffer->cur_line);
        view->empty_linecount = view->height;
        view->bottom = TRUE;
        view->bottom_subline = view->subline =
                view->buffer->cur_line == NULL ? 0 :
                view_get_linecount(view, view->buffer->cur_line);
        view->empty_linecount = view->height;
        view->bottom = TRUE;
+       view->more_text = FALSE;
 
         textbuffer_view_redraw(view);
 }
 
         textbuffer_view_redraw(view);
 }
@@ -673,35 +847,28 @@ void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines)
                            lines, TRUE);
        view->ypos += lines < 0 ? count : -count;
        view->bottom = view_is_bottom(view);
                            lines, TRUE);
        view->ypos += lines < 0 ? count : -count;
        view->bottom = view_is_bottom(view);
+        if (view->bottom) view->more_text = FALSE;
 
         if (view->window != NULL)
 
         if (view->window != NULL)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* Scroll to specified line */
 void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
 }
 
 /* Scroll to specified line */
 void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
-       GList *tmp;
-
         g_return_if_fail(view != NULL);
 
         g_return_if_fail(view != NULL);
 
-       if (g_list_find(view->bottom_startline->next, line) != NULL) {
+       if (textbuffer_line_exists_after(view->bottom_startline->next, line)) {
                view->startline = view->bottom_startline;
                view->subline = view->bottom_subline;
        } else {
                view->startline = view->bottom_startline;
                view->subline = view->bottom_subline;
        } else {
-               for (tmp = view->buffer->lines; tmp != NULL; tmp = tmp->next) {
-                       LINE_REC *rec = tmp->data;
-
-                       if (rec == line) {
-                               view->startline = tmp;
-                               view->subline = 0;
-                               break;
-                       }
-               }
+               view->startline = line;
+                view->subline = 0;
        }
 
        textbuffer_view_init_ypos(view);
        view->bottom = view_is_bottom(view);
        }
 
        textbuffer_view_init_ypos(view);
        view->bottom = view_is_bottom(view);
+        if (view->bottom) view->more_text = FALSE;
 
        textbuffer_view_redraw(view);
 }
 
        textbuffer_view_redraw(view);
 }
@@ -724,42 +891,20 @@ LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
         return cache;
 }
 
         return cache;
 }
 
-static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
-                             unsigned char update_counter)
-{
-       LINE_CACHE_REC *cache;
-
-       if (view->cache->update_counter == update_counter)
-               return;
-       view->cache->update_counter = update_counter;
-
-       cache = g_hash_table_lookup(view->cache->line_cache, line);
-       if (cache != NULL) {
-                g_free(cache);
-               g_hash_table_remove(view->cache->line_cache, line);
-       }
-}
-
-static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
-                             unsigned char update_counter)
-{
-       view_remove_cache(view, line, update_counter);
-
-       if (view->buffer->cur_line == line)
-               view->cache->last_linecount = view_get_linecount(view, line);
-}
-
 static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
        int linecount, ypos, subline;
 
 static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
        int linecount, ypos, subline;
 
+        if (!view->bottom)
+               view->more_text = TRUE;
+
        if (view->bottom_startline == NULL) {
                view->startline = view->bottom_startline =
        if (view->bottom_startline == NULL) {
                view->startline = view->bottom_startline =
-                       view->buffer->lines;
+                       view->buffer->first_line;
        }
 
        if (view->buffer->cur_line != line &&
        }
 
        if (view->buffer->cur_line != line &&
-           g_list_find(view->bottom_startline, line) == NULL)
+           !textbuffer_line_exists_after(view->bottom_startline, line))
                return;
 
        linecount = view->cache->last_linecount;
                return;
 
        linecount = view->cache->last_linecount;
@@ -780,11 +925,13 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        }
 
        if (view->bottom) {
        }
 
        if (view->bottom) {
-               if (view->ypos >= view->height) {
+               if (view->scroll && view->ypos >= view->height) {
                        linecount = view->ypos-view->height+1;
                        view_scroll(view, &view->startline,
                                    &view->subline, linecount, FALSE);
                        view->ypos -= linecount;
                        linecount = view->ypos-view->height+1;
                        view_scroll(view, &view->startline,
                                    &view->subline, linecount, FALSE);
                        view->ypos -= linecount;
+               } else {
+                       view->bottom = view_is_bottom(view);
                }
 
                if (view->window != NULL) {
                }
 
                if (view->window != NULL) {
@@ -795,13 +942,15 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                                subline = -ypos;
                                ypos = 0;
                        }
                                subline = -ypos;
                                ypos = 0;
                        }
-                       view_line_draw(view, line, subline, ypos,
-                                      view->height - ypos);
+                       if (ypos < view->height) {
+                               view_line_draw(view, line, subline, ypos,
+                                              view->height - ypos);
+                       }
                }
        }
 
         if (view->window != NULL)
                }
        }
 
         if (view->window != NULL)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* Update some line in the buffer which has been modified using
 }
 
 /* Update some line in the buffer which has been modified using
@@ -853,11 +1002,8 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                             (GHFunc) bookmark_check_remove, &rec);
 
        if (rec.remove_list != NULL) {
                             (GHFunc) bookmark_check_remove, &rec);
 
        if (rec.remove_list != NULL) {
-               GList *pos = g_list_find(view->buffer->lines, line);
-
-               new_line = pos == NULL || pos->prev == NULL ? NULL :
-                       (pos->next == NULL ? pos->prev->data :
-                        pos->next->data);
+               new_line = line->prev == NULL ? NULL :
+                       (line->next == NULL ? line->prev : line->next);
                for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) {
                        g_hash_table_remove(view->bookmarks, tmp->data);
                        if (new_line != NULL) {
                for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) {
                        g_hash_table_remove(view->bookmarks, tmp->data);
                        if (new_line != NULL) {
@@ -872,29 +1018,53 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 /* Return number of real lines `lines' list takes -
    stops counting when the height reaches the view height */
 static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view,
 /* Return number of real lines `lines' list takes -
    stops counting when the height reaches the view height */
 static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view,
-                                GList *lines, int subline,
+                                LINE_REC *line, int subline,
                                 LINE_REC *skip_line)
 {
        int height, linecount;
 
         height = -subline;
                                 LINE_REC *skip_line)
 {
        int height, linecount;
 
         height = -subline;
-       while (lines != NULL && height < view->height) {
-               LINE_REC *line = lines->data;
-
+       while (line != NULL && height < view->height) {
                if (line != skip_line) {
                         linecount = view_get_linecount(view, line);
                        height += linecount;
                }
                if (line != skip_line) {
                         linecount = view_get_linecount(view, line);
                        height += linecount;
                }
-                lines = lines->next;
+                line = line->next;
        }
 
        return height < view->height ? height : view->height;
 }
 
        }
 
        return height < view->height ? height : view->height;
 }
 
+static void view_remove_line_update_startline(TEXT_BUFFER_VIEW_REC *view,
+                                             LINE_REC *line, int linecount)
+{
+       int scroll;
+
+       if (view->startline == line) {
+               view->startline = view->startline->prev != NULL ?
+                       view->startline->prev : view->startline->next;
+               view->subline = 0;
+       } else {
+               scroll = view->height -
+                       view_get_lines_height(view, view->startline,
+                                             view->subline, line);
+               if (scroll > 0) {
+                       view_scroll(view, &view->startline,
+                                   &view->subline, -scroll, FALSE);
+               }
+       }
+
+       /* FIXME: this is slow and unnecessary, but it's easy and
+          really works :) */
+       textbuffer_view_init_ypos(view);
+       if (textbuffer_line_exists_after(view->startline, line))
+               view->ypos -= linecount;
+}
+
 static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                             int linecount)
 {
 static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                             int linecount)
 {
-       int realcount, scroll;
+       int realcount;
 
        view_bookmarks_check(view, line);
 
 
        view_bookmarks_check(view, line);
 
@@ -902,62 +1072,49 @@ static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                 /* the last line is being removed */
                LINE_REC *prevline;
 
                 /* the last line is being removed */
                LINE_REC *prevline;
 
-               prevline = view->buffer->lines->data == line ? NULL :
-                       g_list_last(view->bottom_startline)->data;
+               prevline = view->buffer->first_line == line ? NULL :
+                       textbuffer_line_last(view->buffer);
                view->cache->last_linecount = prevline == NULL ? 0 :
                        view_get_linecount(view, prevline);
        }
 
                view->cache->last_linecount = prevline == NULL ? 0 :
                        view_get_linecount(view, prevline);
        }
 
-       if (line == view->buffer->lines->data) {
+       if (view->buffer->first_line == line) {
                /* first line in the buffer - this is the most commonly
                   removed line.. */
                /* first line in the buffer - this is the most commonly
                   removed line.. */
-               if (view->bottom_startline->data == line) {
+               if (view->bottom_startline == line) {
                        /* very small scrollback.. */
                         view->bottom_startline = view->bottom_startline->next;
                        view->bottom_subline = 0;
                }
 
                        /* very small scrollback.. */
                         view->bottom_startline = view->bottom_startline->next;
                        view->bottom_subline = 0;
                }
 
-               if (view->startline->data == line) {
+               if (view->startline == line) {
                         /* removing the first line in screen */
                        realcount = view_scroll(view, &view->startline,
                                                &view->subline,
                         /* removing the first line in screen */
                        realcount = view_scroll(view, &view->startline,
                                                &view->subline,
-                                               linecount, TRUE);
+                                               linecount, FALSE);
                        view->ypos -= realcount;
                        view->empty_linecount += linecount-realcount;
                }
                        view->ypos -= realcount;
                        view->empty_linecount += linecount-realcount;
                }
-       } else if (g_list_find(view->bottom_startline, line) != NULL) {
-               realcount = view_scroll(view, &view->bottom_startline,
-                                       &view->bottom_subline,
-                                       -linecount, FALSE);
-               if (view->bottom) {
-                       /* we're at the bottom, remove the same amount as
-                          from bottom_startline */
-                       view_scroll(view, &view->startline,
-                                   &view->subline, -linecount, TRUE);
-                       view->ypos -= linecount-realcount;
-               } else {
-                       if (view->startline->data == line) {
-                               view->startline =
-                                       view->startline->next != NULL ?
-                                       view->startline->next :
-                                       view->startline->prev;
-                                view->subline = 0;
-                       }
-                       scroll = view->height -
-                               view_get_lines_height(view, view->startline,
-                                                      view->subline, line);
-                       if (scroll > 0) {
-                               view_scroll(view, &view->startline,
-                                           &view->subline, -scroll, TRUE);
-                                view->ypos -= scroll;
-                       }
+       } else {
+               if (textbuffer_line_exists_after(view->bottom_startline,
+                                                line)) {
+                       realcount = view_scroll(view, &view->bottom_startline,
+                                               &view->bottom_subline,
+                                               -linecount, FALSE);
+                       view->empty_linecount += linecount-realcount;
+               }
+
+               if (textbuffer_line_exists_after(view->startline,
+                                                line)) {
+                       view_remove_line_update_startline(view, line,
+                                                         linecount);
                }
                }
-               view->empty_linecount += linecount-realcount;
        }
 
        view->bottom = view_is_bottom(view);
        }
 
        view->bottom = view_is_bottom(view);
+        if (view->bottom) view->more_text = FALSE;
         if (view->window != NULL)
         if (view->window != NULL)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* Remove one line from buffer. */
 }
 
 /* Remove one line from buffer. */
@@ -986,30 +1143,25 @@ void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        textbuffer_remove(view->buffer, line);
 }
 
        textbuffer_remove(view->buffer, line);
 }
 
+static int g_free_true(void *data)
+{
+       g_free(data);
+        return TRUE;
+}
+
 /* Remove all lines from buffer. */
 void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view)
 {
 /* Remove all lines from buffer. */
 void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view)
 {
-       GSList *tmp;
-
        g_return_if_fail(view != NULL);
 
        textbuffer_remove_all_lines(view->buffer);
 
        g_return_if_fail(view != NULL);
 
        textbuffer_remove_all_lines(view->buffer);
 
-       /* destroy line caches - note that you can't do simultaneously
-          unrefs + cache_get()s or it will keep using the old caches */
-       textbuffer_cache_unref(view->cache);
-        g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL);
+       g_hash_table_foreach_remove(view->bookmarks,
+                                   (GHRFunc) g_free_true, NULL);
 
 
-        /* recreate caches, clear screens */
-       view->cache = textbuffer_cache_get(view->siblings, view->width);
+       view_reset_cache(view);
        textbuffer_view_clear(view);
        textbuffer_view_clear(view);
-
-       for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
-               TEXT_BUFFER_VIEW_REC *rec = tmp->data;
-
-               rec->cache = textbuffer_cache_get(rec->siblings, rec->width);
-               textbuffer_view_clear(rec);
-       }
+       g_slist_foreach(view->siblings, (GFunc) textbuffer_view_clear, NULL);
 }
 
 /* Set a bookmark in view */
 }
 
 /* Set a bookmark in view */
@@ -1040,7 +1192,7 @@ void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
        g_return_if_fail(name != NULL);
 
        if (view->bottom_startline != NULL) {
        g_return_if_fail(name != NULL);
 
        if (view->bottom_startline != NULL) {
-                line = g_list_last(view->bottom_startline)->data;
+                line = textbuffer_line_last(view->buffer);
                textbuffer_view_set_bookmark(view, name, line);
        }
 }
                textbuffer_view_set_bookmark(view, name, line);
        }
 }
@@ -1057,14 +1209,15 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
 
 /* Specify window where the changes in view should be drawn,
    NULL disables it. */
 
 /* Specify window where the changes in view should be drawn,
    NULL disables it. */
-void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window)
+void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view,
+                               TERM_WINDOW *window)
 {
        g_return_if_fail(view != NULL);
 
        if (view->window != window) {
                view->window = window;
 {
        g_return_if_fail(view != NULL);
 
        if (view->window != window) {
                view->window = window;
-               if (window != NULL)
-                       textbuffer_view_redraw(view);
+                if (window != NULL)
+                       view->dirty = TRUE;
        }
 }
 
        }
 }
 
@@ -1074,9 +1227,9 @@ void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view)
        g_return_if_fail(view != NULL);
 
        if (view->window != NULL) {
        g_return_if_fail(view != NULL);
 
        if (view->window != NULL) {
-                werase(view->window);
-               view_draw_top(view, view->height);
-               screen_refresh(view->window);
+               view->dirty = FALSE;
+               view_draw_top(view, view->height, TRUE);
+               term_refresh(view->window);
        }
 }
 
        }
 }
 
@@ -1107,6 +1260,8 @@ static int sig_check_linecache(void)
                                            (GHRFunc) line_cache_check_remove,
                                            &now);
        }
                                            (GHRFunc) line_cache_check_remove,
                                            &now);
        }
+
+        g_slist_free(caches);
        return 1;
 }
 
        return 1;
 }
 
index 21ed28cfb4b80c39f460b442f1f18d1554565ead..b529ebedd099436225c64e8c10b768b1da5c5872 100644 (file)
@@ -2,11 +2,18 @@
 #define __TEXTBUFFER_VIEW_H
 
 #include "textbuffer.h"
 #define __TEXTBUFFER_VIEW_H
 
 #include "textbuffer.h"
-#include "screen.h"
+#include "term.h"
+
+typedef struct _TEXT_BUFFER_VIEW_REC TEXT_BUFFER_VIEW_REC;
+
+/* if ypos == -1, don't print anything, just return the indent size */
+typedef int (*INDENT_FUNC) (TEXT_BUFFER_VIEW_REC *view,
+                           LINE_REC *line, int ypos);
 
 typedef struct {
 
 typedef struct {
-       unsigned char *start;
+       const unsigned char *start;
        int indent;
        int indent;
+        INDENT_FUNC indent_func;
        int color;
 
        /* first word in line belong to the end of the last word in
        int color;
 
        /* first word in line belong to the end of the last word in
@@ -37,24 +44,27 @@ typedef struct {
        int last_linecount;
 } TEXT_BUFFER_CACHE_REC;
 
        int last_linecount;
 } TEXT_BUFFER_CACHE_REC;
 
-typedef struct {
+struct _TEXT_BUFFER_VIEW_REC {
        TEXT_BUFFER_REC *buffer;
        GSList *siblings; /* other views that use the same buffer */
 
        TEXT_BUFFER_REC *buffer;
        GSList *siblings; /* other views that use the same buffer */
 
-        WINDOW *window;
+        TERM_WINDOW *window;
        int width, height;
 
        int default_indent;
        int width, height;
 
        int default_indent;
-       int longword_noindent:1;
+        INDENT_FUNC default_indent_func;
+       unsigned int longword_noindent:1;
+       unsigned int scroll:1; /* scroll down automatically when at bottom */
+       unsigned int utf8:1; /* use UTF8 in this view */
 
        TEXT_BUFFER_CACHE_REC *cache;
        int ypos; /* cursor position - visible area is 0..height-1 */
 
 
        TEXT_BUFFER_CACHE_REC *cache;
        int ypos; /* cursor position - visible area is 0..height-1 */
 
-       GList *startline; /* line at the top of the screen */
+       LINE_REC *startline; /* line at the top of the screen */
        int subline; /* number of "real lines" to skip from `startline' */
 
         /* marks the bottom of the text buffer */
        int subline; /* number of "real lines" to skip from `startline' */
 
         /* marks the bottom of the text buffer */
-       GList *bottom_startline;
+       LINE_REC *bottom_startline;
        int bottom_subline;
 
        /* how many empty lines are in screen. a screenful when started
        int bottom_subline;
 
        /* how many empty lines are in screen. a screenful when started
@@ -62,23 +72,31 @@ typedef struct {
        int empty_linecount; 
         /* window is at the bottom of the text buffer */
        unsigned int bottom:1;
        int empty_linecount; 
         /* window is at the bottom of the text buffer */
        unsigned int bottom:1;
+        /* if !bottom - new text has been printed since we were at bottom */
+       unsigned int more_text:1;
+        /* Window needs a redraw */
+       unsigned int dirty:1;
 
        /* Bookmarks to the lines in the buffer - removed automatically
           when the line gets removed from buffer */
         GHashTable *bookmarks;
 
        /* Bookmarks to the lines in the buffer - removed automatically
           when the line gets removed from buffer */
         GHashTable *bookmarks;
-} TEXT_BUFFER_VIEW_REC;
+};
 
 /* Create new view. */
 TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
                                             int width, int height,
 
 /* Create new view. */
 TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
                                             int width, int height,
-                                            int default_indent,
-                                            int longword_noindent);
+                                            int scroll, int utf8);
 /* Destroy the view. */
 void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view);
 /* Change the default indent position */
 void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
                                        int default_indent,
 /* Destroy the view. */
 void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view);
 /* Change the default indent position */
 void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
                                        int default_indent,
-                                       int longword_noindent);
+                                       int longword_noindent,
+                                       INDENT_FUNC indent_func);
+void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func);
+
+void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll);
+void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8);
 
 /* Resize the view. */
 void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height);
 
 /* Resize the view. */
 void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height);
@@ -86,7 +104,7 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height);
 void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view);
 
 #define textbuffer_view_get_lines(view) \
 void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view);
 
 #define textbuffer_view_get_lines(view) \
-        ((view)->buffer->lines)
+        ((view)->buffer->first_line)
 
 /* Scroll the view up/down */
 void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines);
 
 /* Scroll the view up/down */
 void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines);
@@ -121,7 +139,8 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
 
 /* Specify window where the changes in view should be drawn,
    NULL disables it. */
 
 /* Specify window where the changes in view should be drawn,
    NULL disables it. */
-void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window);
+void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view,
+                               TERM_WINDOW *window);
 /* Redraw the view */
 void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view);
 
 /* Redraw the view */
 void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view);
 
index f97f46c74144336cad34ae769f9e4d3c75b1c9fe..8ac8bf0043efdb0fa7d3c8f40d2c5886efe9a213 100644 (file)
@@ -73,7 +73,7 @@ static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer,
 static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
 {
        TEXT_CHUNK_REC *rec;
 static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
 {
        TEXT_CHUNK_REC *rec;
-       char *buf, *ptr, **pptr;
+       unsigned char *buf, *ptr, **pptr;
 
        rec = g_mem_chunk_alloc(text_chunk);
        rec->pos = 0;
 
        rec = g_mem_chunk_alloc(text_chunk);
        rec->pos = 0;
@@ -90,7 +90,7 @@ static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
                   breaks at least NetBSD/Alpha, so don't go "optimize"
                   it :) */
                ptr = rec->buffer; pptr = &ptr;
                   breaks at least NetBSD/Alpha, so don't go "optimize"
                   it :) */
                ptr = rec->buffer; pptr = &ptr;
-               memcpy(buf, pptr, sizeof(char *));
+               memcpy(buf, pptr, sizeof(unsigned char *));
        } else {
                /* just to be safe */
                mark_temp_eol(rec);
        } else {
                /* just to be safe */
                mark_temp_eol(rec);
@@ -140,7 +140,7 @@ static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
 }
 
 static void text_chunk_append(TEXT_BUFFER_REC *buffer,
 }
 
 static void text_chunk_append(TEXT_BUFFER_REC *buffer,
-                             const char *data, int len)
+                             const unsigned char *data, int len)
 {
         TEXT_CHUNK_REC *chunk;
        int left;
 {
         TEXT_CHUNK_REC *chunk;
        int left;
@@ -151,7 +151,8 @@ static void text_chunk_append(TEXT_BUFFER_REC *buffer,
         chunk = buffer->cur_text;
        while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
                left = TEXT_CHUNK_USABLE_SIZE - chunk->pos;
         chunk = buffer->cur_text;
        while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
                left = TEXT_CHUNK_USABLE_SIZE - chunk->pos;
-               if (data[left-1] == 0) left--; /* don't split the commands */
+               if (left > 0 && data[left-1] == 0)
+                       left--; /* don't split the commands */
 
                memcpy(chunk->buffer + chunk->pos, data, left);
                chunk->pos += left;
 
                memcpy(chunk->buffer + chunk->pos, data, left);
                chunk->pos += left;
@@ -187,14 +188,22 @@ static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
 {
        LINE_REC *line;
 
 {
        LINE_REC *line;
 
-        line = textbuffer_line_create(buffer);
-       if (prev == buffer->cur_line) {
-               buffer->cur_line = line;
-               buffer->lines = g_list_append(buffer->lines, buffer->cur_line);
+       line = textbuffer_line_create(buffer);
+       line->prev = prev;
+       if (prev == NULL) {
+               line->next = buffer->first_line;
+                if (buffer->first_line != NULL)
+                       buffer->first_line->prev = line;
+               buffer->first_line = line;
        } else {
        } else {
-               buffer->lines = g_list_insert(buffer->lines, line,
-                                             g_list_index(buffer->lines, prev)+1);
+               line->next = prev->next;
+                if (line->next != NULL)
+                       line->next->prev = line;
+               prev->next = line;
        }
        }
+
+       if (prev == buffer->cur_line)
+               buffer->cur_line = line;
         buffer->lines_count++;
 
         return line;
         buffer->lines_count++;
 
         return line;
@@ -224,11 +233,34 @@ void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list)
        g_return_if_fail(buffer != NULL);
 
        while (list != NULL) {
        g_return_if_fail(buffer != NULL);
 
        while (list != NULL) {
-                textbuffer_line_unref(buffer, list->data);
+                if (list->data != NULL)
+                       textbuffer_line_unref(buffer, list->data);
                 list = list->next;
        }
 }
 
                 list = list->next;
        }
 }
 
+LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer)
+{
+       LINE_REC *line;
+
+       line = buffer->cur_line;
+       if (line != NULL) {
+               while (line->next != NULL)
+                       line = line->next;
+       }
+        return line;
+}
+
+int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search)
+{
+       while (line != NULL) {
+               if (line == search)
+                       return TRUE;
+                line = line->next;
+       }
+        return FALSE;
+}
+
 LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
                            const unsigned char *data, int len,
                            LINE_INFO_REC *info)
 LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
                            const unsigned char *data, int len,
                            LINE_INFO_REC *info)
@@ -245,6 +277,9 @@ LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(data != NULL, NULL);
 
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(data != NULL, NULL);
 
+       if (len == 0)
+                return insert_after;
+
        line = !buffer->last_eol ? insert_after :
                textbuffer_line_insert(buffer, insert_after);
 
        line = !buffer->last_eol ? insert_after :
                textbuffer_line_insert(buffer, insert_after);
 
@@ -264,13 +299,20 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
        g_return_if_fail(buffer != NULL);
        g_return_if_fail(line != NULL);
 
        g_return_if_fail(buffer != NULL);
        g_return_if_fail(line != NULL);
 
-       buffer->lines = g_list_remove(buffer->lines, line);
+       if (buffer->first_line == line)
+               buffer->first_line = line->next;
+       if (line->prev != NULL)
+               line->prev->next = line->next;
+       if (line->next != NULL)
+               line->next->prev = line->prev;
 
        if (buffer->cur_line == line) {
 
        if (buffer->cur_line == line) {
-               buffer->cur_line = buffer->lines == NULL ? NULL :
-                       g_list_last(buffer->lines)->data;
+               buffer->cur_line = line->next != NULL ?
+                       line->next : line->prev;
        }
 
        }
 
+        line->prev = line->next = NULL;
+
        buffer->lines_count--;
         textbuffer_line_unref(buffer, line);
 }
        buffer->lines_count--;
         textbuffer_line_unref(buffer, line);
 }
@@ -279,40 +321,85 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
 void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
 {
        GSList *tmp;
 void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
 {
        GSList *tmp;
+        LINE_REC *line;
 
        g_return_if_fail(buffer != NULL);
 
        for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next)
                 g_mem_chunk_free(text_chunk, tmp->data);
        g_slist_free(buffer->text_chunks);
 
        g_return_if_fail(buffer != NULL);
 
        for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next)
                 g_mem_chunk_free(text_chunk, tmp->data);
        g_slist_free(buffer->text_chunks);
-        buffer->text_chunks = NULL;
+       buffer->text_chunks = NULL;
 
 
-       g_list_free(buffer->lines);
-        buffer->lines = NULL;
+       while (buffer->first_line != NULL) {
+               line = buffer->first_line->next;
+               g_mem_chunk_free(line_chunk, buffer->first_line);
+                buffer->first_line = line;
+       }
+       buffer->lines_count = 0;
 
         buffer->cur_line = NULL;
 
         buffer->cur_line = NULL;
-       buffer->lines_count = 0;
+        buffer->cur_text = NULL;
+
+       buffer->last_eol = TRUE;
+}
+
+static void set_color(GString *str, int cmd, int *last_fg, int *last_bg)
+{
+       if (cmd & LINE_COLOR_DEFAULT) {
+               g_string_sprintfa(str, "\004%c", FORMAT_STYLE_DEFAULTS);
+
+               /* need to reset the fg/bg color */
+               if (cmd & LINE_COLOR_BG) {
+                        *last_bg = -1;
+                       if (*last_fg != -1) {
+                               g_string_sprintfa(str, "\004%c%c",
+                                                 *last_fg,
+                                                 FORMAT_COLOR_NOCHANGE);
+                       }
+               } else {
+                        *last_fg = -1;
+                       if (*last_bg != -1) {
+                               g_string_sprintfa(str, "\004%c%c",
+                                                 FORMAT_COLOR_NOCHANGE,
+                                                 *last_bg);
+                       }
+               }
+                return;
+       }
+
+       if ((cmd & LINE_COLOR_BG) == 0) {
+                /* change foreground color */
+                *last_fg = (cmd & 0x0f)+'0';
+               g_string_sprintfa(str, "\004%c%c", *last_fg,
+                                 FORMAT_COLOR_NOCHANGE);
+       } else {
+               /* change background color */
+                *last_bg = (cmd & 0x0f)+'0';
+               g_string_sprintfa(str, "\004%c%c",
+                                 FORMAT_COLOR_NOCHANGE, *last_bg);
+       }
 }
 
 void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 {
 }
 
 void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 {
-        unsigned char cmd;
-       char *ptr, *tmp;
+        unsigned char cmd, *ptr, *tmp;
+        int last_fg, last_bg;
 
        g_return_if_fail(line != NULL);
        g_return_if_fail(str != NULL);
 
         g_string_truncate(str, 0);
 
 
        g_return_if_fail(line != NULL);
        g_return_if_fail(str != NULL);
 
         g_string_truncate(str, 0);
 
+        last_fg = last_bg = -1;
        for (ptr = line->text;;) {
                if (*ptr != 0) {
        for (ptr = line->text;;) {
                if (*ptr != 0) {
-                       g_string_append_c(str, *ptr);
+                       g_string_append_c(str, (char) *ptr);
                         ptr++;
                        continue;
                }
 
                ptr++;
                         ptr++;
                        continue;
                }
 
                ptr++;
-                cmd = (unsigned char) *ptr;
+                cmd = *ptr;
                ptr++;
 
                if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
                ptr++;
 
                if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
@@ -322,7 +409,7 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 
                if (cmd == LINE_CMD_CONTINUE) {
                         /* line continues in another address.. */
 
                if (cmd == LINE_CMD_CONTINUE) {
                         /* line continues in another address.. */
-                       memcpy(&tmp, ptr, sizeof(char *));
+                       memcpy(&tmp, ptr, sizeof(unsigned char *));
                        ptr = tmp;
                         continue;
                }
                        ptr = tmp;
                         continue;
                }
@@ -334,40 +421,39 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 
                if ((cmd & 0x80) == 0) {
                        /* set color */
 
                if ((cmd & 0x80) == 0) {
                        /* set color */
-                       g_string_sprintfa(str, "\004%c%c",
-                                         (cmd & 0x0f)+'0',
-                                         ((cmd & 0xf0) >> 4)+'0');
+                        set_color(str, cmd, &last_fg, &last_bg);
                } else switch (cmd) {
                case LINE_CMD_UNDERLINE:
                        g_string_append_c(str, 31);
                        break;
                } else switch (cmd) {
                case LINE_CMD_UNDERLINE:
                        g_string_append_c(str, 31);
                        break;
+               case LINE_CMD_REVERSE:
+                       g_string_append_c(str, 22);
+                       break;
                case LINE_CMD_COLOR0:
                        g_string_sprintfa(str, "\004%c%c",
                                          '0', FORMAT_COLOR_NOCHANGE);
                        break;
                case LINE_CMD_COLOR0:
                        g_string_sprintfa(str, "\004%c%c",
                                          '0', FORMAT_COLOR_NOCHANGE);
                        break;
-               case LINE_CMD_COLOR8:
-                       g_string_sprintfa(str, "\004%c%c",
-                                         '8', FORMAT_COLOR_NOCHANGE);
-                       break;
-               case LINE_CMD_BLINK:
-                       g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK);
-                       break;
                case LINE_CMD_INDENT:
                        break;
                case LINE_CMD_INDENT:
                        break;
+               case LINE_CMD_INDENT_FUNC:
+                        ptr += sizeof(void *);
+                       break;
                }
        }
 }
 
 GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
                            int level, int nolevel, const char *text,
                }
        }
 }
 
 GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
                            int level, int nolevel, const char *text,
+                           int before, int after,
                            int regexp, int fullword, int case_sensitive)
 {
 #ifdef HAVE_REGEX_H
        regex_t preg;
 #endif
                            int regexp, int fullword, int case_sensitive)
 {
 #ifdef HAVE_REGEX_H
        regex_t preg;
 #endif
-       GList *line, *tmp;
+        LINE_REC *line, *pre_line;
        GList *matches;
        GList *matches;
-        GString *str;
+       GString *str;
+        int i, match_after, line_matched;
 
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(text != NULL, NULL);
 
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(text != NULL, NULL);
@@ -383,216 +469,65 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
 #endif
        }
 
 #endif
        }
 
-       matches = NULL;
+       matches = NULL; match_after = 0;
         str = g_string_new(NULL);
 
         str = g_string_new(NULL);
 
-        line = g_list_find(buffer->lines, startline);
-       if (line == NULL)
-               line = buffer->lines;
-
-       for (tmp = line; tmp != NULL; tmp = tmp->next) {
-               LINE_REC *rec = tmp->data;
+       line = startline != NULL ? startline : buffer->first_line;
 
 
-               if ((rec->info.level & level) == 0 ||
-                   (rec->info.level & nolevel) != 0)
+       for (; line != NULL; line = line->next) {
+               if ((line->info.level & level) == 0 ||
+                   (line->info.level & nolevel) != 0)
                         continue;
 
                if (*text == '\0') {
                         /* no search word, everything matches */
                         continue;
 
                if (*text == '\0') {
                         /* no search word, everything matches */
-                        textbuffer_line_ref(rec);
-                       matches = g_list_append(matches, rec);
+                        textbuffer_line_ref(line);
+                       matches = g_list_append(matches, line);
                        continue;
                }
 
                        continue;
                }
 
-                textbuffer_line2text(rec, FALSE, str);
+                textbuffer_line2text(line, FALSE, str);
 
 
-                if (
+               line_matched =
 #ifdef HAVE_REGEX_H
 #ifdef HAVE_REGEX_H
-                   regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+                       regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
 #endif
 #endif
-                   fullword ? strstr_full_case(str->str, text,
-                                               !case_sensitive) != NULL :
-                   case_sensitive ? strstr(str->str, text) != NULL :
-                                    stristr(str->str, text) != NULL) {
-                       /* matched */
-                        textbuffer_line_ref(rec);
-                       matches = g_list_append(matches, rec);
-               }
-       }
-#ifdef HAVE_REGEX_H
-       if (regexp) regfree(&preg);
-#endif
-        g_string_free(str, TRUE);
-       return matches;
-}
-
-#if 0 /* FIXME: saving formats is broken */
-static char *line_read_format(unsigned const char **text)
-{
-       GString *str;
-       char *ret;
-
-       str = g_string_new(NULL);
-       for (;;) {
-               if (**text == '\0') {
-                       if ((*text)[1] == LINE_CMD_EOL) {
-                               /* leave text at \0<eof> */
-                               break;
+                       fullword ? strstr_full_case(str->str, text, !case_sensitive) != NULL :
+                       case_sensitive ? strstr(str->str, text) != NULL :
+                       stristr(str->str, text) != NULL;
+               if (line_matched) {
+                        /* add the -before lines */
+                       pre_line = line;
+                       for (i = 0; i < before; i++) {
+                               if (pre_line->prev == NULL ||
+                                   g_list_find(matches, pre_line->prev) != NULL)
+                                       break;
+                                pre_line = pre_line->prev;
                        }
                        }
-                       if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
-                               /* leave text at \0<format_cont> */
-                               break;
-                       }
-                       (*text)++;
 
 
-                       if (**text == LINE_CMD_FORMAT) {
-                               /* move text to start after \0<format> */
-                               (*text)++;
-                               break;
+                       for (; pre_line != line; pre_line = pre_line->next) {
+                               textbuffer_line_ref(pre_line);
+                               matches = g_list_append(matches, pre_line);
                        }
 
                        }
 
-                       if (**text == LINE_CMD_CONTINUE) {
-                               unsigned char *tmp;
-
-                               memcpy(&tmp, (*text)+1, sizeof(char *));
-                               *text = tmp;
-                               continue;
-                       } else if (**text & 0x80)
-                               (*text)++;
-                       continue;
+                       match_after = after;
                }
 
                }
 
-               g_string_append_c(str, (char) **text);
-               (*text)++;
-       }
-
-       ret = str->str;
-       g_string_free(str, FALSE);
-       return ret;
-}
-
-static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
-                                       GString *raw)
-{
-       const unsigned char *text;
-       char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
-       TEXT_DEST_REC dest;
-       int formatnum, argcount;
-
-       text = (const unsigned char *) line->text;
-
-       /* skip the beginning of the line until we find the format */
-       g_free(line_read_format(&text));
-       if (text[1] == LINE_CMD_FORMAT_CONT) {
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT);
-               return NULL;
-       }
-
-       /* read format information */
-        module = line_read_format(&text);
-       format_name = line_read_format(&text);
-
-       if (raw != NULL) {
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
-
-               g_string_append(raw, module);
-
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
-
-               g_string_append(raw, format_name);
-       }
+               if (line_matched || match_after > 0) {
+                       /* matched */
+                        textbuffer_line_ref(line);
+                       matches = g_list_append(matches, line);
 
 
-       formatnum = format_find_tag(module, format_name);
-       if (formatnum == -1)
-               ret = NULL;
-       else {
-                argcount = 0;
-                memset(args, 0, sizeof(args));
-               while (*text != '\0' || text[1] != LINE_CMD_EOL) {
-                       args[argcount] = line_read_format(&text);
-                       if (raw != NULL) {
-                               g_string_append_c(raw, '\0');
-                               g_string_append_c(raw,
-                                                 (char)LINE_CMD_FORMAT);
-
-                               g_string_append(raw, args[argcount]);
-                       }
-                       argcount++;
+                       if (!line_matched && --match_after == 0)
+                               matches = g_list_append(matches, NULL);
                }
                }
-
-               /* get the format text */
-               format_create_dest(&dest, NULL, NULL, line->level, window);
-               ret = format_get_text_theme_charargs(current_theme,
-                                                    module, &dest,
-                                                    formatnum, args);
-               while (argcount > 0)
-                       g_free(args[--argcount]);
        }
        }
-
-       g_free(module);
-       g_free(format_name);
-
-       return ret;
-}
-
-void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line)
-{
-       GUI_WINDOW_REC *gui;
-       TEXT_DEST_REC dest;
-       GString *raw;
-       char *str, *tmp, *prestr, *linestart, *leveltag;
-
-       gui = WINDOW_GUI(window);
-
-       raw = g_string_new(NULL);
-       str = textbuffer_line_get_format(window, line, raw);
-
-        if (str == NULL && raw->len == 2 &&
-            raw->str[1] == (char)LINE_CMD_FORMAT_CONT) {
-                /* multiline format, format explained in one the
-                   following lines. remove this line. */
-                textbuffer_line_remove(window, line, FALSE);
-       } else if (str != NULL) {
-                /* FIXME: ugly ugly .. and this can't handle
-                   non-formatted lines.. */
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_EOL);
-
-                textbuffer_line_text_free(gui, line);
-
-                gui->temp_line = line;
-               gui->temp_line->text = gui->cur_text->buffer+gui->cur_text->pos;
-                gui->cur_text->lines++;
-               gui->eol_marked = FALSE;
-
-               format_create_dest(&dest, NULL, NULL, line->level, window);
-
-               linestart = format_get_line_start(current_theme, &dest, line->time);
-               leveltag = format_get_level_tag(current_theme, &dest);
-
-               prestr = g_strconcat(linestart == NULL ? "" : linestart,
-                                    leveltag, NULL);
-               g_free_not_null(linestart);
-               g_free_not_null(leveltag);
-
-               tmp = format_add_linestart(str, prestr);
-               g_free(str);
-               g_free(prestr);
-
-               format_send_to_gui(&dest, tmp);
-               g_free(tmp);
-
-               textbuffer_line_append(gui, raw->str, raw->len);
-
-               gui->eol_marked = TRUE;
-               gui->temp_line = NULL;
-       }
-       g_string_free(raw, TRUE);
-}
+#ifdef HAVE_REGEX_H
+       if (regexp) regfree(&preg);
 #endif
 #endif
+        g_string_free(str, TRUE);
+       return matches;
+}
 
 void textbuffer_init(void)
 {
 
 void textbuffer_init(void)
 {
index 21f70e260de022c4ad49631e6b921ee0035a2598..adea3604c3a84613c71181235758e6fe1c40c9b2 100644 (file)
@@ -1,20 +1,21 @@
 #ifndef __TEXTBUFFER_H
 #define __TEXTBUFFER_H
 
 #ifndef __TEXTBUFFER_H
 #define __TEXTBUFFER_H
 
-/* FIXME: Textbuffer code gets a lot faster in some points when I get rid of
-   GList and make prev/next pointers directly in LINE_REC. However, this
-   can still wait for a while until I get rid of GList entirely everywhere. */
-
 #define LINE_TEXT_CHUNK_SIZE 16384
 
 #define LINE_TEXT_CHUNK_SIZE 16384
 
+#define LINE_COLOR_BG          0x20
+#define LINE_COLOR_DEFAULT     0x10
+#define LINE_COLOR_BOLD                0x08
+#define LINE_COLOR_BLINK               0x08
+
 enum {
        LINE_CMD_EOL=0x80,      /* line ends here */
        LINE_CMD_CONTINUE,      /* line continues in next block */
        LINE_CMD_COLOR0,        /* change to black, would be same as \0\0 but it breaks things.. */
 enum {
        LINE_CMD_EOL=0x80,      /* line ends here */
        LINE_CMD_CONTINUE,      /* line continues in next block */
        LINE_CMD_COLOR0,        /* change to black, would be same as \0\0 but it breaks things.. */
-       LINE_CMD_COLOR8,        /* change to dark grey, normally 8 = bold black */
        LINE_CMD_UNDERLINE,     /* enable/disable underlining */
        LINE_CMD_UNDERLINE,     /* enable/disable underlining */
+       LINE_CMD_REVERSE,       /* enable/disable reversed text */
        LINE_CMD_INDENT,        /* if line is split, indent it at this position */
        LINE_CMD_INDENT,        /* if line is split, indent it at this position */
-       LINE_CMD_BLINK,         /* blinking background */
+       LINE_CMD_INDENT_FUNC,   /* if line is split, use the specified indentation function */
        LINE_CMD_FORMAT,        /* end of line, but next will come the format that was used to create the
                                   text in format <module><format_name><arg><arg2...> - fields are separated
                                   with \0<format> and last argument ends with \0<eol>. \0<continue> is allowed
        LINE_CMD_FORMAT,        /* end of line, but next will come the format that was used to create the
                                   text in format <module><format_name><arg><arg2...> - fields are separated
                                   with \0<format> and last argument ends with \0<eol>. \0<continue> is allowed
@@ -27,13 +28,23 @@ typedef struct {
        time_t time;
 } LINE_INFO_REC;
 
        time_t time;
 } LINE_INFO_REC;
 
-typedef struct {
-       /* text in the line. \0 means that the next char will be a
-          color or command. <= 127 = color or if 8. bit is set, the
-          first 7 bits are the command. See LINE_CMD_xxxx.
+typedef struct _LINE_REC {
+       /* Text in the line. \0 means that the next char will be a
+          color or command.
+
+          If the 7th bit is set, the lowest 7 bits are the command
+          (see LINE_CMD_xxxx). Otherwise they specify a color change:
+
+          Bit:
+            5 - Setting a background color
+            4 - Use "default terminal color"
+            3 - Bold (fg) / blink (bg) - can be used with 4th bit
+            0-2 - Color
 
           DO NOT ADD BLACK WITH \0\0 - this will break things. Use
           LINE_CMD_COLOR0 instead. */
 
           DO NOT ADD BLACK WITH \0\0 - this will break things. Use
           LINE_CMD_COLOR0 instead. */
+       struct _LINE_REC *prev, *next;
+
        unsigned char *text;
         unsigned char refcount;
         LINE_INFO_REC info;
        unsigned char *text;
         unsigned char refcount;
         LINE_INFO_REC info;
@@ -47,7 +58,7 @@ typedef struct {
 
 typedef struct {
        GSList *text_chunks;
 
 typedef struct {
        GSList *text_chunks;
-       GList *lines;
+        LINE_REC *first_line;
         int lines_count;
 
        LINE_REC *cur_line;
         int lines_count;
 
        LINE_REC *cur_line;
@@ -65,6 +76,9 @@ void textbuffer_line_ref(LINE_REC *line);
 void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line);
 void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list);
 
 void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line);
 void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list);
 
+LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer);
+int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search);
+
 /* Append text to buffer. When \0<EOL> is found at the END OF DATA, a new
    line is created. You must send the EOL command before you can do anything
    else with the buffer. */
 /* Append text to buffer. When \0<EOL> is found at the END OF DATA, a new
    line is created. You must send the EOL command before you can do anything
    else with the buffer. */
@@ -82,6 +96,7 @@ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer);
 void textbuffer_line2text(LINE_REC *line, int coloring, GString *str);
 GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
                            int level, int nolevel, const char *text,
 void textbuffer_line2text(LINE_REC *line, int coloring, GString *str);
 GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
                            int level, int nolevel, const char *text,
+                           int before, int after,
                            int regexp, int fullword, int case_sensitive);
 
 void textbuffer_init(void);
                            int regexp, int fullword, int case_sensitive);
 
 void textbuffer_init(void);
diff --git a/apps/irssi/src/fe-text/tparm.c b/apps/irssi/src/fe-text/tparm.c
new file mode 100644 (file)
index 0000000..3f58e6f
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * 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 wether 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 paramter [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 condtion 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*(paramter%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 defintion 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
+                                          realy 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/apps/irssi/src/fe-text/utf8.c b/apps/irssi/src/fe-text/utf8.c
new file mode 100644 (file)
index 0000000..63a834c
--- /dev/null
@@ -0,0 +1,64 @@
+
+#define UTF8_COMPUTE(Char, Mask, Len)                                        \
+  if (Char < 128)                                                            \
+    {                                                                        \
+      Len = 1;                                                               \
+      Mask = 0x7f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xe0) == 0xc0)                                            \
+    {                                                                        \
+      Len = 2;                                                               \
+      Mask = 0x1f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf0) == 0xe0)                                            \
+    {                                                                        \
+      Len = 3;                                                               \
+      Mask = 0x0f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf8) == 0xf0)                                            \
+    {                                                                        \
+      Len = 4;                                                               \
+      Mask = 0x07;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfc) == 0xf8)                                            \
+    {                                                                        \
+      Len = 5;                                                               \
+      Mask = 0x03;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfe) == 0xfc)                                            \
+    {                                                                        \
+      Len = 6;                                                               \
+      Mask = 0x01;                                                           \
+    }                                                                        \
+  else                                                                       \
+    Len = -1;
+
+#define UTF8_GET(Result, Chars, Count, Mask, Len)                            \
+  (Result) = (Chars)[0] & (Mask);                                            \
+  for ((Count) = 1; (Count) < (Len); ++(Count))                                      \
+    {                                                                        \
+      if (((Chars)[(Count)] & 0xc0) != 0x80)                                 \
+       {                                                                     \
+         (Result) = -1;                                                      \
+         break;                                                              \
+       }                                                                     \
+      (Result) <<= 6;                                                        \
+      (Result) |= ((Chars)[(Count)] & 0x3f);                                 \
+    }
+
+void get_utf8_char(const unsigned char **ptr)
+{
+       int i, mask = 0, len;
+       unsigned int result;
+       unsigned char c = (unsigned char) **ptr;
+
+       UTF8_COMPUTE(c, mask, len);
+       if (len == -1)
+               return;
+
+       UTF8_GET(result, *ptr, i, mask, len);
+       if (result == -1)
+                return;
+
+        *ptr += len-1;
+}
diff --git a/apps/irssi/src/fe-text/utf8.h b/apps/irssi/src/fe-text/utf8.h
new file mode 100644 (file)
index 0000000..3d8f378
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __UTF8_H
+#define __UTF8_H
+
+void get_utf8_char(const unsigned char **ptr);
+
+#endif
diff --git a/apps/irssi/src/lib-config/.cvsignore b/apps/irssi/src/lib-config/.cvsignore
new file mode 100644 (file)
index 0000000..577aa17
--- /dev/null
@@ -0,0 +1,7 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
index 98e2a30b328dfa7cd09175f27bf9277e2203d2fc..124d710129c98d4bb601097a01a4aa0d25c241e2 100644 (file)
@@ -153,7 +153,7 @@ int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int d
        str = config_get_str(rec, section, key, NULL);
        if (str == NULL) return def;
 
        str = config_get_str(rec, section, key, NULL);
        if (str == NULL) return def;
 
-        return toupper(*str) == 'T' || toupper(*str) == 'Y';
+        return i_toupper(*str) == 'T' || i_toupper(*str) == 'Y';
 }
 
 /* Return value of key `value_key' from list item where `key' is `value' */
 }
 
 /* Return value of key `value_key' from list item where `key' is `value' */
@@ -224,8 +224,8 @@ int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def)
        str = config_node_get_str(parent, key, NULL);
        if (str == NULL) return def;
 
        str = config_node_get_str(parent, key, NULL);
        if (str == NULL) return def;
 
-       return toupper(*str) == 'T' || toupper(*str) == 'Y' ||
-               (toupper(*str) == 'O' && toupper(str[1]) == 'N');
+       return i_toupper(*str) == 'T' || i_toupper(*str) == 'Y' ||
+               (i_toupper(*str) == 'O' && i_toupper(str[1]) == 'N');
 }
 
 /* Get the value of keys `key' and `key_value' and put them to
 }
 
 /* Get the value of keys `key' and `key_value' and put them to
@@ -308,3 +308,23 @@ CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index)
 
        return NULL;
 }
 
        return NULL;
 }
+
+/* Returns the first non-comment node in list */
+GSList *config_node_first(GSList *list)
+{
+       while (list != NULL) {
+               CONFIG_NODE *node = list->data;
+
+               if (node->type != NODE_TYPE_COMMENT)
+                        break;
+               list = list->next;
+       }
+       return list;
+}
+
+/* Returns the next non-comment node in list */
+GSList *config_node_next(GSList *list)
+{
+       list = list->next;
+        return config_node_first(list);
+}
index ea2e6c37739eafb77df42862d41883c8d6b15952..fc475f4ec3498c2a328a2a0df7dc0c950e3824b4 100644 (file)
@@ -100,6 +100,11 @@ CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const c
 /* Returns n'th node from list. */
 CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index);
 
 /* Returns n'th node from list. */
 CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index);
 
+/* Returns the first non-comment node in list */
+GSList *config_node_first(GSList *list);
+/* Returns the next non-comment node in list */
+GSList *config_node_next(GSList *list);
+
 /* Setting values */
 int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value);
 int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value);
 /* Setting values */
 int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value);
 int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value);
index 883a920b68443760472583c1574403ad32ac86e1..02eb4255f5ba4c5d6b9a5ef752589c166c1ff36c 100644 (file)
@@ -32,7 +32,7 @@ static unsigned int g_istr_hash(gconstpointer v)
        unsigned int h = 0, g;
 
        while (*s != '\0') {
        unsigned int h = 0, g;
 
        while (*s != '\0') {
-               h = (h << 4) + toupper(*s);
+               h = (h << 4) + i_toupper(*s);
                if ((g = h & 0xf0000000UL)) {
                        h = h ^ (g >> 24);
                        h = h ^ g;
                if ((g = h & 0xf0000000UL)) {
                        h = h ^ (g >> 24);
                        h = h ^ g;
@@ -82,11 +82,7 @@ static void config_parse_get_token(GScanner *scanner, CONFIG_NODE *node)
                } else {
                        if (scanner->token == G_TOKEN_INT) {
                                scanner->token = G_TOKEN_STRING;
                } else {
                        if (scanner->token == G_TOKEN_INT) {
                                scanner->token = G_TOKEN_STRING;
-#undef g_strdup_printf /* This is free'd by GLib itself */
                                scanner->value.v_string = g_strdup_printf("%lu", scanner->value.v_int);
                                scanner->value.v_string = g_strdup_printf("%lu", scanner->value.v_int);
-#ifdef MEM_DEBUG
-#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b)
-#endif
                        }
                        break;
                }
                        }
                        break;
                }
index 446735cdcf8b3d052829de167f14e4b772229e92..194478279c0da77e3d4d8f5b5a86f645c3657d23 100644 (file)
@@ -77,7 +77,7 @@ static int config_has_specials(const char *text)
        g_return_val_if_fail(text != NULL, FALSE);
 
        while (*text != '\0') {
        g_return_val_if_fail(text != NULL, FALSE);
 
        while (*text != '\0') {
-               if (!isalnum((int) *text) && *text != '_')
+               if (!i_isalnum(*text) && *text != '_')
                        return TRUE;
                text++;
        }
                        return TRUE;
                text++;
        }
index c09a0b301caaadc1b33ce72fcedeeb2df43e7664..a23ae8aa78b917f45b37921fff98f26e8da550b7 100644 (file)
@@ -452,7 +452,8 @@ int poptGetNextOpt(poptContext con) {
 
        if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
                     && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) 
 
        if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
                     && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) 
-           con->finalArgv[con->finalArgvCount++] = g_strdup(con->os->nextArg);
+           con->finalArgv[con->finalArgvCount++] =
+               strcpy(malloc(strlen(con->os->nextArg)+1), con->os->nextArg);
     }
 
     if (dup) g_free(dup);
     }
 
     if (dup) g_free(dup);
index d064297c8b284d83c468d3c04627513662053d78..835798a5803529e42121b51659db3744fcbe87ca 100644 (file)
@@ -17,19 +17,19 @@ static void configLine(poptContext con, char * line) {
     
     if (strncmp(line, con->appName, nameLength)) return;
     line += nameLength;
     
     if (strncmp(line, con->appName, nameLength)) return;
     line += nameLength;
-    if (!*line || !isspace(*line)) return;
-    while (*line && isspace(*line)) line++;
+    if (!*line || !i_isspace(*line)) return;
+    while (*line && i_isspace(*line)) line++;
     entryType = line;
 
     entryType = line;
 
-    while (!*line || !isspace(*line)) line++;
+    while (!*line || !i_isspace(*line)) line++;
     *line++ = '\0';
     *line++ = '\0';
-    while (*line && isspace(*line)) line++;
+    while (*line && i_isspace(*line)) line++;
     if (!*line) return;
     opt = line;
 
     if (!*line) return;
     opt = line;
 
-    while (!*line || !isspace(*line)) line++;
+    while (!*line || !i_isspace(*line)) line++;
     *line++ = '\0';
     *line++ = '\0';
-    while (*line && isspace(*line)) line++;
+    while (*line && i_isspace(*line)) line++;
     if (!*line) return;
 
     if (opt[0] == '-' && opt[1] == '-')
     if (!*line) return;
 
     if (opt[0] == '-' && opt[1] == '-')
@@ -92,7 +92,7 @@ int poptReadConfigFile(poptContext con, char * fn) {
          case '\n':
            *dst = '\0';
            dst = buf;
          case '\n':
            *dst = '\0';
            dst = buf;
-           while (*dst && isspace(*dst)) dst++;
+           while (*dst && i_isspace(*dst)) dst++;
            if (*dst && *dst != '#') {
                configLine(con, dst);
            }
            if (*dst && *dst != '#') {
                configLine(con, dst);
            }
index c1876d74ed18369db56b91e33aea6a9bc5a45c7d..243f868e4bafe7024def119f383f86da6fc5d96c 100644 (file)
@@ -94,15 +94,15 @@ static void singleOptionHelp(FILE * f, int maxLeftCol,
     helpLength = strlen(help);
     while (helpLength > lineLength) {
        ch = help + lineLength - 1;
     helpLength = strlen(help);
     while (helpLength > lineLength) {
        ch = help + lineLength - 1;
-       while (ch > help && !isspace(*ch)) ch--;
+       while (ch > help && !i_isspace(*ch)) ch--;
        if (ch == help) break;          /* give up */
        if (ch == help) break;          /* give up */
-       while (ch > (help + 1) && isspace(*ch)) ch--;
+       while (ch > (help + 1) && i_isspace(*ch)) ch--;
        ch++;
 
        sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
        fprintf(f, format, help, " ");
        help = ch;
        ch++;
 
        sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
        fprintf(f, format, help, " ");
        help = ch;
-       while (isspace(*help) && *help) help++;
+       while (i_isspace(*help) && *help) help++;
        helpLength = strlen(help);
     }
 
        helpLength = strlen(help);
     }
 
index eb2a6721633f9d931722fa22239e1e7aeca9736e..135ead56e2ac9d2623b846fed0e8d99875b8b741 100644 (file)
@@ -45,7 +45,7 @@ int poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr) {
                if (*src != quote) *buf++ = '\\';
            }
            *buf++ = *src;
                if (*src != quote) *buf++ = '\\';
            }
            *buf++ = *src;
-       } else if (isspace(*src)) {
+       } else if (isspace((int) (unsigned char) *src)) {
            if (*argv[argc]) {
                buf++, argc++;
                if (argc == argvAlloced) {
            if (*argv[argc]) {
                buf++, argc++;
                if (argc == argvAlloced) {
diff --git a/apps/irssi/src/perl/.cvsignore b/apps/irssi/src/perl/.cvsignore
new file mode 100644 (file)
index 0000000..189d2fd
--- /dev/null
@@ -0,0 +1,10 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+perl-signals-list.h
+irssi-core.pl.h
diff --git a/apps/irssi/src/perl/Makefile.am b/apps/irssi/src/perl/Makefile.am
new file mode 100644 (file)
index 0000000..965605a
--- /dev/null
@@ -0,0 +1,145 @@
+LIBTOOL = $(PERL_LIBTOOL)
+
+include $(top_srcdir)/Makefile.defines.in
+
+moduledir = $(silc_modulesdir)
+
+module_LTLIBRARIES = $(perl_module_lib) $(perl_module_fe_lib)
+noinst_LTLIBRARIES = $(perl_static_lib) $(perl_static_fe_lib)
+EXTRA_LTLIBRARIES = \
+       libperl_core.la libfe_perl.la \
+       libperl_core_static.la libfe_perl_static.la
+
+libperl_core_la_LDFLAGS = -avoid-version -rpath $(moduledir)
+libfe_perl_la_LDFLAGS = -avoid-version -rpath $(moduledir)
+
+perl-core.c: perl-signals-list.h irssi-core.pl.h
+
+INCLUDES = \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/core \
+       -I$(top_srcdir)/src/fe-common/core \
+       -I$(top_srcdir)/src/fe-common/silc \
+       $(GLIB_CFLAGS) \
+       -DSCRIPTDIR=\""$(libdir)/irssi/scripts"\" \
+       -DPERL_USE_LIB=\""$(PERL_USE_LIB)"\" \
+       -DPERL_STATIC_LIBS=$(PERL_STATIC_LIBS) \
+       $(PERL_CFLAGS)
+
+perl_sources = \
+       perl-core.c \
+       perl-common.c \
+       perl-signals.c \
+       perl-sources.c
+
+perl_fe_sources = \
+       module-formats.c \
+       perl-fe.c
+
+noinst_HEADERS = \
+       module.h \
+       module-fe.h \
+       module-formats.h \
+       perl-core.h \
+       perl-common.h \
+       perl-signals.h \
+       perl-sources.h
+
+libperl_core_la_DEPENDENCIES = .libs/libperl_orig.a .libs/DynaLoader.a
+
+.libs/libperl_orig.a:
+       if [ ! -d .libs ]; then mkdir .libs; fi
+       rm -f .libs/libperl_orig.a
+       if [ x$(LIBPERL_A) = x ]; then touch .libs/libperl_orig.a; else $(LN_S) $(LIBPERL_A) .libs/libperl_orig.a; fi
+.libs/DynaLoader.a:
+       if [ ! -d .libs ]; then mkdir .libs; fi
+       rm -f .libs/DynaLoader.a
+       $(LN_S) $(DYNALOADER_A) .libs/DynaLoader.a
+
+libperl_core_la_SOURCES = \
+       $(perl_sources)
+
+libperl_core_static_la_SOURCES = \
+       $(perl_sources)
+
+libfe_perl_la_SOURCES = \
+       $(perl_fe_sources)
+
+libfe_perl_static_la_SOURCES = \
+       $(perl_fe_sources)
+
+perl-signals-list.h: $(top_srcdir)/docs/signals.txt $(srcdir)/get-signals.pl
+       cat $(top_srcdir)/docs/signals.txt | $(perlpath) $(srcdir)/get-signals.pl > perl-signals-list.h
+
+irssi-core.pl.h: irssi-core.pl
+       $(top_srcdir)/file2header.sh $(srcdir)/irssi-core.pl irssi_core_code > irssi-core.pl.h
+
+common_sources = \
+       common/Irssi.xs \
+       common/Irssi.pm \
+       common/Channel.xs \
+       common/Core.xs \
+       common/Ignore.xs \
+       common/Log.xs \
+       common/Masks.xs \
+       common/Query.xs \
+       common/Rawlog.xs \
+       common/Server.xs \
+       common/Settings.xs \
+       common/Makefile.PL.in \
+       common/typemap \
+       common/module.h
+
+ui_sources = \
+       ui/UI.xs \
+       ui/UI.pm \
+       ui/Formats.xs \
+       ui/Themes.xs \
+       ui/Window.xs \
+       ui/Makefile.PL.in \
+       ui/typemap \
+       ui/module.h
+
+textui_sources = \
+       textui/TextUI.xs \
+       textui/TextUI.pm \
+       textui/TextBuffer.xs \
+       textui/TextBufferView.xs \
+       textui/Statusbar.xs \
+       textui/Makefile.PL.in \
+       textui/typemap \
+       textui/module.h
+
+EXTRA_DIST = \
+       libperl_dynaloader.la \
+       libperl_orig.la \
+       get-signals.pl \
+       irssi-core.pl \
+       $(common_sources) \
+       $(ui_sources) \
+       $(textui_sources)
+
+all-local:
+       for dir in common ui textui; do \
+         cd $$dir && \
+         if [ ! -f Makefile ]; then \
+            $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \
+         fi && \
+         ($(MAKE) || $(MAKE)) && \
+         cd ..; \
+       done
+
+install-exec-local:
+       for dir in common ui textui; do \
+         cd $$dir && $(MAKE) install && cd ..; \
+       done
+
+clean-generic:
+       rm -f common/Makefile ui/Makefile textui/Makefile
+
+distclean-generic:
+       -(cd common && $(MAKE) realclean && rm -f Makefile.PL)
+       -(cd ui && $(MAKE) realclean && rm -f Makefile.PL)
+       -(cd textui && $(MAKE) realclean && rm -f Makefile.PL)
+
+libperl_core_la_LIBADD = $(PERL_LDFLAGS)
diff --git a/apps/irssi/src/perl/common/.cvsignore b/apps/irssi/src/perl/common/.cvsignore
new file mode 100644 (file)
index 0000000..e965849
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.PL
+Irssi.bs
+*.c
+*.o
+pm_to_blib
+blib
diff --git a/apps/irssi/src/perl/common/Channel.xs b/apps/irssi/src/perl/common/Channel.xs
new file mode 100644 (file)
index 0000000..f425dc2
--- /dev/null
@@ -0,0 +1,124 @@
+#include "module.h"
+
+MODULE = Irssi::Channel  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+channels()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = channels; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data)));
+       }
+
+Irssi::Channel
+channel_find(channel)
+       char *channel
+CODE:
+       RETVAL = channel_find(NULL, channel);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Channel  PACKAGE = Irssi::Server
+#*******************************
+
+void
+channels(server)
+       Irssi::Server server
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data)));
+       }
+
+void
+channels_join(server, channels, automatic)
+       Irssi::Server server
+       char *channels
+       int automatic
+CODE:
+       server->channels_join(server, channels, automatic);
+
+Irssi::Channel
+channel_create(server, name, automatic)
+       Irssi::Server server
+       char *name
+       int automatic
+CODE:
+       CHAT_PROTOCOL(server)->channel_create(server, name, automatic);
+
+Irssi::Channel
+channel_find(server, name)
+       Irssi::Server server
+       char *name
+
+void
+nicks_get_same(server, nick)
+       Irssi::Server server
+        char *nick
+PREINIT:
+       GSList *list, *tmp;
+PPCODE:
+       list = nicklist_get_same(server, nick);
+
+       for (tmp = list; tmp != NULL; tmp = tmp->next->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data)));
+               XPUSHs(sv_2mortal(iobject_bless((NICK_REC *) tmp->next->data)));
+       }
+       g_slist_free(list);
+
+#*******************************
+MODULE = Irssi::Channel  PACKAGE = Irssi::Channel  PREFIX = channel_
+#*******************************
+
+void
+channel_destroy(channel)
+       Irssi::Channel channel
+
+void
+nick_insert(channel, nick)
+       Irssi::Channel channel
+       Irssi::Nick nick
+CODE:
+       nicklist_insert(channel, nick);
+
+void
+nick_remove(channel, nick)
+       Irssi::Channel channel
+       Irssi::Nick nick
+CODE:
+       nicklist_remove(channel, nick);
+
+Irssi::Nick
+nick_find(channel, nick)
+       Irssi::Channel channel
+       char *nick
+CODE:
+       RETVAL = nicklist_find(channel, nick);
+OUTPUT:
+       RETVAL
+
+Irssi::Nick
+nick_find_mask(channel, mask)
+       Irssi::Channel channel
+       char *mask
+CODE:
+       RETVAL = nicklist_find_mask(channel, mask);
+OUTPUT:
+       RETVAL
+
+void
+nicks(channel)
+       Irssi::Channel channel
+PREINIT:
+       GSList *list, *tmp;
+PPCODE:
+       list = nicklist_getnicks(channel);
+
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((NICK_REC *) tmp->data)));
+       }
+       g_slist_free(list);
diff --git a/apps/irssi/src/perl/common/Core.xs b/apps/irssi/src/perl/common/Core.xs
new file mode 100644 (file)
index 0000000..4db742c
--- /dev/null
@@ -0,0 +1,528 @@
+#include "module.h"
+#include "irssi-version.h"
+
+#define DEFAULT_COMMAND_CATEGORY "Perl scripts' commands"
+
+void perl_signal_add_hash(int priority, SV *sv)
+{
+       HV *hv;
+        HE *he;
+       I32 len;
+
+       if (!is_hvref(sv))
+               croak("Usage: Irssi::signal_add(hash)");
+
+        hv = hvref(sv);
+       hv_iterinit(hv);
+       while ((he = hv_iternext(hv)) != NULL)
+                perl_signal_add_to(hv_iterkey(he, &len), HeVAL(he), priority);
+}
+
+static void perl_command_bind_add_hash(int priority, SV *sv, char *category)
+{
+       HV *hv;
+        HE *he;
+       I32 len;
+
+        hv = hvref(sv);
+       hv_iterinit(hv);
+       while ((he = hv_iternext(hv)) != NULL)
+               perl_command_bind_to(hv_iterkey(he, &len), category, HeVAL(he), priority);
+}
+
+static void handle_command_bind(int priority, int items, SV *p0, SV *p1, SV *p2)
+{
+       char *category;
+       int hash;
+
+       hash = items > 0 && is_hvref(p0);
+       if (!hash) {
+               if (items < 2 || items > 3)
+                       croak("Usage: Irssi::command_bind(signal, func, category)");
+       } else if (items > 2)
+               croak("Usage: Irssi::command_bind(signals_hash, category)");
+
+       if (!hash) {
+               category = items < 3 ? DEFAULT_COMMAND_CATEGORY :
+                       (char *)SvPV(p2, PL_na);
+               perl_command_bind_to((char *)SvPV(p0, PL_na), category, p1, priority);
+       } else {
+               category = items < 2 ? DEFAULT_COMMAND_CATEGORY :
+                       (char *)SvPV(p1, PL_na);
+               perl_command_bind_add_hash(priority, p0, category);
+       }
+}
+
+MODULE = Irssi::Core  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+signal_emit(signal, ...)
+       char *signal
+CODE:
+       void *p[SIGNAL_MAX_ARGUMENTS];
+       int n;
+
+       memset(p, 0, sizeof(p));
+       for (n = 1; n < items && n < SIGNAL_MAX_ARGUMENTS+1; n++) {
+               if (SvPOKp(ST(n)))
+                       p[n-1] = SvPV(ST(n), PL_na);
+               else if (irssi_is_ref_object(ST(n)))
+                       p[n-1] = irssi_ref_object(ST(n));
+               else if (SvROK(ST(n)))
+                       p[n-1] = (void *) SvIV((SV*)SvRV(ST(n)));
+               else
+                       p[n-1] = NULL;
+       }
+       signal_emit(signal, items-1, p[0], p[1], p[2], p[3], p[4], p[5]);
+
+void
+signal_add(...)
+CODE:
+       if (items != 1 && items != 2)
+               croak("Usage: Irssi::signal_add(signal, func)");
+       if (items == 2)
+               perl_signal_add((char *)SvPV(ST(0),PL_na), ST(1));
+       else
+               perl_signal_add_hash(1, ST(0));
+
+void
+signal_add_first(...)
+CODE:
+       if (items != 1 && items != 2)
+               croak("Usage: Irssi::signal_add_first(signal, func)");
+       if (items == 2)
+               perl_signal_add_first((char *)SvPV(ST(0),PL_na), ST(1));
+       else
+               perl_signal_add_hash(0, ST(0));
+
+void
+signal_add_last(...)
+CODE:
+       if (items != 1 && items != 2)
+               croak("Usage: Irssi::signal_add_last(signal, func)");
+       if (items == 2)
+               perl_signal_add_last((char *)SvPV(ST(0),PL_na), ST(1));
+       else
+               perl_signal_add_hash(2, ST(0));
+
+void
+signal_remove(signal, func)
+       char *signal
+       SV *func
+CODE:
+       perl_signal_remove(signal, func);
+
+void
+signal_stop()
+
+void
+signal_stop_by_name(signal)
+       char *signal
+
+char *
+signal_get_emitted()
+CODE:
+       RETVAL = (char *) signal_get_emitted();
+OUTPUT:
+       RETVAL
+
+int
+signal_get_emitted_id()
+
+int
+timeout_add(msecs, func, data)
+       int msecs
+       SV *func
+       SV *data
+CODE:
+       RETVAL = perl_timeout_add(msecs, func, data);
+OUTPUT:
+       RETVAL
+
+void
+timeout_remove(tag)
+       int tag
+CODE:
+       perl_source_remove(tag);
+
+
+int
+INPUT_READ()
+CODE:
+       RETVAL = G_INPUT_READ;
+OUTPUT:
+       RETVAL
+
+int
+INPUT_WRITE()
+CODE:
+       RETVAL = G_INPUT_WRITE;
+OUTPUT:
+       RETVAL
+
+int
+input_add(source, condition, func, data)
+       int source
+       int condition
+       SV *func
+       SV *data
+CODE:
+       RETVAL = perl_input_add(source, condition, func, data);
+OUTPUT:
+       RETVAL
+
+void
+input_remove(tag)
+       int tag
+CODE:
+       perl_source_remove(tag);
+
+# maybe there's some easier way than this..? :)
+int
+MSGLEVEL_CRAP()
+CODE:
+       RETVAL = MSGLEVEL_CRAP;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_MSGS()
+CODE:
+       RETVAL = MSGLEVEL_MSGS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_PUBLIC()
+CODE:
+       RETVAL = MSGLEVEL_PUBLIC;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NOTICES()
+CODE:
+       RETVAL = MSGLEVEL_NOTICES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_SNOTES()
+CODE:
+       RETVAL = MSGLEVEL_SNOTES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CTCPS()
+CODE:
+       RETVAL = MSGLEVEL_CTCPS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_ACTIONS()
+CODE:
+       RETVAL = MSGLEVEL_ACTIONS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_JOINS()
+CODE:
+       RETVAL = MSGLEVEL_JOINS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_PARTS()
+CODE:
+       RETVAL = MSGLEVEL_PARTS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_QUITS()
+CODE:
+       RETVAL = MSGLEVEL_QUITS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_KICKS()
+CODE:
+       RETVAL = MSGLEVEL_KICKS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_MODES()
+CODE:
+       RETVAL = MSGLEVEL_MODES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_TOPICS()
+CODE:
+       RETVAL = MSGLEVEL_TOPICS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_WALLOPS()
+CODE:
+       RETVAL = MSGLEVEL_WALLOPS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_INVITES()
+CODE:
+       RETVAL = MSGLEVEL_INVITES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NICKS()
+CODE:
+       RETVAL = MSGLEVEL_NICKS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_DCC()
+CODE:
+       RETVAL = MSGLEVEL_DCC;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_DCCMSGS()
+CODE:
+       RETVAL = MSGLEVEL_DCCMSGS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CLIENTNOTICE()
+CODE:
+       RETVAL = MSGLEVEL_CLIENTNOTICE;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CLIENTCRAP()
+CODE:
+       RETVAL = MSGLEVEL_CLIENTCRAP;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CLIENTERROR()
+CODE:
+       RETVAL = MSGLEVEL_CLIENTERROR;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_HILIGHT()
+CODE:
+       RETVAL = MSGLEVEL_HILIGHT;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_ALL()
+CODE:
+       RETVAL = MSGLEVEL_ALL;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NOHILIGHT()
+CODE:
+       RETVAL = MSGLEVEL_NOHILIGHT;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NO_ACT()
+CODE:
+       RETVAL = MSGLEVEL_NO_ACT;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NEVER()
+CODE:
+       RETVAL = MSGLEVEL_NEVER;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_LASTLOG()
+CODE:
+       RETVAL = MSGLEVEL_LASTLOG;
+OUTPUT:
+       RETVAL
+
+int
+level2bits(str)
+       char *str
+
+char *
+bits2level(bits)
+       int bits
+
+int
+combine_level(level, str)
+       int level
+       char *str
+
+void
+command(cmd)
+       char *cmd
+CODE:
+       perl_command(cmd, NULL, NULL);
+
+void
+commands()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Command")));
+       }
+
+void
+command_bind_first(...)
+CODE:
+       handle_command_bind(0, items, ST(0), ST(1), ST(2));
+
+void
+command_bind(...)
+CODE:
+       handle_command_bind(1, items, ST(0), ST(1), ST(2));
+
+void
+command_bind_last(...)
+CODE:
+       handle_command_bind(2, items, ST(0), ST(1), ST(2));
+
+void
+command_runsub(cmd, data, server, item)
+       char *cmd
+       char *data
+       Irssi::Server server
+       Irssi::Windowitem item
+CODE:
+       perl_command_runsub(cmd, data, server, item);
+
+void
+command_unbind(cmd, func)
+       char *cmd
+       SV *func
+CODE:
+       perl_command_unbind(cmd, func);
+
+void
+command_set_options(cmd, options)
+       char *cmd
+       char *options
+
+void
+pidwait_add(pid)
+       int pid
+
+void 
+pidwait_remove(pid)
+       int pid
+
+char *
+parse_special(cmd, data="", flags=0)
+       char *cmd
+       char *data
+       int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       ret = parse_special_string(cmd, NULL, NULL, data, NULL, flags);
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
+
+char *
+get_irssi_dir()
+CODE:
+       RETVAL = (char *) get_irssi_dir();
+OUTPUT:
+       RETVAL
+
+char *
+get_irssi_config()
+CODE:
+       RETVAL = (char *) get_irssi_config();
+OUTPUT:
+       RETVAL
+
+char *
+version()
+PREINIT:
+       char version[100];
+CODE:
+       g_snprintf(version, sizeof(version), "%d.%04d",
+                  IRSSI_VERSION_DATE, IRSSI_VERSION_TIME);
+       RETVAL = version;
+OUTPUT:
+        RETVAL
+
+#*******************************
+MODULE = Irssi::Core   PACKAGE = Irssi::Server
+#*******************************
+
+void
+parse_special(server, cmd, data="", flags=0)
+       Irssi::Server server
+       char *cmd
+       char *data
+       int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       ret = parse_special_string(cmd, server, NULL, data, NULL, flags);
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
+
+void
+command(server, cmd)
+       Irssi::Server server
+       char *cmd
+CODE:
+       perl_command(cmd, server, NULL);
+
+
+#*******************************
+MODULE = Irssi::Core   PACKAGE = Irssi::Windowitem
+#*******************************
+
+void
+parse_special(item, cmd, data="", flags=0)
+       Irssi::Windowitem item
+       char *cmd
+       char *data
+       int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       ret = parse_special_string(cmd, item->server, item, data, NULL, flags);
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
+
+void
+command(item, cmd)
+       Irssi::Windowitem item
+       char *cmd
+CODE:
+       perl_command(cmd, item->server, item);
+
diff --git a/apps/irssi/src/perl/common/Ignore.xs b/apps/irssi/src/perl/common/Ignore.xs
new file mode 100644 (file)
index 0000000..e4452c7
--- /dev/null
@@ -0,0 +1,50 @@
+#include "module.h"
+
+MODULE = Irssi::Ignore  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+ignores()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Ignore")));
+       }
+
+int
+ignore_check(nick, host, channel, text, level)
+       char *nick
+       char *host
+       char *channel
+       char *text
+       int level
+CODE:
+       RETVAL = ignore_check(NULL, nick, host, channel, text, level);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Ignore  PACKAGE = Irssi::Server
+#*******************************
+
+int
+ignore_check(server, nick, host, channel, text, level)
+       Irssi::Server server
+       char *nick
+       char *host
+       char *channel
+       char *text
+       int level
+
+#*******************************
+MODULE = Irssi::Ignore  PACKAGE = Irssi::Ignore  PREFIX = ignore_
+#*******************************
+
+void
+ignore_add_rec(rec)
+       Irssi::Ignore rec
+
+void
+ignore_update_rec(rec)
+       Irssi::Ignore rec
diff --git a/apps/irssi/src/perl/common/Irssi.bs b/apps/irssi/src/perl/common/Irssi.bs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/irssi/src/perl/common/Irssi.pm b/apps/irssi/src/perl/common/Irssi.pm
new file mode 100644 (file)
index 0000000..ce35c52
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Perl interface to irssi functions.
+#
+
+package Irssi;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+sub VERSION {
+  my $version = $_[1];
+  die "This script requires irssi version $version or later"
+    if ($version > version());
+}
+
+sub EXPORT_ALL () {
+  no strict 'refs';
+  @EXPORT_OK = grep { /[a-z]/ && defined *{$_}{CODE} } keys %Irssi::;
+}
+
+$VERSION = "0.9";
+
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw(INPUT_READ INPUT_WRITE
+       MSGLEVEL_CRAP MSGLEVEL_MSGS MSGLEVEL_PUBLIC MSGLEVEL_NOTICES
+       MSGLEVEL_SNOTES MSGLEVEL_CTCPS MSGLEVEL_ACTIONS MSGLEVEL_JOINS
+       MSGLEVEL_PARTS MSGLEVEL_QUITS MSGLEVEL_KICKS MSGLEVEL_MODES
+       MSGLEVEL_TOPICS MSGLEVEL_WALLOPS MSGLEVEL_INVITES MSGLEVEL_NICKS
+       MSGLEVEL_DCC MSGLEVEL_DCCMSGS MSGLEVEL_CLIENTNOTICE MSGLEVEL_CLIENTCRAP
+       MSGLEVEL_CLIENTERROR MSGLEVEL_HILIGHT MSGLEVEL_ALL MSGLEVEL_NOHILIGHT
+       MSGLEVEL_NO_ACT MSGLEVEL_NEVER MSGLEVEL_LASTLOG
+);
+@EXPORT_OK = qw();
+
+bootstrap Irssi $VERSION if (!Irssi::Core::is_static());
+
+@Irssi::Channel::ISA = qw(Irssi::Windowitem);
+@Irssi::Query::ISA = qw(Irssi::Windowitem);
+
+Irssi::init();
+
+Irssi::EXPORT_ALL();
+
+1;
+
diff --git a/apps/irssi/src/perl/common/Irssi.xs b/apps/irssi/src/perl/common/Irssi.xs
new file mode 100644 (file)
index 0000000..67d5e96
--- /dev/null
@@ -0,0 +1,33 @@
+#include "module.h"
+
+static int initialized = FALSE;
+
+MODULE = Irssi  PACKAGE = Irssi
+
+PROTOTYPES: ENABLE
+
+void
+init()
+CODE:
+       if (initialized) return;
+       perl_api_version_check("Irssi");
+       initialized = TRUE;
+
+        perl_settings_init();
+
+void
+deinit()
+CODE:
+       if (!initialized) return;
+        perl_settings_deinit();
+
+BOOT:
+        irssi_boot(Channel);
+       irssi_boot(Core);
+       irssi_boot(Ignore);
+       irssi_boot(Log);
+       irssi_boot(Masks);
+       irssi_boot(Query);
+       irssi_boot(Rawlog);
+       irssi_boot(Server);
+       irssi_boot(Settings);
diff --git a/apps/irssi/src/perl/common/Log.xs b/apps/irssi/src/perl/common/Log.xs
new file mode 100644 (file)
index 0000000..c87ee45
--- /dev/null
@@ -0,0 +1,67 @@
+#include "module.h"
+
+MODULE = Irssi::Log  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+logs()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = logs; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Log")));
+       }
+
+Irssi::Log
+log_create_rec(fname, level)
+       char *fname
+       int level
+
+Irssi::Log
+log_find(fname)
+       char *fname
+
+#*******************************
+MODULE = Irssi::Log  PACKAGE = Irssi::Log  PREFIX = log_
+#*******************************
+
+void
+log_item_add(log, type, name, servertag)
+       Irssi::Log log
+       int type
+       char *name
+       char *servertag
+
+void
+log_item_destroy(log, item)
+       Irssi::Log log
+       Irssi::Logitem item
+
+Irssi::Logitem
+log_item_find(log, type, item, servertag)
+       Irssi::Log log
+       int type
+       char *item
+       char *servertag
+
+void
+log_update(log)
+       Irssi::Log log
+
+void
+log_close(log)
+       Irssi::Log log
+
+void
+log_write_rec(log, str, level)
+       Irssi::Log log
+       char *str
+       int level
+
+void
+log_start_logging(log)
+       Irssi::Log log
+
+void
+log_stop_logging(log)
+       Irssi::Log log
diff --git a/apps/irssi/src/perl/common/Makefile.PL.in b/apps/irssi/src/perl/common/Makefile.PL.in
new file mode 100644 (file)
index 0000000..4e545e7
--- /dev/null
@@ -0,0 +1,7 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile('NAME' => 'Irssi',
+              'LIBS' => '',
+             'OBJECT' => '$(O_FILES)',
+              'INC' => '-I../../.. -I@top_srcdir@ -I@top_srcdir@/src -I@top_srcdir@/src/core @GLIB_CFLAGS@',
+             'VERSION_FROM' => '@srcdir@/Irssi.pm');
diff --git a/apps/irssi/src/perl/common/Masks.xs b/apps/irssi/src/perl/common/Masks.xs
new file mode 100644 (file)
index 0000000..1ea1396
--- /dev/null
@@ -0,0 +1,61 @@
+#include "module.h"
+
+MODULE = Irssi::Masks  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+int
+mask_match(mask, nick, user, host)
+       char *mask
+       char *nick
+       char *user
+       char *host
+CODE:
+       RETVAL = mask_match(NULL, mask, nick, user, host);
+OUTPUT:
+       RETVAL
+
+int
+mask_match_address(mask, nick, address)
+       char *mask
+       char *nick
+       char *address
+CODE:
+       RETVAL = mask_match_address(NULL, mask, nick, address);
+OUTPUT:
+       RETVAL
+
+int
+masks_match(masks, nick, address)
+       char *masks
+       char *nick
+       char *address
+CODE:
+       RETVAL = masks_match(NULL, masks, nick, address);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Masks  PACKAGE = Irssi::Server
+#*******************************
+
+int
+mask_match(server, mask, nick, user, host)
+       Irssi::Server server
+       char *mask
+       char *nick
+       char *user
+       char *host
+
+int
+mask_match_address(server, mask, nick, address)
+       Irssi::Server server
+       char *mask
+       char *nick
+       char *address
+
+int
+masks_match(server, masks, nick, address)
+       Irssi::Server server
+       char *masks
+       char *nick
+       char *address
diff --git a/apps/irssi/src/perl/common/Query.xs b/apps/irssi/src/perl/common/Query.xs
new file mode 100644 (file)
index 0000000..54a0582
--- /dev/null
@@ -0,0 +1,57 @@
+#include "module.h"
+
+MODULE = Irssi::Query  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+queries()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               XPUSHs(sv_2mortal(iobject_bless(rec)));
+       }
+
+Irssi::Query
+query_find(nick)
+       char *nick
+CODE:
+       RETVAL = query_find(NULL, nick);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Query  PACKAGE = Irssi::Server
+#*******************************
+
+void
+queries(server)
+       Irssi::Server server
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               XPUSHs(sv_2mortal(iobject_bless(rec)));
+       }
+
+Irssi::Query
+query_find(server, nick)
+       Irssi::Server server
+       char *nick
+
+#*******************************
+MODULE = Irssi::Query  PACKAGE = Irssi::Query  PREFIX = query_
+#*******************************
+
+void
+query_destroy(query)
+       Irssi::Query query
+
+void
+query_change_server(query, server)
+       Irssi::Query query
+       Irssi::Server server
diff --git a/apps/irssi/src/perl/common/Rawlog.xs b/apps/irssi/src/perl/common/Rawlog.xs
new file mode 100644 (file)
index 0000000..dd95ce5
--- /dev/null
@@ -0,0 +1,58 @@
+#include "module.h"
+
+MODULE = Irssi::Rawlog  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+rawlog_set_size(lines)
+       int lines
+
+Irssi::Rawlog
+rawlog_create()
+
+#*******************************
+MODULE = Irssi::Rawlog  PACKAGE = Irssi::Rawlog  PREFIX = rawlog_
+#*******************************
+
+void
+rawlog_get_lines(rawlog)
+       Irssi::Rawlog rawlog
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = rawlog->lines; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(new_pv(tmp->data)));
+       }
+
+void
+rawlog_destroy(rawlog)
+       Irssi::Rawlog rawlog
+
+void
+rawlog_input(rawlog, str)
+       Irssi::Rawlog rawlog
+       char *str
+
+void
+rawlog_output(rawlog, str)
+       Irssi::Rawlog rawlog
+       char *str
+
+void
+rawlog_redirect(rawlog, str)
+       Irssi::Rawlog rawlog
+       char *str
+
+void
+rawlog_open(rawlog, fname)
+       Irssi::Rawlog rawlog
+       char *fname
+
+void
+rawlog_close(rawlog)
+       Irssi::Rawlog rawlog
+
+void
+rawlog_save(rawlog, fname)
+       Irssi::Rawlog rawlog
+       char *fname
diff --git a/apps/irssi/src/perl/common/Server.xs b/apps/irssi/src/perl/common/Server.xs
new file mode 100644 (file)
index 0000000..adcabb1
--- /dev/null
@@ -0,0 +1,104 @@
+#include "module.h"
+
+MODULE = Irssi::Server  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+servers()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((SERVER_REC *) tmp->data)));
+       }
+
+void
+reconnects()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Reconnect")));
+       }
+
+void
+chatnets()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = chatnets; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHATNET_REC *) tmp->data)));
+       }
+
+Irssi::Connect
+server_create_conn(chat_type, dest, port=6667, chatnet=NULL, password=NULL, nick=NULL)
+       int chat_type
+       char *dest
+       int port
+       char *chatnet
+       char *password
+       char *nick
+
+Irssi::Server
+server_find_tag(tag)
+       char *tag
+
+Irssi::Server
+server_find_chatnet(chatnet)
+       char *chatnet
+
+Irssi::Chatnet
+chatnet_find(name)
+       char *name
+
+#*******************************
+MODULE = Irssi::Server PACKAGE = Irssi::Server  PREFIX = server_
+#*******************************
+
+void
+server_disconnect(server)
+       Irssi::Server server
+
+void
+server_ref(server)
+       Irssi::Server server
+
+void
+server_unref(server)
+       Irssi::Server server
+
+int
+isnickflag(server, flag)
+       Irssi::Server server
+       char flag
+CODE:
+       RETVAL = server->isnickflag(flag);
+OUTPUT:
+       RETVAL
+
+int
+ischannel(server, data)
+       Irssi::Server server
+       char *data
+CODE:
+       RETVAL = server->ischannel(server, data);
+OUTPUT:
+       RETVAL
+
+char *
+get_nick_flags(server)
+       Irssi::Server server
+CODE:
+       RETVAL = (char *) server->get_nick_flags();
+OUTPUT:
+       RETVAL
+
+void
+send_message(server, target, msg, target_type)
+       Irssi::Server server
+       char *target
+       char *msg
+       int target_type
+CODE:
+       server->send_message(server, target, msg, target_type);
+
diff --git a/apps/irssi/src/perl/common/Settings.xs b/apps/irssi/src/perl/common/Settings.xs
new file mode 100644 (file)
index 0000000..a7c5e6f
--- /dev/null
@@ -0,0 +1,134 @@
+#include "module.h"
+#include "misc.h"
+
+static GHashTable *perl_settings;
+
+static void perl_settings_add(const char *key)
+{
+       PERL_SCRIPT_REC *script;
+        GSList *list;
+
+       script = perl_script_find_package(perl_get_package());
+       g_return_if_fail(script != NULL);
+
+       list = g_hash_table_lookup(perl_settings, script);
+        list = g_slist_append(list, g_strdup(key));
+       g_hash_table_insert(perl_settings, script, list);
+}
+
+static void perl_settings_remove(const char *key)
+{
+       PERL_SCRIPT_REC *script;
+        GSList *list, *pos;
+
+       script = perl_script_find_package(perl_get_package());
+       g_return_if_fail(script != NULL);
+
+       list = g_hash_table_lookup(perl_settings, script);
+       pos = gslist_find_icase_string(list, key);
+       if (pos != NULL) {
+               list = g_slist_remove(list, pos->data);
+               g_hash_table_insert(perl_settings, script, list);
+       }
+}
+
+static void perl_settings_free(PERL_SCRIPT_REC *script, GSList *list)
+{
+       g_slist_foreach(list, (GFunc) g_free, NULL);
+        g_slist_free(list);
+}
+
+static void sig_script_destroyed(PERL_SCRIPT_REC *script)
+{
+       GSList *list;
+
+       list = g_hash_table_lookup(perl_settings, script);
+       if (list != NULL) {
+                g_slist_foreach(list, (GFunc) settings_remove, NULL);
+               perl_settings_free(script, list);
+               g_hash_table_remove(perl_settings, script);
+       }
+}
+
+void perl_settings_init(void)
+{
+       perl_settings = g_hash_table_new((GHashFunc) g_direct_hash,
+                                        (GCompareFunc) g_direct_equal);
+        signal_add("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+}
+
+void perl_settings_deinit(void)
+{
+        signal_remove("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+
+       g_hash_table_foreach(perl_settings, (GHFunc) perl_settings_free, NULL);
+       g_hash_table_destroy(perl_settings);
+}
+
+MODULE = Irssi::Settings  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+char *
+settings_get_str(key)
+       char *key
+CODE:
+       RETVAL = (char *) settings_get_str(key);
+OUTPUT:
+       RETVAL
+
+int
+settings_get_int(key)
+       char *key
+
+int
+settings_get_bool(key)
+       char *key
+
+void
+settings_set_str(key, value)
+       char *key
+       char *value
+
+void
+settings_set_int(key, value)
+       char *key
+       int value
+
+void
+settings_set_bool(key, value)
+       char *key
+       int value
+
+void
+settings_add_str(section, key, def)
+       char *section
+       char *key
+       char *def
+CODE:
+        perl_settings_add(key);
+       settings_add_str_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_add_int(section, key, def)
+       char *section
+       char *key
+       int def
+CODE:
+        perl_settings_add(key);
+       settings_add_int_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_add_bool(section, key, def)
+       char *section
+       char *key
+       int def
+CODE:
+        perl_settings_add(key);
+       settings_add_bool_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_remove(key)
+       char *key
+CODE:
+        perl_settings_remove(key);
+       settings_remove(key);
diff --git a/apps/irssi/src/perl/common/module.h b/apps/irssi/src/perl/common/module.h
new file mode 100644 (file)
index 0000000..5456f63
--- /dev/null
@@ -0,0 +1,44 @@
+#define NEED_PERL_H
+#define HAVE_CONFIG_H
+#include "../module.h"
+#include <XSUB.h>
+
+#include "network.h"
+#include "levels.h"
+#include "commands.h"
+#include "log.h"
+#include "rawlog.h"
+#include "ignore.h"
+#include "settings.h"
+#include "masks.h"
+#include "special-vars.h"
+#include "window-item-def.h"
+
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+#include "servers-reconnect.h"
+#include "servers-setup.h"
+#include "channels.h"
+#include "queries.h"
+#include "nicklist.h"
+
+#include "perl/perl-core.h"
+#include "perl/perl-common.h"
+#include "perl/perl-signals.h"
+
+typedef COMMAND_REC *Irssi__Command;
+typedef LOG_REC *Irssi__Log;
+typedef LOG_ITEM_REC *Irssi__Logitem;
+typedef RAWLOG_REC *Irssi__Rawlog;
+typedef IGNORE_REC *Irssi__Ignore;
+typedef MODULE_REC *Irssi__Module;
+typedef WI_ITEM_REC *Irssi__Windowitem;
+
+typedef CHATNET_REC *Irssi__Chatnet;
+typedef SERVER_REC *Irssi__Server;
+typedef SERVER_CONNECT_REC *Irssi__Connect;
+typedef RECONNECT_REC *Irssi__Reconnect;
+typedef CHANNEL_REC *Irssi__Channel;
+typedef QUERY_REC *Irssi__Query;
+typedef NICK_REC *Irssi__Nick;
diff --git a/apps/irssi/src/perl/common/typemap b/apps/irssi/src/perl/common/typemap
new file mode 100644 (file)
index 0000000..5b7e0c3
--- /dev/null
@@ -0,0 +1,32 @@
+TYPEMAP
+Irssi::Chatnet         T_IrssiObj
+Irssi::Server          T_IrssiObj
+Irssi::Connect         T_IrssiObj
+Irssi::Reconnect       T_PlainObj
+Irssi::Channel         T_IrssiObj
+Irssi::Query           T_IrssiObj
+Irssi::Command         T_PlainObj
+Irssi::Nick            T_IrssiObj
+Irssi::Ignore          T_PlainObj
+Irssi::Log             T_PlainObj
+Irssi::Logitem         T_PlainObj
+Irssi::Rawlog          T_PlainObj
+Irssi::Module          T_PlainObj
+Irssi::Windowitem      T_IrssiObj
+
+INPUT
+
+T_IrssiObj
+       $var = irssi_ref_object($arg)
+
+T_PlainObj
+       $var = irssi_ref_object($arg)
+
+OUTPUT
+
+T_IrssiObj
+       $arg = iobject_bless((SERVER_REC *)$var);
+
+T_PlainObj
+       $arg = plain_bless($var, \"$type\");
+
diff --git a/apps/irssi/src/perl/get-signals.pl b/apps/irssi/src/perl/get-signals.pl
new file mode 100755 (executable)
index 0000000..df4667c
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+print "static PERL_SIGNAL_ARGS_REC perl_signal_args[] =\n{\n";
+
+while (<STDIN>) {
+       chomp;
+
+       next if (!/^ "([^"]*)"(<.*>)?,\s*(.*)/);
+       next if (/\.\.\./);
+       next if (/\(/);
+
+       $signal = $1;
+       $_ = $3;
+
+       s/GList \* of ([^,]*)/glistptr_\1/g;
+       s/GSList of (\w+)s/gslist_\1/g;
+
+       s/char \*[^,]*/string/g;
+       s/ulong \*[^,]*/ulongptr/g;
+       s/int \*[^,]*/intptr/g;
+       s/int [^,]*/int/g;
+
+       # core
+        s/CHATNET_REC[^,]*/iobject/g;
+        s/SERVER_REC[^,]*/iobject/g;
+        s/RECONNECT_REC[^,]*/iobject/g;
+       s/CHANNEL_REC[^,]*/iobject/g;
+       s/QUERY_REC[^,]*/iobject/g;
+       s/COMMAND_REC[^,]*/Irssi::Command/g;
+       s/NICK_REC[^,]*/iobject/g;
+       s/LOG_REC[^,]*/Irssi::Log/g;
+       s/RAWLOG_REC[^,]*/Irssi::Rawlog/g;
+       s/IGNORE_REC[^,]*/Irssi::Ignore/g;
+       s/MODULE_REC[^,]*/Irssi::Module/g;
+
+       # irc
+       s/BAN_REC[^,]*/Irssi::Irc::Ban/g;
+       s/NETSPLIT_REC[^,]*/Irssi::Irc::Netsplit/g;
+       s/NETSPLIT_SERVER_REC[^,]*/Irssi::Irc::Netsplitserver/g;
+
+       # irc modules
+       s/DCC_REC[^,]*/siobject/g;
+       s/AUTOIGNORE_REC[^,]*/Irssi::Irc::Autoignore/g;
+       s/NOTIFYLIST_REC[^,]*/Irssi::Irc::Notifylist/g;
+
+       # fe-common
+       s/THEME_REC[^,]*/Irssi::UI::Theme/g;
+       s/KEYINFO_REC[^,]*/Irssi::UI::Keyinfo/g;
+       s/PROCESS_REC[^,]*/Irssi::UI::Process/g;
+       s/TEXT_DEST_REC[^,]*/Irssi::UI::TextDest/g;
+       s/WINDOW_REC[^,]*/Irssi::UI::Window/g;
+       s/WI_ITEM_REC[^,]*/iobject/g;
+
+       s/([\w\*:]+)(,|$)/"\1"\2/g;
+       print "    { \"$signal\", { $_, NULL } },\n";
+}
+
+print "\n    { NULL }\n};\n";
diff --git a/apps/irssi/src/perl/irssi-core.pl b/apps/irssi/src/perl/irssi-core.pl
new file mode 100644 (file)
index 0000000..31fbe48
--- /dev/null
@@ -0,0 +1,47 @@
+# NOTE: this is printed through printf()-like function,
+# so no extra percent characters.
+
+# %%d : must be first - 1 if perl libraries are to be linked 
+#       statically with irssi binary, 0 if not
+# %%s : must be second - use Irssi; use Irssi::Irc; etc..
+package Irssi::Core;
+
+use Symbol qw(delete_package);
+
+sub is_static {
+  return %d;
+}
+
+sub destroy {
+  delete_package($_[0]);
+}
+
+sub eval_data {
+  my ($data, $id) = @_;
+  destroy("Irssi::Script::$id");
+
+  my $package = "Irssi::Script::$id";
+  my $eval = qq{package $package; %s sub handler { $data; }};
+  {
+      # hide our variables within this block
+      my ($filename, $package, $data);
+      eval $eval;
+  }
+  die $@ if $@;
+
+  eval {$package->handler;};
+  die $@ if $@;
+}
+
+sub eval_file {
+  my ($filename, $id) = @_;
+
+  local *FH;
+  open FH, $filename or die "File not found: $filename";
+  local($/) = undef;
+  my $data = <FH>;
+  close FH;
+  local($/) = "\n";
+
+  eval_data($data, $id);
+}
diff --git a/apps/irssi/src/perl/irssi-core.pl.h b/apps/irssi/src/perl/irssi-core.pl.h
new file mode 100644 (file)
index 0000000..3e4ec9a
--- /dev/null
@@ -0,0 +1,49 @@
+const char *irssi_core_code =
+"# NOTE: this is printed through printf()-like function,\n"
+"# so no extra percent characters.\n"
+"\n"
+"# %%d : must be first - 1 if perl libraries are to be linked \n"
+"#       statically with irssi binary, 0 if not\n"
+"# %%s : must be second - use Irssi; use Irssi::Irc; etc..\n"
+"package Irssi::Core;\n"
+"\n"
+"use Symbol qw(delete_package);\n"
+"\n"
+"sub is_static {\n"
+"  return %d;\n"
+"}\n"
+"\n"
+"sub destroy {\n"
+"  delete_package($_[0]);\n"
+"}\n"
+"\n"
+"sub eval_data {\n"
+"  my ($data, $id) = @_;\n"
+"  destroy(\"Irssi::Script::$id\");\n"
+"\n"
+"  my $package = \"Irssi::Script::$id\";\n"
+"  my $eval = qq{package $package; %s sub handler { $data; }};\n"
+"  {\n"
+"      # hide our variables within this block\n"
+"      my ($filename, $package, $data);\n"
+"      eval $eval;\n"
+"  }\n"
+"  die $@ if $@;\n"
+"\n"
+"  eval {$package->handler;};\n"
+"  die $@ if $@;\n"
+"}\n"
+"\n"
+"sub eval_file {\n"
+"  my ($filename, $id) = @_;\n"
+"\n"
+"  local *FH;\n"
+"  open FH, $filename or die \"File not found: $filename\";\n"
+"  local($/) = undef;\n"
+"  my $data = <FH>;\n"
+"  close FH;\n"
+"  local($/) = \"\\n\";\n"
+"\n"
+"  eval_data($data, $id);\n"
+"}\n"
+;
diff --git a/apps/irssi/src/perl/libperl_dynaloader.la b/apps/irssi/src/perl/libperl_dynaloader.la
new file mode 100644 (file)
index 0000000..9117cdb
--- /dev/null
@@ -0,0 +1,25 @@
+# libsilc.la - a libtool library file
+# Generated by ltmain.sh - GNU libtool 1.3.5 (1.385.2.206 2000/05/27 11:12:27)
+
+# The name that we can dlopen(3).
+dlname=''
+
+# Names of this library.
+library_names=''
+
+# The name of the static archive.
+old_library='DynaLoader.a'
+
+# Libraries that this one depends upon.
+dependency_libs=''
+
+# Version information for libsilc.
+current=0
+age=0
+revision=0
+
+# Is this an already installed library?
+installed=no
+
+# Directory that this library needs to be installed in:
+libdir=''
diff --git a/apps/irssi/src/perl/libperl_orig.la b/apps/irssi/src/perl/libperl_orig.la
new file mode 100644 (file)
index 0000000..c83ffc4
--- /dev/null
@@ -0,0 +1,25 @@
+# libsilc.la - a libtool library file
+# Generated by ltmain.sh - GNU libtool 1.3.5 (1.385.2.206 2000/05/27 11:12:27)
+
+# The name that we can dlopen(3).
+dlname=''
+
+# Names of this library.
+library_names=''
+
+# The name of the static archive.
+old_library='libperl_orig.a'
+
+# Libraries that this one depends upon.
+dependency_libs=''
+
+# Version information for libsilc.
+current=0
+age=0
+revision=0
+
+# Is this an already installed library?
+installed=no
+
+# Directory that this library needs to be installed in:
+libdir=''
diff --git a/apps/irssi/src/perl/module-fe.h b/apps/irssi/src/perl/module-fe.h
new file mode 100644 (file)
index 0000000..2dc8c28
--- /dev/null
@@ -0,0 +1,4 @@
+#include "module.h"
+
+#undef MODULE_NAME
+#define MODULE_NAME "fe-common/perl"
diff --git a/apps/irssi/src/perl/module-formats.c b/apps/irssi/src/perl/module-formats.c
new file mode 100644 (file)
index 0000000..5c2e1c6
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ module-formats.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "formats.h"
+
+FORMAT_REC feperl_formats[] = {
+       { MODULE_NAME, "Core", 0 },
+
+       /* ---- */
+       { NULL, "Perl", 0 },
+
+       { "script_not_found", "Script {hilight $0} not found", 1, { 0 } },
+       { "script_not_loaded", "Script {hilight $0} is not loaded", 1, { 0 } },
+       { "script_loaded", "Loaded script {hilight $0}", 2, { 0, 0 } },
+       { "script_unloaded", "Unloaded script {hilight $0}", 1, { 0 } },
+       { "no_scripts_loaded", "No scripts are loaded", 0 },
+       { "script_list_header", "Loaded scripts:", 0 },
+       { "script_list_line", "$[!15]0 $1", 2, { 0, 0 } },
+       { "script_list_footer", "", 0 },
+       { "script_error", "{error Error in script {hilight $0}:}", 1, { 0 } },
+
+       { NULL, NULL, 0 }
+};
diff --git a/apps/irssi/src/perl/module-formats.h b/apps/irssi/src/perl/module-formats.h
new file mode 100644 (file)
index 0000000..74d2e13
--- /dev/null
@@ -0,0 +1,19 @@
+#include "formats.h"
+
+enum {
+       IRCTXT_MODULE_NAME,
+
+       IRCTXT_FILL_1,
+
+        TXT_SCRIPT_NOT_FOUND,
+        TXT_SCRIPT_NOT_LOADED,
+        TXT_SCRIPT_LOADED,
+        TXT_SCRIPT_UNLOADED,
+        TXT_NO_SCRIPTS_LOADED,
+        TXT_SCRIPT_LIST_HEADER,
+        TXT_SCRIPT_LIST_LINE,
+        TXT_SCRIPT_LIST_FOOTER,
+        TXT_SCRIPT_ERROR
+};
+
+extern FORMAT_REC feperl_formats[];
diff --git a/apps/irssi/src/perl/module.h b/apps/irssi/src/perl/module.h
new file mode 100644 (file)
index 0000000..e12482a
--- /dev/null
@@ -0,0 +1,25 @@
+#ifdef NEED_PERL_H
+#  include <EXTERN.h>
+#  ifndef _SEM_SEMUN_UNDEFINED
+#    define HAS_UNION_SEMUN
+#  endif
+#  include <perl.h>
+
+#  undef _
+#  undef PACKAGE
+
+/* For compatibility with perl 5.004 and older */
+#  ifndef ERRSV
+#    define ERRSV GvSV(errgv)
+#  endif
+
+extern PerlInterpreter *my_perl; /* must be called my_perl or some perl implementations won't work */
+#endif
+
+#include "common.h"
+
+#define MODULE_NAME "perl/core"
+
+/* Change this every time when some API changes between irssi's perl module
+   (or irssi itself) and irssi's perl libraries. */
+#define IRSSI_PERL_API_VERSION 20011214
diff --git a/apps/irssi/src/perl/perl-common.c b/apps/irssi/src/perl/perl-common.c
new file mode 100644 (file)
index 0000000..76fa3f3
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ perl-common.c : irssi
+
+    Copyright (C) 2000 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "core.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "commands.h"
+#include "ignore.h"
+#include "log.h"
+#include "rawlog.h"
+#include "servers-reconnect.h"
+
+#include "window-item-def.h"
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+#include "channels.h"
+#include "queries.h"
+#include "nicklist.h"
+
+#include "perl-common.h"
+
+typedef struct {
+       char *stash;
+        PERL_OBJECT_FUNC fill_func;
+} PERL_OBJECT_REC;
+
+#ifndef HAVE_PL_PERL
+STRLEN PL_na;
+#endif
+
+static GHashTable *iobject_stashes, *plain_stashes;
+static GSList *use_protocols;
+
+/* returns the package who called us */
+const char *perl_get_package(void)
+{
+       return SvPV(perl_eval_pv("caller", TRUE), PL_na);
+}
+
+/* Parses the package part from function name */
+char *perl_function_get_package(const char *function)
+{
+       const char *p;
+        int pos;
+
+        pos = 0;
+       for (p = function; *p != '\0'; p++) {
+               if (*p == ':' && p[1] == ':') {
+                       if (++pos == 3)
+                                return g_strndup(function, (int) (p-function));
+               }
+       }
+
+        return NULL;
+}
+
+SV *perl_func_sv_inc(SV *func, const char *package)
+{
+       char *name;
+
+       if (SvPOK(func)) {
+               /* prefix with package name */
+               name = g_strdup_printf("%s::%s", package,
+                                      (char *) SvPV(func, PL_na));
+               func = new_pv(name);
+                g_free(name);
+       } else {
+               SvREFCNT_inc(func);
+       }
+
+        return func;
+}
+
+SV *irssi_bless_iobject(int type, int chat_type, void *object)
+{
+        PERL_OBJECT_REC *rec;
+       HV *stash, *hv;
+
+       g_return_val_if_fail((type & ~0xffff) == 0, NULL);
+       g_return_val_if_fail((chat_type & ~0xffff) == 0, NULL);
+
+       rec = g_hash_table_lookup(iobject_stashes,
+                                 GINT_TO_POINTER(type | (chat_type << 16)));
+       if (rec == NULL) {
+                /* unknown iobject */
+               return newSViv(GPOINTER_TO_INT(object));
+       }
+
+       stash = gv_stashpv(rec->stash, 1);
+
+       hv = newHV();
+       hv_store(hv, "_irssi", 6, newSViv(GPOINTER_TO_INT(object)), 0);
+        rec->fill_func(hv, object);
+       return sv_bless(newRV_noinc((SV*)hv), stash);
+}
+
+SV *irssi_bless_plain(const char *stash, void *object)
+{
+        PERL_OBJECT_FUNC fill_func;
+       HV *hv;
+
+       fill_func = g_hash_table_lookup(plain_stashes, stash);
+
+       hv = newHV();
+       hv_store(hv, "_irssi", 6, newSViv(GPOINTER_TO_INT(object)), 0);
+       if (fill_func != NULL)
+               fill_func(hv, object);
+       return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 1));
+}
+
+int irssi_is_ref_object(SV *o)
+{
+        SV **sv;
+       HV *hv;
+
+        hv = hvref(o);
+       if (hv != NULL) {
+               sv = hv_fetch(hv, "_irssi", 6, 0);
+               if (sv != NULL)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+void *irssi_ref_object(SV *o)
+{
+        SV **sv;
+       HV *hv;
+
+        hv = hvref(o);
+       if (hv == NULL)
+               return NULL;
+
+       sv = hv_fetch(hv, "_irssi", 6, 0);
+       if (sv == NULL)
+                croak("variable is damaged");
+       return GINT_TO_POINTER(SvIV(*sv));
+}
+
+void irssi_add_object(int type, int chat_type, const char *stash,
+                     PERL_OBJECT_FUNC func)
+{
+       PERL_OBJECT_REC *rec;
+        void *hash;
+
+       g_return_if_fail((type & ~0xffff) == 0);
+       g_return_if_fail((chat_type & ~0xffff) == 0);
+
+        hash = GINT_TO_POINTER(type | (chat_type << 16));
+       rec = g_hash_table_lookup(iobject_stashes, hash);
+       if (rec == NULL) {
+               rec = g_new(PERL_OBJECT_REC, 1);
+               rec->stash = g_strdup(stash);
+               g_hash_table_insert(iobject_stashes, hash, rec);
+       }
+       rec->fill_func = func;
+}
+
+void irssi_add_plain(const char *stash, PERL_OBJECT_FUNC func)
+{
+        if (g_hash_table_lookup(plain_stashes, stash) == NULL)
+               g_hash_table_insert(plain_stashes, g_strdup(stash), func);
+}
+
+void irssi_add_plains(PLAIN_OBJECT_INIT_REC *objects)
+{
+       while (objects->name != NULL) {
+                irssi_add_plain(objects->name, objects->fill_func);
+                objects++;
+       }
+}
+
+char *perl_get_use_list(void)
+{
+       GString *str;
+       GSList *tmp;
+        char *ret;
+        const char *use_lib;
+
+       str = g_string_new(NULL);
+
+       use_lib = settings_get_str("perl_use_lib");
+       g_string_sprintf(str, "use lib qw(%s/scripts "SCRIPTDIR" %s);",
+                        get_irssi_dir(), use_lib);
+
+        g_string_append(str, "use Irssi;");
+       if (irssi_gui != IRSSI_GUI_NONE)
+               g_string_append(str, "use Irssi::UI;");
+
+       for (tmp = use_protocols; tmp != NULL; tmp = tmp->next)
+               g_string_sprintfa(str, "use Irssi::%s;", (char *) tmp->data);
+
+       ret = str->str;
+        g_string_free(str, FALSE);
+        return ret;
+}
+
+void irssi_callXS(void (*subaddr)(CV* cv), CV *cv, SV **mark)
+{
+       dSP;
+
+       PUSHMARK(mark);
+       (*subaddr)(cv);
+
+       PUTBACK;
+}
+
+void perl_chatnet_fill_hash(HV *hv, CHATNET_REC *chatnet)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(chatnet != NULL);
+
+       type = "CHATNET";
+       chat_type = (char *) chat_protocol_find_id(chatnet->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       hv_store(hv, "name", 4, new_pv(chatnet->name), 0);
+
+       hv_store(hv, "nick", 4, new_pv(chatnet->nick), 0);
+       hv_store(hv, "username", 8, new_pv(chatnet->username), 0);
+       hv_store(hv, "realname", 8, new_pv(chatnet->realname), 0);
+
+       hv_store(hv, "own_host", 8, new_pv(chatnet->own_host), 0);
+       hv_store(hv, "autosendcmd", 11, new_pv(chatnet->autosendcmd), 0);
+}
+
+void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(conn != NULL);
+
+       type = "SERVER CONNECT";
+       chat_type = (char *) chat_protocol_find_id(conn->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       hv_store(hv, "address", 7, new_pv(conn->address), 0);
+       hv_store(hv, "port", 4, newSViv(conn->port), 0);
+       hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0);
+
+       hv_store(hv, "password", 8, new_pv(conn->password), 0);
+       hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
+       hv_store(hv, "username", 8, new_pv(conn->username), 0);
+       hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
+}
+
+void perl_server_fill_hash(HV *hv, SERVER_REC *server)
+{
+       char *type;
+       HV *stash;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(server != NULL);
+
+       perl_connect_fill_hash(hv, server->connrec);
+
+       type = "SERVER";
+       hv_store(hv, "type", 4, new_pv(type), 0);
+
+       hv_store(hv, "connect_time", 12, newSViv(server->connect_time), 0);
+       hv_store(hv, "real_connect_time", 17, newSViv(server->real_connect_time), 0);
+
+       hv_store(hv, "tag", 3, new_pv(server->tag), 0);
+       hv_store(hv, "nick", 4, new_pv(server->nick), 0);
+
+       hv_store(hv, "connected", 9, newSViv(server->connected), 0);
+       hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0);
+
+       stash = gv_stashpv("Irssi::Rawlog", 0);
+       hv_store(hv, "rawlog", 6, sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(server->rawlog))), stash), 0);
+
+       hv_store(hv, "version", 7, new_pv(server->version), 0);
+       hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0);
+       hv_store(hv, "last_invite", 11, new_pv(server->last_invite), 0);
+       hv_store(hv, "server_operator", 15, newSViv(server->server_operator), 0);
+       hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0);
+       hv_store(hv, "banned", 6, newSViv(server->banned), 0);
+
+       hv_store(hv, "lag", 3, newSViv(server->lag), 0);
+}
+
+void perl_window_item_fill_hash(HV *hv, WI_ITEM_REC *item)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(item != NULL);
+
+       type = (char *) module_find_id_str("WINDOW ITEM TYPE", item->type);
+       chat_type = (char *) chat_protocol_find_id(item->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       if (item->server != NULL) {
+               hv_store(hv, "server", 6, iobject_bless(item->server), 0);
+       }
+       hv_store(hv, "name", 4, new_pv(item->name), 0);
+
+       hv_store(hv, "createtime", 10, newSViv(item->createtime), 0);
+       hv_store(hv, "data_level", 8, newSViv(item->data_level), 0);
+       hv_store(hv, "hilight_color", 10, new_pv(item->hilight_color), 0);
+}
+
+void perl_channel_fill_hash(HV *hv, CHANNEL_REC *channel)
+{
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(channel != NULL);
+
+       perl_window_item_fill_hash(hv, (WI_ITEM_REC *) channel);
+
+       hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
+       hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0);
+       hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0);
+
+       hv_store(hv, "no_modes", 8, newSViv(channel->no_modes), 0);
+       hv_store(hv, "mode", 4, new_pv(channel->mode), 0);
+       hv_store(hv, "limit", 5, newSViv(channel->limit), 0);
+       hv_store(hv, "key", 3, new_pv(channel->key), 0);
+
+       hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0);
+       hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0);
+       hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0);
+       hv_store(hv, "synced", 6, newSViv(channel->synced), 0);
+
+       hv_store(hv, "joined", 6, newSViv(channel->joined), 0);
+       hv_store(hv, "left", 4, newSViv(channel->left), 0);
+       hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0);
+}
+
+void perl_query_fill_hash(HV *hv, QUERY_REC *query)
+{
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(query != NULL);
+
+       perl_window_item_fill_hash(hv, (WI_ITEM_REC *) query);
+
+       hv_store(hv, "address", 7, new_pv(query->address), 0);
+       hv_store(hv, "server_tag", 10, new_pv(query->server_tag), 0);
+       hv_store(hv, "unwanted", 8, newSViv(query->unwanted), 0);
+}
+
+void perl_nick_fill_hash(HV *hv, NICK_REC *nick)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(nick != NULL);
+
+       type = "NICK";
+       chat_type = (char *) chat_protocol_find_id(nick->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       hv_store(hv, "nick", 4, new_pv(nick->nick), 0);
+       hv_store(hv, "host", 4, new_pv(nick->host), 0);
+       hv_store(hv, "realname", 8, new_pv(nick->realname), 0);
+       hv_store(hv, "hops", 4, newSViv(nick->hops), 0);
+
+       hv_store(hv, "gone", 4, newSViv(nick->gone), 0);
+       hv_store(hv, "serverop", 8, newSViv(nick->serverop), 0);
+
+       hv_store(hv, "op", 2, newSViv(nick->op), 0);
+       hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0);
+       hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
+
+       hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
+       hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
+}
+
+static void perl_command_fill_hash(HV *hv, COMMAND_REC *cmd)
+{
+       hv_store(hv, "category", 8, new_pv(cmd->category), 0);
+       hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0);
+}
+
+static void perl_ignore_fill_hash(HV *hv, IGNORE_REC *ignore)
+{
+       AV *av;
+       char **tmp;
+
+       hv_store(hv, "mask", 4, new_pv(ignore->mask), 0);
+       hv_store(hv, "servertag", 9, new_pv(ignore->servertag), 0);
+       av = newAV();
+       for (tmp = ignore->channels; *tmp != NULL; tmp++) {
+               av_push(av, new_pv(*tmp));
+       }
+       hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
+       hv_store(hv, "pattern", 7, new_pv(ignore->pattern), 0);
+
+       hv_store(hv, "level", 5, newSViv(ignore->level), 0);
+
+       hv_store(hv, "exception", 6, newSViv(ignore->exception), 0);
+       hv_store(hv, "regexp", 6, newSViv(ignore->regexp), 0);
+       hv_store(hv, "fullword", 8, newSViv(ignore->fullword), 0);
+}
+
+static void perl_log_fill_hash(HV *hv, LOG_REC *log)
+{
+       AV *av;
+       GSList *tmp;
+
+       hv_store(hv, "fname", 5, new_pv(log->fname), 0);
+       hv_store(hv, "real_fname", 10, new_pv(log->real_fname), 0);
+       hv_store(hv, "opened", 6, newSViv(log->opened), 0);
+       hv_store(hv, "level", 5, newSViv(log->level), 0);
+       hv_store(hv, "last", 4, newSViv(log->last), 0);
+       hv_store(hv, "autoopen", 8, newSViv(log->autoopen), 0);
+       hv_store(hv, "failed", 6, newSViv(log->failed), 0);
+       hv_store(hv, "temp", 4, newSViv(log->temp), 0);
+
+       av = newAV();
+       for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
+               av_push(av, plain_bless(tmp->data, "Irssi::Logitem"));
+       }
+       hv_store(hv, "items", 5, newRV_noinc((SV*)av), 0);
+}
+
+static void perl_log_item_fill_hash(HV *hv, LOG_ITEM_REC *item)
+{
+       hv_store(hv, "type", 4, newSViv(item->type), 0);
+       hv_store(hv, "name", 4, new_pv(item->name), 0);
+       hv_store(hv, "servertag", 9, new_pv(item->servertag), 0);
+}
+
+static void perl_rawlog_fill_hash(HV *hv, RAWLOG_REC *rawlog)
+{
+       hv_store(hv, "logging", 7, newSViv(rawlog->logging), 0);
+       hv_store(hv, "nlines", 6, newSViv(rawlog->nlines), 0);
+}
+
+static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect)
+{
+       char *type;
+
+       perl_connect_fill_hash(hv, reconnect->conn);
+
+       type = "RECONNECT";
+       hv_store(hv, "type", 4, new_pv(type), 0);
+
+       hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0);
+       hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
+}
+
+void perl_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item)
+{
+        const char *cmdchars;
+       char *sendcmd = (char *) cmd;
+
+       if (*cmd == '\0')
+                return;
+
+        cmdchars = settings_get_str("cmdchars");
+       if (strchr(cmdchars, *cmd) == NULL) {
+               /* no command char - let's put it there.. */
+               sendcmd = g_strdup_printf("%c%s", *cmdchars, cmd);
+       }
+
+       signal_emit("send command", 3, sendcmd, server, item);
+       if (sendcmd != cmd) g_free(sendcmd);
+}
+
+static void perl_register_protocol(CHAT_PROTOCOL_REC *rec)
+{
+       static char *items[] = {
+               "Chatnet",
+               "Server", "ServerConnect", "ServerSetup",
+               "Channel", "Query",
+               "Nick"
+       };
+       static char *find_use_code =
+               "my $pkg = Irssi::%s; $pkg =~ s/::/\\//;\n"
+               "foreach my $i (@INC) {\n"
+               "  return 1 if (-f \"$i/$pkg.pm\");\n"
+               "}\n"
+               "return 0;\n";
+
+       char *name, stash[100], code[100], *pcode;
+       int type, chat_type, n;
+        SV *sv;
+
+       chat_type = chat_protocol_lookup(rec->name);
+       g_return_if_fail(chat_type >= 0);
+
+       name = g_strdup(rec->name);
+       g_strdown(name+1);
+
+       /* window items: channel, query */
+       type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Channel", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_channel_fill_hash);
+
+       type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Query", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_query_fill_hash);
+
+        /* channel nicks */
+       type = module_get_uniq_id("NICK", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Nick", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_nick_fill_hash);
+
+        /* chatnets */
+       type = module_get_uniq_id("CHATNET", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Chatnet", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_chatnet_fill_hash);
+
+       /* server specific */
+       type = module_get_uniq_id("SERVER", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Server", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_server_fill_hash);
+
+       type = module_get_uniq_id("SERVER CONNECT", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Connect", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_connect_fill_hash);
+
+       /* register ISAs */
+       for (n = 0; n < sizeof(items)/sizeof(items[0]); n++) {
+               g_snprintf(code, sizeof(code),
+                          "@Irssi::%s::%s::ISA = qw(Irssi::%s);",
+                          name, items[n], items[n]);
+               perl_eval_pv(code, TRUE);
+       }
+
+       pcode = g_strdup_printf(find_use_code, name);
+       sv = perl_eval_pv(pcode, TRUE);
+       g_free(pcode);
+
+       if (SvIV(sv)) {
+               use_protocols =
+                       g_slist_append(use_protocols, g_strdup(name));
+       }
+
+       g_free(name);
+}
+
+static void free_iobject_hash(void *key, PERL_OBJECT_REC *rec)
+{
+        g_free(rec->stash);
+       g_free(rec);
+}
+
+static int free_iobject_proto(void *key, void *value, void *chat_type)
+{
+       if ((GPOINTER_TO_INT(key) >> 16) == GPOINTER_TO_INT(chat_type)) {
+                free_iobject_hash(key, value);
+                return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void perl_unregister_protocol(CHAT_PROTOCOL_REC *rec)
+{
+        GSList *item;
+
+       item = gslist_find_icase_string(use_protocols, rec->name);
+       if (item != NULL) {
+               g_free(item->data);
+               use_protocols =
+                       g_slist_remove(use_protocols, item->data);
+       }
+       g_hash_table_foreach_remove(iobject_stashes,
+                                   (GHRFunc) free_iobject_proto,
+                                   GINT_TO_POINTER(rec->id));
+}
+
+void perl_common_start(void)
+{
+       static PLAIN_OBJECT_INIT_REC core_plains[] = {
+               { "Irssi::Command", (PERL_OBJECT_FUNC) perl_command_fill_hash },
+               { "Irssi::Ignore", (PERL_OBJECT_FUNC) perl_ignore_fill_hash },
+               { "Irssi::Log", (PERL_OBJECT_FUNC) perl_log_fill_hash },
+               { "Irssi::Logitem", (PERL_OBJECT_FUNC) perl_log_item_fill_hash },
+               { "Irssi::Rawlog", (PERL_OBJECT_FUNC) perl_rawlog_fill_hash },
+               { "Irssi::Reconnect", (PERL_OBJECT_FUNC) perl_reconnect_fill_hash },
+
+               { NULL, NULL }
+       };
+
+       iobject_stashes = g_hash_table_new((GHashFunc) g_direct_hash,
+                                       (GCompareFunc) g_direct_equal);
+       plain_stashes = g_hash_table_new((GHashFunc) g_str_hash,
+                                        (GCompareFunc) g_str_equal);
+        irssi_add_plains(core_plains);
+
+        use_protocols = NULL;
+       g_slist_foreach(chat_protocols, (GFunc) perl_register_protocol, NULL);
+
+       signal_add("chat protocol created", (SIGNAL_FUNC) perl_register_protocol);
+       signal_add("chat protocol destroyed", (SIGNAL_FUNC) perl_unregister_protocol);
+}
+
+void perl_common_stop(void)
+{
+        g_hash_table_foreach(iobject_stashes, (GHFunc) free_iobject_hash, NULL);
+       g_hash_table_destroy(iobject_stashes);
+        iobject_stashes = NULL;
+
+       g_hash_table_foreach(plain_stashes, (GHFunc) g_free, NULL);
+       g_hash_table_destroy(plain_stashes);
+        plain_stashes = NULL;
+
+       g_slist_foreach(use_protocols, (GFunc) g_free, NULL);
+       g_slist_free(use_protocols);
+        use_protocols = NULL;
+
+       signal_remove("chat protocol created", (SIGNAL_FUNC) perl_register_protocol);
+       signal_remove("chat protocol destroyed", (SIGNAL_FUNC) perl_unregister_protocol);
+}
diff --git a/apps/irssi/src/perl/perl-common.h b/apps/irssi/src/perl/perl-common.h
new file mode 100644 (file)
index 0000000..9fa85f0
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef __PERL_COMMON_H
+#define __PERL_COMMON_H
+
+/* helper defines */
+#define new_pv(a) \
+       (newSVpv((a) == NULL ? "" : (a), (a) == NULL ? 0 : strlen(a)))
+
+#define is_hvref(o) \
+       ((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVHV))
+
+#define hvref(o) \
+       (is_hvref(o) ? (HV *)SvRV(o) : NULL)
+
+typedef void (*PERL_OBJECT_FUNC) (HV *hv, void *object);
+
+typedef struct {
+       char *name;
+        PERL_OBJECT_FUNC fill_func;
+} PLAIN_OBJECT_INIT_REC;
+
+/* Returns the package who called us */
+const char *perl_get_package(void);
+/* Parses the package part from function name */
+char *perl_function_get_package(const char *function);
+/* If SV is a string, prefix it with given package.
+   Increases the reference counter for the return value. */
+SV *perl_func_sv_inc(SV *func, const char *package);
+
+/* For compatibility with perl 5.004 and older */
+#ifndef HAVE_PL_PERL
+#  define PL_sv_undef sv_undef
+extern STRLEN PL_na;
+#endif
+
+#define iobject_bless(object) \
+       ((object) == NULL ? &PL_sv_undef : \
+       irssi_bless_iobject((object)->type, (object)->chat_type, object))
+
+#define simple_iobject_bless(object) \
+       ((object) == NULL ? &PL_sv_undef : \
+       irssi_bless_iobject((object)->type, 0, object))
+
+#define plain_bless(object, stash) \
+       ((object) == NULL ? &PL_sv_undef : \
+       irssi_bless_plain(stash, object))
+
+SV *irssi_bless_iobject(int type, int chat_type, void *object);
+SV *irssi_bless_plain(const char *stash, void *object);
+int irssi_is_ref_object(SV *o);
+void *irssi_ref_object(SV *o);
+
+void irssi_add_object(int type, int chat_type, const char *stash,
+                     PERL_OBJECT_FUNC func);
+void irssi_add_plain(const char *stash, PERL_OBJECT_FUNC func);
+void irssi_add_plains(PLAIN_OBJECT_INIT_REC *objects);
+
+char *perl_get_use_list(void);
+
+#define irssi_boot(x) { \
+       extern void boot_Irssi__##x(CV *cv); \
+       irssi_callXS(boot_Irssi__##x, cv, mark); \
+       }
+void irssi_callXS(void (*subaddr)(CV* cv), CV *cv, SV **mark);
+
+void perl_common_start(void);
+void perl_common_stop(void);
+
+#endif
diff --git a/apps/irssi/src/perl/perl-core.c b/apps/irssi/src/perl/perl-core.c
new file mode 100644 (file)
index 0000000..1ed95c3
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ perl-core.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "modules.h"
+#include "core.h"
+#include "signals.h"
+#include "misc.h"
+#include "settings.h"
+#include "lib-config/iconfig.h" /* FIXME: remove before .99 */
+
+#include "perl-core.h"
+#include "perl-common.h"
+#include "perl-signals.h"
+#include "perl-sources.h"
+
+#include "XSUB.h"
+#include "irssi-core.pl.h"
+
+/* For compatibility with perl 5.004 and older */
+#ifndef HAVE_PL_PERL
+#  define PL_perl_destruct_level perl_destruct_level
+#endif
+
+GSList *perl_scripts;
+PerlInterpreter *my_perl;
+
+static int print_script_errors;
+
+#define IS_PERL_SCRIPT(file) \
+       (strlen(file) > 3 && strcmp(file+strlen(file)-3, ".pl") == 0)
+
+static void perl_script_destroy_package(PERL_SCRIPT_REC *script)
+{
+       dSP;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(new_pv(script->package)));
+       PUTBACK;
+
+       perl_call_pv("Irssi::Core::destroy", G_VOID|G_EVAL|G_DISCARD);
+
+       SPAGAIN;
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+static void perl_script_destroy(PERL_SCRIPT_REC *script)
+{
+       perl_scripts = g_slist_remove(perl_scripts, script);
+
+       signal_emit("script destroyed", 1, script);
+
+       perl_signal_remove_script(script);
+       perl_source_remove_script(script);
+
+       g_free(script->name);
+       g_free(script->package);
+        g_free_not_null(script->path);
+        g_free_not_null(script->data);
+        g_free(script);
+}
+
+extern void boot_DynaLoader(CV* cv);
+
+#if PERL_STATIC_LIBS == 1
+extern void boot_Irssi(CV *cv);
+
+XS(boot_Irssi_Core)
+{
+       dXSARGS;
+
+       irssi_callXS(boot_Irssi, cv, mark);
+        irssi_boot(Irc);
+        irssi_boot(UI);
+        irssi_boot(TextUI);
+       XSRETURN_YES;
+}
+#endif
+
+static void xs_init(void)
+{
+       dXSUB_SYS;
+
+#if PERL_STATIC_LIBS == 1
+       newXS("Irssi::Core::boot_Irssi_Core", boot_Irssi_Core, __FILE__);
+#endif
+
+       /* boot the dynaloader too, if we want to use some
+          other dynamic modules.. */
+       newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
+}
+
+/* Initialize perl interpreter */
+void perl_scripts_init(void)
+{
+       char *args[] = {"", "-e", "0"};
+       char *code, *use_code;
+
+       perl_scripts = NULL;
+        perl_sources_start();
+       perl_signals_start();
+
+       my_perl = perl_alloc();
+       perl_construct(my_perl);
+
+       perl_parse(my_perl, xs_init, 3, args, NULL);
+#if PERL_STATIC_LIBS == 1
+       perl_eval_pv("Irssi::Core::boot_Irssi_Core();", TRUE);
+#endif
+
+        perl_common_start();
+
+       use_code = perl_get_use_list();
+        code = g_strdup_printf(irssi_core_code, PERL_STATIC_LIBS, use_code);
+       perl_eval_pv(code, TRUE);
+
+       g_free(code);
+        g_free(use_code);
+}
+
+/* Destroy all perl scripts and deinitialize perl interpreter */
+void perl_scripts_deinit(void)
+{
+       if (my_perl == NULL)
+               return;
+
+       /* unload all scripts */
+        while (perl_scripts != NULL)
+               perl_script_unload(perl_scripts->data);
+
+        signal_emit("perl scripts deinit", 0);
+
+        perl_signals_stop();
+       perl_sources_stop();
+       perl_common_stop();
+
+       /* Unload all perl libraries loaded with dynaloader */
+       perl_eval_pv("foreach my $lib (@DynaLoader::dl_modules) { if ($lib =~ /^Irssi\\b/) { $lib .= '::deinit();'; eval $lib; } }", TRUE);
+       perl_eval_pv("eval { foreach my $lib (@DynaLoader::dl_librefs) { DynaLoader::dl_unload_file($lib); } }", TRUE);
+
+       /* perl interpreter */
+       perl_destruct(my_perl);
+       perl_free(my_perl);
+       my_perl = NULL;
+}
+
+/* Modify the script name so that all non-alphanumeric characters are
+   translated to '_' */
+void script_fix_name(char *name)
+{
+       char *p;
+
+       p = strrchr(name, '.');
+       if (p != NULL) *p = '\0';
+
+       while (*name != '\0') {
+               if (*name != '_' && !i_isalnum(*name))
+                       *name = '_';
+               name++;
+       }
+}
+
+static char *script_file_get_name(const char *path)
+{
+       char *name;
+
+        name = g_strdup(g_basename(path));
+       script_fix_name(name);
+        return name;
+}
+
+static char *script_data_get_name(void)
+{
+       GString *name;
+        char *ret;
+       int n;
+
+       name = g_string_new(NULL);
+        n = 1;
+       do {
+               g_string_sprintf(name, "data%d", n);
+                n++;
+       } while (perl_script_find(name->str) != NULL);
+
+       ret = name->str;
+        g_string_free(name, FALSE);
+        return ret;
+}
+
+static int perl_script_eval(PERL_SCRIPT_REC *script)
+{
+       dSP;
+       char *error;
+       int retcount;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(new_pv(script->path != NULL ? script->path :
+                                script->data)));
+       XPUSHs(sv_2mortal(new_pv(script->name)));
+       PUTBACK;
+
+       retcount = perl_call_pv(script->path != NULL ?
+                               "Irssi::Core::eval_file" :
+                               "Irssi::Core::eval_data",
+                               G_EVAL|G_SCALAR);
+       SPAGAIN;
+
+        error = NULL;
+       if (SvTRUE(ERRSV)) {
+                error = SvPV(ERRSV, PL_na);
+       } else if (retcount > 0) {
+               error = POPp;
+       }
+
+       if (error != NULL) {
+               if (*error == '\0')
+                       error = NULL;
+               else {
+                        error = g_strdup(error);
+                       signal_emit("script error", 2, script, error);
+                        g_free(error);
+               }
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+
+        return error == NULL;
+}
+
+/* NOTE: name must not be free'd */
+static PERL_SCRIPT_REC *script_load(char *name, const char *path,
+                                   const char *data)
+{
+        PERL_SCRIPT_REC *script;
+
+       /* if there's a script with a same name, destroy it */
+       script = perl_script_find(name);
+       if (script != NULL)
+               perl_script_destroy(script);
+
+       script = g_new0(PERL_SCRIPT_REC, 1);
+       script->name = name;
+       script->package = g_strdup_printf("Irssi::Script::%s", name);
+       script->path = g_strdup(path);
+        script->data = g_strdup(data);
+
+       perl_scripts = g_slist_append(perl_scripts, script);
+       signal_emit("script created", 1, script);
+
+       if (!perl_script_eval(script))
+                script = NULL; /* the script is destroyed in "script error" signal */
+        return script;
+}
+
+/* Load a perl script, path must be a full path. */
+PERL_SCRIPT_REC *perl_script_load_file(const char *path)
+{
+       char *name;
+
+        g_return_val_if_fail(path != NULL, NULL);
+
+        name = script_file_get_name(path);
+        return script_load(name, path, NULL);
+}
+
+/* Load a perl script from given data */
+PERL_SCRIPT_REC *perl_script_load_data(const char *data)
+{
+       char *name;
+
+        g_return_val_if_fail(data != NULL, NULL);
+
+       name = script_data_get_name();
+       return script_load(name, NULL, data);
+}
+
+/* Unload perl script */
+void perl_script_unload(PERL_SCRIPT_REC *script)
+{
+        g_return_if_fail(script != NULL);
+
+       perl_script_destroy_package(script);
+        perl_script_destroy(script);
+}
+
+/* Find loaded script by name */
+PERL_SCRIPT_REC *perl_script_find(const char *name)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(name != NULL, NULL);
+
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+               if (strcmp(rec->name, name) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+/* Find loaded script by package */
+PERL_SCRIPT_REC *perl_script_find_package(const char *package)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(package != NULL, NULL);
+
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+               if (strcmp(rec->package, package) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+/* Returns full path for the script */
+char *perl_script_get_path(const char *name)
+{
+       struct stat statbuf;
+       char *file, *path;
+
+       if (g_path_is_absolute(name) || (name[0] == '~' && name[1] == '/')) {
+               /* full path specified */
+                return convert_home(name);
+       }
+
+       /* add .pl suffix if it's missing */
+       file = IS_PERL_SCRIPT(name) ? g_strdup(name) :
+               g_strdup_printf("%s.pl", name);
+
+       /* check from ~/.irssi/scripts/ */
+       path = g_strdup_printf("%s/scripts/%s", get_irssi_dir(), file);
+       if (stat(path, &statbuf) != 0) {
+               /* check from SCRIPTDIR */
+               g_free(path);
+               path = g_strdup_printf(SCRIPTDIR"/%s", file);
+               if (stat(path, &statbuf) != 0)
+                        path = NULL;
+       }
+       g_free(file);
+        return path;
+}
+
+/* If core should handle printing script errors */
+void perl_core_print_script_error(int print)
+{
+        print_script_errors = print;
+}
+
+/* Returns the perl module's API version. */
+int perl_get_api_version(void)
+{
+        return IRSSI_PERL_API_VERSION;
+}
+
+static void perl_scripts_autorun(void)
+{
+       DIR *dirp;
+       struct dirent *dp;
+       struct stat statbuf;
+       char *path, *fname;
+
+        /* run *.pl scripts from ~/.irssi/scripts/autorun/ */
+       path = g_strdup_printf("%s/scripts/autorun", get_irssi_dir());
+       dirp = opendir(path);
+       if (dirp == NULL) {
+               g_free(path);
+               return;
+       }
+
+       while ((dp = readdir(dirp)) != NULL) {
+               if (!IS_PERL_SCRIPT(dp->d_name))
+                       continue;
+
+               fname = g_strdup_printf("%s/%s", path, dp->d_name);
+               if (stat(fname, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode))
+                       perl_script_load_file(fname);
+               g_free(fname);
+       }
+       closedir(dirp);
+       g_free(path);
+}
+
+static void sig_script_error(PERL_SCRIPT_REC *script, const char *error)
+{
+       char *str;
+
+       if (print_script_errors) {
+               str = g_strdup_printf("Script '%s' error:",
+                                     script == NULL ? "??" : script->name);
+               signal_emit("gui dialog", 2, "error", str);
+               signal_emit("gui dialog", 2, "error", error);
+                g_free(str);
+       }
+
+       if (script != NULL) {
+               perl_script_unload(script);
+                signal_stop();
+       }
+}
+
+static void sig_autorun(void)
+{
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_autorun);
+
+        perl_scripts_autorun();
+}
+
+void perl_core_init(void)
+{
+        print_script_errors = 1;
+       settings_add_str("perl", "perl_use_lib", PERL_USE_LIB);
+
+       PL_perl_destruct_level = 1;
+       perl_signals_init();
+        signal_add_last("script error", (SIGNAL_FUNC) sig_script_error);
+
+       perl_scripts_init();
+
+       if (irssi_init_finished)
+               perl_scripts_autorun();
+       else {
+               signal_add("irssi init finished", (SIGNAL_FUNC) sig_autorun);
+               settings_check();
+       }
+
+       module_register("perl", "core");
+}
+
+void perl_core_deinit(void)
+{
+       perl_signals_deinit();
+        perl_scripts_deinit();
+
+       signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
+}
diff --git a/apps/irssi/src/perl/perl-core.h b/apps/irssi/src/perl/perl-core.h
new file mode 100644 (file)
index 0000000..b451cc5
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef __PERL_CORE_H
+#define __PERL_CORE_H
+
+typedef struct {
+       char *name; /* unique name */
+        char *package; /* package name */
+
+        /* Script can be loaded from a file, or from some data in memory */
+       char *path; /* FILE: full path for file */
+       char *data; /* DATA: data used for the script */
+} PERL_SCRIPT_REC;
+
+extern GSList *perl_scripts;
+
+/* Initialize perl interpreter */
+void perl_scripts_init(void);
+/* Destroy all perl scripts and deinitialize perl interpreter */
+void perl_scripts_deinit(void);
+
+/* Load a perl script, path must be a full path. */
+PERL_SCRIPT_REC *perl_script_load_file(const char *path);
+/* Load a perl script from given data */
+PERL_SCRIPT_REC *perl_script_load_data(const char *data);
+/* Unload perl script */
+void perl_script_unload(PERL_SCRIPT_REC *script);
+
+/* Find loaded script by name */
+PERL_SCRIPT_REC *perl_script_find(const char *name);
+/* Find loaded script by package */
+PERL_SCRIPT_REC *perl_script_find_package(const char *package);
+
+/* Returns full path for the script */
+char *perl_script_get_path(const char *name);
+/* Modify the script name so that all non-alphanumeric characters are
+   translated to '_' */
+void script_fix_name(char *name);
+
+/* If core should handle printing script errors */
+void perl_core_print_script_error(int print);
+
+/* Returns the perl module's API version. */
+int perl_get_api_version(void);
+
+/* Checks that the API version is correct. */
+#define perl_api_version_check(library) \
+       if (perl_get_api_version() != IRSSI_PERL_API_VERSION) { \
+               die("Version of perl module (%d) doesn't match the " \
+                   "version of "library" library (%d)", \
+                   perl_get_api_version(), IRSSI_PERL_API_VERSION); \
+               return; \
+        }
+
+void perl_core_init(void);
+void perl_core_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/perl/perl-fe.c b/apps/irssi/src/perl/perl-fe.c
new file mode 100644 (file)
index 0000000..3c47a6d
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ perl-core.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module-fe.h"
+#include "modules.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+
+#include "printtext.h"
+#include "completion.h"
+
+#include "perl-core.h"
+
+static void cmd_script(const char *data, SERVER_REC *server, void *item)
+{
+       if (*data == '\0')
+                data = "list";
+
+       command_runsub("script", data, server, item);
+}
+
+static void cmd_script_exec(const char *data)
+{
+        PERL_SCRIPT_REC *script;
+       GHashTable *optlist;
+       char *code;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_GETREST,
+                           "script exec", &optlist, &code))
+               return;
+
+        if (*code == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+        script = perl_script_load_data(code);
+       if (script != NULL &&
+           g_hash_table_lookup(optlist, "permanent") == NULL) {
+               /* not a permanent script, unload immediately */
+                perl_script_unload(script);
+       }
+
+
+       cmd_params_free(free_arg);
+}
+
+static void cmd_script_load(const char *data)
+{
+        PERL_SCRIPT_REC *script;
+       char *fname, *path;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1, &path))
+               return;
+
+        if (*path == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       fname = perl_script_get_path(path);
+       if (fname == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                            TXT_SCRIPT_NOT_FOUND, data);
+       } else {
+               script = perl_script_load_file(fname);
+               if (script != NULL) {
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                                   TXT_SCRIPT_LOADED,
+                                   script->name, script->path);
+               }
+               g_free(fname);
+       }
+       cmd_params_free(free_arg);
+}
+
+static void cmd_script_unload(const char *data)
+{
+       PERL_SCRIPT_REC *script;
+        char *name;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1, &name))
+               return;
+
+        if (*name == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+        script_fix_name(name);
+       script = perl_script_find(name);
+       if (script == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                            TXT_SCRIPT_NOT_LOADED, name);
+       } else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_SCRIPT_UNLOADED, script->name);
+               perl_script_unload(script);
+       }
+       cmd_params_free(free_arg);
+}
+
+static void cmd_script_reset(const char *data)
+{
+       perl_scripts_deinit();
+       perl_scripts_init();
+}
+
+static void cmd_script_list(void)
+{
+       GSList *tmp;
+        GString *data;
+
+       if (perl_scripts == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                            TXT_NO_SCRIPTS_LOADED);
+                return;
+       }
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                   TXT_SCRIPT_LIST_HEADER);
+
+       data = g_string_new(NULL);
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+                if (rec->path != NULL)
+                       g_string_assign(data, rec->path);
+               else {
+                       g_string_assign(data, rec->data);
+                       if (data->len > 50) {
+                               g_string_truncate(data, 50);
+                                g_string_append(data, " ...");
+                       }
+               }
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                           TXT_SCRIPT_LIST_LINE, rec->name, data->str);
+       }
+        g_string_free(data, TRUE);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                   TXT_SCRIPT_LIST_FOOTER);
+}
+
+static void sig_script_error(PERL_SCRIPT_REC *script, const char *error)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                   TXT_SCRIPT_ERROR, script == NULL ? "??" : script->name);
+
+       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%[-s]%s", error);
+}
+
+static void sig_complete_load(GList **list, WINDOW_REC *window,
+                             const char *word, const char *line,
+                             int *want_space)
+{
+        char *user_dir;
+
+       if (*line != '\0')
+               return;
+
+       /* completing filename parameter for /SCRIPT LOAD */
+       user_dir = g_strdup_printf("%s/scripts", get_irssi_dir());
+       *list = filename_complete(word, user_dir);
+       *list = g_list_concat(*list, filename_complete(word, SCRIPTDIR));
+        g_free(user_dir);
+
+       if (*list != NULL) {
+               *want_space = FALSE;
+               signal_stop();
+       }
+}
+
+static GList *script_complete(const char *name)
+{
+       GSList *tmp;
+        GList *list;
+        int len;
+
+        list = NULL;
+        len = strlen(name);
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+               if (strncmp(rec->name, name, len) == 0)
+                        list = g_list_append(list, g_strdup(rec->name));
+       }
+
+        return list;
+}
+
+static void sig_complete_unload(GList **list, WINDOW_REC *window,
+                               const char *word, const char *line,
+                               int *want_space)
+{
+       if (*line != '\0')
+               return;
+
+       /* completing script parameter for /SCRIPT UNLOAD */
+       *list = script_complete(word);
+       if (*list != NULL)
+               signal_stop();
+}
+
+void fe_perl_init(void)
+{
+       theme_register(feperl_formats);
+
+       command_bind("script", NULL, (SIGNAL_FUNC) cmd_script);
+       command_bind("script exec", NULL, (SIGNAL_FUNC) cmd_script_exec);
+       command_bind("script load", NULL, (SIGNAL_FUNC) cmd_script_load);
+       command_bind("script unload", NULL, (SIGNAL_FUNC) cmd_script_unload);
+       command_bind("script reset", NULL, (SIGNAL_FUNC) cmd_script_reset);
+       command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list);
+       command_set_options("script exec", "permanent");
+
+        signal_add("script error", (SIGNAL_FUNC) sig_script_error);
+       signal_add("complete command script load", (SIGNAL_FUNC) sig_complete_load);
+       signal_add("complete command script unload", (SIGNAL_FUNC) sig_complete_unload);
+
+        perl_core_print_script_error(FALSE);
+       module_register("perl", "fe");
+}
+
+void fe_perl_deinit(void)
+{
+       command_unbind("script", (SIGNAL_FUNC) cmd_script);
+       command_unbind("script exec", (SIGNAL_FUNC) cmd_script_exec);
+       command_unbind("script load", (SIGNAL_FUNC) cmd_script_load);
+       command_unbind("script unload", (SIGNAL_FUNC) cmd_script_unload);
+       command_unbind("script reset", (SIGNAL_FUNC) cmd_script_reset);
+       command_unbind("script list", (SIGNAL_FUNC) cmd_script_list);
+
+        signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
+       signal_remove("complete command script load", (SIGNAL_FUNC) sig_complete_load);
+       signal_remove("complete command script unload", (SIGNAL_FUNC) sig_complete_unload);
+
+        perl_core_print_script_error(TRUE);
+}
diff --git a/apps/irssi/src/perl/perl-signals-list.h b/apps/irssi/src/perl/perl-signals-list.h
new file mode 100644 (file)
index 0000000..4c1c9ad
--- /dev/null
@@ -0,0 +1,178 @@
+static PERL_SIGNAL_ARGS_REC perl_signal_args[] =
+{
+    { "gui dialog", { "string", "string", NULL } },
+    { "send command", { "string", "iobject", "iobject", NULL } },
+    { "chat protocol created", { "CHAT_PROTOCOL_REC", NULL } },
+    { "chat protocol updated", { "CHAT_PROTOCOL_REC", NULL } },
+    { "chat protocol destroyed", { "CHAT_PROTOCOL_REC", NULL } },
+    { "channel created", { "iobject", "int", NULL } },
+    { "channel destroyed", { "iobject", NULL } },
+    { "chatnet created", { "iobject", NULL } },
+    { "chatnet destroyed", { "iobject", NULL } },
+    { "commandlist new", { "Irssi::Command", NULL } },
+    { "commandlist remove", { "Irssi::Command", NULL } },
+    { "error command", { "int", "string", NULL } },
+    { "send command", { "string", "iobject", "iobject", NULL } },
+    { "send text", { "string", "iobject", "iobject", NULL } },
+    { "command ", { "string", "iobject", "iobject", NULL } },
+    { "default command", { "string", "iobject", "iobject", NULL } },
+    { "ignore created", { "Irssi::Ignore", NULL } },
+    { "ignore destroyed", { "Irssi::Ignore", NULL } },
+    { "ignore changed", { "Irssi::Ignore", NULL } },
+    { "log new", { "Irssi::Log", NULL } },
+    { "log remove", { "Irssi::Log", NULL } },
+    { "log create failed", { "Irssi::Log", NULL } },
+    { "log locked", { "Irssi::Log", NULL } },
+    { "log started", { "Irssi::Log", NULL } },
+    { "log stopped", { "Irssi::Log", NULL } },
+    { "log rotated", { "Irssi::Log", NULL } },
+    { "log written", { "Irssi::Log", "string", NULL } },
+    { "module loaded", { "Irssi::Module", "MODULE_FILE_REC", NULL } },
+    { "module unloaded", { "Irssi::Module", "MODULE_FILE_REC", NULL } },
+    { "module error", { "int", "string", "string", "string", NULL } },
+    { "nicklist new", { "iobject", "iobject", NULL } },
+    { "nicklist remove", { "iobject", "iobject", NULL } },
+    { "nicklist changed", { "iobject", "iobject", "string", NULL } },
+    { "nicklist host changed", { "iobject", "iobject", NULL } },
+    { "nicklist gone changed", { "iobject", "iobject", NULL } },
+    { "nicklist serverop changed", { "iobject", "iobject", NULL } },
+    { "pidwait", { "int", "int", NULL } },
+    { "query created", { "iobject", "int", NULL } },
+    { "query destroyed", { "iobject", NULL } },
+    { "query nick changed", { "iobject", "string", NULL } },
+    { "query address changed", { "iobject", NULL } },
+    { "query server changed", { "iobject", "iobject", NULL } },
+    { "rawlog", { "RAWIrssi::Log", "string", NULL } },
+    { "server looking", { "iobject", NULL } },
+    { "server connected", { "iobject", NULL } },
+    { "server connecting", { "iobject", "ulongptr", NULL } },
+    { "server connect failed", { "iobject", NULL } },
+    { "server disconnected", { "iobject", NULL } },
+    { "server quit", { "iobject", "string", NULL } },
+    { "setup reread", { "string", NULL } },
+    { "setup saved", { "string", "int", NULL } },
+    { "ban type changed", { "string", NULL } },
+    { "channel joined", { "iobject", NULL } },
+    { "channel wholist", { "iobject", NULL } },
+    { "channel sync", { "iobject", NULL } },
+    { "channel topic changed", { "iobject", NULL } },
+    { "ctcp msg", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp msg ", { "iobject", "string", "string", "string", "string", NULL } },
+    { "default ctcp msg", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp reply", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp reply ", { "iobject", "string", "string", "string", "string", NULL } },
+    { "default ctcp reply", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp action", { "iobject", "string", "string", "string", "string", NULL } },
+    { "awaylog show", { "Irssi::Log", "int", "int", NULL } },
+    { "server nick changed", { "iobject", NULL } },
+    { "event connected", { "iobject", NULL } },
+    { "server event", { "iobject", "string", "string", "string", NULL } },
+    { "event ", { "iobject", "string", "string", "string", NULL } },
+    { "default event", { "iobject", "string", "string", "string", NULL } },
+    { "server incoming", { "iobject", "string", NULL } },
+    { "redir ", { "iobject", "string", "string", "string", NULL } },
+    { "server lag", { "iobject", NULL } },
+    { "server lag disconnect", { "iobject", NULL } },
+    { "massjoin", { "iobject", "gslist_iobject", NULL } },
+    { "ban new", { "iobject", "Irssi::Irc::Ban", NULL } },
+    { "ban remove", { "iobject", "Irssi::Irc::Ban", NULL } },
+    { "channel mode changed", { "iobject", NULL } },
+    { "nick mode changed", { "iobject", "iobject", NULL } },
+    { "user mode changed", { "iobject", "string", NULL } },
+    { "away mode changed", { "iobject", NULL } },
+    { "netsplit server new", { "iobject", "NETSPLIT_iobject", NULL } },
+    { "netsplit server remove", { "iobject", "NETSPLIT_iobject", NULL } },
+    { "netsplit new", { "Irssi::Irc::Netsplit", NULL } },
+    { "netsplit remove", { "Irssi::Irc::Netsplit", NULL } },
+    { "dcc ctcp ", { "string", "siobject", NULL } },
+    { "default dcc ctcp", { "string", "siobject", NULL } },
+    { "dcc unknown ctcp", { "string", "string", "string", NULL } },
+    { "dcc reply ", { "string", "siobject", NULL } },
+    { "default dcc reply", { "string", "siobject", NULL } },
+    { "dcc unknown reply", { "string", "string", "string", NULL } },
+    { "dcc chat message", { "siobject", "string", NULL } },
+    { "dcc created", { "siobject", NULL } },
+    { "dcc destroyed", { "siobject", NULL } },
+    { "dcc connected", { "siobject", NULL } },
+    { "dcc rejecting", { "siobject", NULL } },
+    { "dcc closed", { "siobject", NULL } },
+    { "dcc request", { "siobject", "string", NULL } },
+    { "dcc request send", { "siobject", NULL } },
+    { "dcc chat message", { "siobject", "string", NULL } },
+    { "dcc transfer update", { "siobject", NULL } },
+    { "dcc get receive", { "siobject", NULL } },
+    { "dcc error connect", { "siobject", NULL } },
+    { "dcc error file create", { "siobject", "string", NULL } },
+    { "dcc error file open", { "string", "string", "int", NULL } },
+    { "dcc error get not found", { "string", NULL } },
+    { "dcc error send exists", { "string", "string", NULL } },
+    { "dcc error unknown type", { "string", NULL } },
+    { "dcc error close not found", { "string", "string", "string", NULL } },
+    { "autoignore new", { "iobject", "AUTOIrssi::Ignore", NULL } },
+    { "autoignore remove", { "iobject", "AUTOIrssi::Ignore", NULL } },
+    { "flood", { "iobject", "string", "string", "int", "string", NULL } },
+    { "notifylist new", { "Irssi::Irc::Notifylist", NULL } },
+    { "notifylist remove", { "Irssi::Irc::Notifylist", NULL } },
+    { "notifylist joined", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "notifylist away changed", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "notifylist unidle", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "notifylist left", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "proxy client connected", { "CLIENT_REC", NULL } },
+    { "proxy client disconnected", { "CLIENT_REC", NULL } },
+    { "gui print text", { "Irssi::UI::Window", "int", "int", "int", "string", "int", NULL } },
+    { "gui print text finished", { "Irssi::UI::Window", NULL } },
+    { "complete word", { "glistptr_char*", "Irssi::UI::Window", "string", "string", "intptr", NULL } },
+    { "exec new", { "Irssi::UI::Process", NULL } },
+    { "exec remove", { "Irssi::UI::Process", "int", NULL } },
+    { "exec input", { "Irssi::UI::Process", "string", NULL } },
+    { "message public", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message private", { "iobject", "string", "string", "string", NULL } },
+    { "message own_public", { "iobject", "string", "string", NULL } },
+    { "message own_private", { "iobject", "string", "string", "string", NULL } },
+    { "message join", { "iobject", "string", "string", "string", NULL } },
+    { "message part", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message quit", { "iobject", "string", "string", "string", NULL } },
+    { "message kick", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "message nick", { "iobject", "string", "string", "string", NULL } },
+    { "message own_nick", { "iobject", "string", "string", "string", NULL } },
+    { "message invite", { "iobject", "string", "string", "string", NULL } },
+    { "message topic", { "iobject", "string", "string", "string", "string", NULL } },
+    { "keyinfo created", { "Irssi::UI::Keyinfo", NULL } },
+    { "keyinfo destroyed", { "Irssi::UI::Keyinfo", NULL } },
+    { "print text", { "Irssi::UI::TextDest", "string", "string", NULL } },
+    { "theme created", { "Irssi::UI::Theme", NULL } },
+    { "theme destroyed", { "Irssi::UI::Theme", NULL } },
+    { "window hilight", { "Irssi::UI::Window", NULL } },
+    { "window activity", { "Irssi::UI::Window", "int", NULL } },
+    { "window item hilight", { "iobject", NULL } },
+    { "window item activity", { "iobject", "int", NULL } },
+    { "window item new", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window item remove", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window item changed", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window item server changed", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window created", { "Irssi::UI::Window", NULL } },
+    { "window destroyed", { "Irssi::UI::Window", NULL } },
+    { "window changed", { "Irssi::UI::Window", "Irssi::UI::Window", NULL } },
+    { "window changed automatic", { "Irssi::UI::Window", NULL } },
+    { "window server changed", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window refnum changed", { "Irssi::UI::Window", "int", NULL } },
+    { "window name changed", { "Irssi::UI::Window", NULL } },
+    { "window history changed", { "Irssi::UI::Window", "string", NULL } },
+    { "window level changed", { "Irssi::UI::Window", NULL } },
+    { "message irc op_public", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message irc own_wall", { "iobject", "string", "string", NULL } },
+    { "message irc own_action", { "iobject", "string", "string", NULL } },
+    { "message irc action", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message irc own_notice", { "iobject", "string", "string", NULL } },
+    { "message irc notice", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message irc own_ctcp", { "iobject", "string", "string", "string", NULL } },
+    { "message irc ctcp", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message dcc own", { "siobject", "string", NULL } },
+    { "message dcc own_action", { "siobject", "string", NULL } },
+    { "message dcc own_ctcp", { "siobject", "string", "string", NULL } },
+    { "message dcc", { "siobject", "string", NULL } },
+    { "message dcc action", { "siobject", "string", NULL } },
+    { "message dcc ctcp", { "siobject", "string", "string", NULL } },
+
+    { NULL }
+};
diff --git a/apps/irssi/src/perl/perl-signals.c b/apps/irssi/src/perl/perl-signals.c
new file mode 100644 (file)
index 0000000..eb3a8b4
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ perl-signals.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "servers.h"
+
+#include "perl-core.h"
+#include "perl-common.h"
+#include "perl-signals.h"
+
+typedef struct {
+        PERL_SCRIPT_REC *script;
+       int signal_id;
+       char *signal;
+
+       SV *func;
+       int priority;
+} PERL_SIGNAL_REC;
+
+typedef struct {
+       char *signal;
+       char *args[7];
+} PERL_SIGNAL_ARGS_REC;
+
+#include "perl-signals-list.h"
+
+static GHashTable *signals[3];
+static GHashTable *perl_signal_args_hash;
+static GSList *perl_signal_args_partial;
+
+static PERL_SIGNAL_ARGS_REC *perl_signal_args_find(int signal_id)
+{
+       PERL_SIGNAL_ARGS_REC *rec;
+        GSList *tmp;
+       const char *signame;
+
+       rec = g_hash_table_lookup(perl_signal_args_hash,
+                                 GINT_TO_POINTER(signal_id));
+        if (rec != NULL) return rec;
+
+       /* try to find by name */
+       signame = signal_get_id_str(signal_id);
+       for (tmp = perl_signal_args_partial; tmp != NULL; tmp = tmp->next) {
+               rec = tmp->data;
+
+               if (strncmp(rec->signal, signame, strlen(rec->signal)) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
+                            int signal_id, gconstpointer *args)
+{
+       dSP;
+
+       PERL_SIGNAL_ARGS_REC *rec;
+       SV *sv, *perlarg, *saved_args[SIGNAL_MAX_ARGUMENTS];
+       AV *av;
+        void *arg;
+       int n;
+
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(sp);
+
+       /* push signal argument to perl stack */
+       rec = perl_signal_args_find(signal_id);
+
+        memset(saved_args, 0, sizeof(saved_args));
+       for (n = 0; n < SIGNAL_MAX_ARGUMENTS &&
+                   rec != NULL && rec->args[n] != NULL; n++) {
+               arg = (void *) args[n];
+
+               if (strcmp(rec->args[n], "string") == 0)
+                       perlarg = new_pv(arg);
+               else if (strcmp(rec->args[n], "int") == 0)
+                       perlarg = newSViv(GPOINTER_TO_INT(arg));
+               else if (strcmp(rec->args[n], "ulongptr") == 0)
+                       perlarg = newSViv(*(unsigned long *) arg);
+               else if (strcmp(rec->args[n], "intptr") == 0)
+                       saved_args[n] = perlarg = newRV_noinc(newSViv(*(int *) arg));
+               else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
+                       /* pointer to linked list - push as AV */
+                       GList *tmp, **ptr;
+                        int is_iobject, is_str;
+
+                        is_iobject = strcmp(rec->args[n]+9, "iobject") == 0;
+                        is_str = strcmp(rec->args[n]+9, "char*") == 0;
+                       av = newAV();
+
+                       ptr = arg;
+                       for (tmp = *ptr; tmp != NULL; tmp = tmp->next) {
+                               sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
+                                       is_str ? new_pv(tmp->data) :
+                                       irssi_bless_plain(rec->args[n]+9, tmp->data);
+                               av_push(av, sv);
+                       }
+
+                       saved_args[n] = perlarg = newRV_noinc((SV *) av);
+               } else if (strncmp(rec->args[n], "gslist_", 7) == 0) {
+                       /* linked list - push as AV */
+                       GSList *tmp;
+                       int is_iobject;
+
+                        is_iobject = strcmp(rec->args[n]+7, "iobject") == 0;
+                       av = newAV();
+                       for (tmp = arg; tmp != NULL; tmp = tmp->next) {
+                               sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
+                                       irssi_bless_plain(rec->args[n]+7, tmp->data);
+                               av_push(av, sv);
+                       }
+
+                       perlarg = newRV_noinc((SV *) av);
+               } else if (arg == NULL) {
+                       /* don't bless NULL arguments */
+                       perlarg = newSViv(0);
+               } else if (strcmp(rec->args[n], "iobject") == 0) {
+                       /* "irssi object" - any struct that has
+                          "int type; int chat_type" as it's first
+                          variables (server, channel, ..) */
+                       perlarg = iobject_bless((SERVER_REC *) arg);
+               } else if (strcmp(rec->args[n], "siobject") == 0) {
+                       /* "simple irssi object" - any struct that has
+                          int type; as it's first variable (dcc) */
+                       perlarg = simple_iobject_bless((SERVER_REC *) arg);
+               } else {
+                       /* blessed object */
+                       perlarg = plain_bless(arg, rec->args[n]);
+               }
+               XPUSHs(sv_2mortal(perlarg));
+       }
+
+       PUTBACK;
+       perl_call_sv(func, G_EVAL|G_DISCARD);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+               char *error = g_strdup(SvPV(ERRSV, PL_na));
+               signal_emit("script error", 2, script, error);
+                g_free(error);
+                rec = NULL;
+       }
+
+        /* restore arguments the perl script modified */
+       for (n = 0; n < SIGNAL_MAX_ARGUMENTS &&
+                   rec != NULL && rec->args[n] != NULL; n++) {
+               arg = (void *) args[n];
+
+               if (saved_args[n] == NULL)
+                        continue;
+
+               if (strcmp(rec->args[n], "intptr") == 0) {
+                       int *val = arg;
+                       *val = SvIV(SvRV(saved_args[n]));
+               } else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
+                        GList **ret = arg;
+                       GList *out = NULL;
+                        void *val;
+                       STRLEN len;
+                        int count;
+
+                       av = (AV *) SvRV(saved_args[n]);
+                        count = av_len(av);
+                       while (count-- >= 0) {
+                               sv = av_shift(av);
+                               if (SvPOKp(sv))
+                                       val = g_strdup(SvPV(sv, len));
+                               else
+                                        val = GINT_TO_POINTER(SvIV(sv));
+
+                               out = g_list_append(out, val);
+                       }
+
+                       if (strcmp(rec->args[n]+9, "char*") == 0)
+                                g_list_foreach(*ret, (GFunc) g_free, NULL);
+                       g_list_free(*ret);
+                        *ret = out;
+               }
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+static void sig_func(int priority, gconstpointer *args)
+{
+       GSList **list, *tmp, *next;
+        int signal_id;
+
+        signal_id = signal_get_emitted_id();
+       list = g_hash_table_lookup(signals[priority],
+                                  GINT_TO_POINTER(signal_id));
+       for (tmp = list == NULL ? NULL : *list; tmp != NULL; tmp = next) {
+               PERL_SIGNAL_REC *rec = tmp->data;
+
+                next = tmp->next;
+               perl_call_signal(rec->script, rec->func, signal_id, args);
+               if (signal_is_stopped(signal_id))
+                        break;
+       }
+}
+
+#define SIG_FUNC_DECL(priority, priority_name) \
+static void sig_func_##priority_name(gconstpointer p1, gconstpointer p2, \
+                                    gconstpointer p3, gconstpointer p4, \
+                                    gconstpointer p5, gconstpointer p6) \
+{ \
+       gconstpointer args[6]; \
+        args[0] = p1; args[1] = p2; args[2] = p3; \
+        args[3] = p4; args[4] = p5; args[5] = p6; \
+        sig_func(priority, args); \
+}
+
+SIG_FUNC_DECL(0, first);
+SIG_FUNC_DECL(1, default);
+SIG_FUNC_DECL(2, last);
+
+#define priority_get_func(priority) \
+       (priority == 0 ? sig_func_first : \
+       priority == 1 ? sig_func_default : sig_func_last)
+
+#define perl_signal_get_func(rec) \
+       (priority_get_func((rec)->priority))
+
+static void perl_signal_add_to_int(const char *signal, SV *func,
+                                  int priority, int command)
+{
+        PERL_SCRIPT_REC *script;
+       PERL_SIGNAL_REC *rec;
+       GHashTable *table;
+       GSList **siglist;
+       void *signal_idp;
+
+        g_return_if_fail(signal != NULL);
+        g_return_if_fail(func != NULL);
+        g_return_if_fail(priority >= 0 && priority <= 2);
+
+        script = perl_script_find_package(perl_get_package());
+        g_return_if_fail(script != NULL);
+
+       if (!command && strncmp(signal, "command ", 8) == 0) {
+               /* we used Irssi::signal_add() instead of
+                  Irssi::command_bind() - oh well, allow this.. */
+               command_bind_to(MODULE_NAME, priority, signal+8, -1,
+                               NULL, priority_get_func(priority));
+                command = TRUE;
+       }
+
+       rec = g_new(PERL_SIGNAL_REC, 1);
+        rec->script = script;
+       rec->signal_id = signal_get_uniq_id(signal);
+       rec->signal = g_strdup(signal);
+       rec->func = perl_func_sv_inc(func, perl_get_package());
+       rec->priority = priority;
+
+       table = signals[priority];
+       signal_idp = GINT_TO_POINTER(rec->signal_id);
+
+       siglist = g_hash_table_lookup(table, signal_idp);
+       if (siglist == NULL) {
+               siglist = g_new0(GSList *, 1);
+               g_hash_table_insert(table, signal_idp, siglist);
+
+               if (!command) {
+                       signal_add_to_id(MODULE_NAME, priority, rec->signal_id,
+                                        perl_signal_get_func(rec));
+               }
+       }
+
+       *siglist = g_slist_append(*siglist, rec);
+}
+
+void perl_signal_add_to(const char *signal, SV *func, int priority)
+{
+        perl_signal_add_to_int(signal, func, priority, FALSE);
+}
+
+static void perl_signal_destroy(PERL_SIGNAL_REC *rec)
+{
+       if (strncmp(rec->signal, "command ", 8) == 0)
+               command_unbind(rec->signal+8, perl_signal_get_func(rec));
+
+        SvREFCNT_dec(rec->func);
+       g_free(rec->signal);
+       g_free(rec);
+}
+
+static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec)
+{
+       void *signal_idp;
+
+       g_return_if_fail(rec != NULL);
+
+       signal_idp = GINT_TO_POINTER(rec->signal_id);
+
+       *siglist = g_slist_remove(*siglist, rec);
+       if (*siglist == NULL) {
+               signal_remove_id(rec->signal_id, perl_signal_get_func(rec));
+               g_free(siglist);
+               g_hash_table_remove(signals[rec->priority], signal_idp);
+       }
+
+        perl_signal_destroy(rec);
+}
+
+#define sv_func_cmp(f1, f2, len) \
+       (f1 == f2 || (SvPOK(f1) && SvPOK(f2) && \
+               strcmp((char *) SvPV(f1, len), (char *) SvPV(f2, len)) == 0))
+
+static void perl_signal_remove_list(GSList **list, SV *func)
+{
+       GSList *tmp;
+
+       g_return_if_fail(list != NULL);
+
+       for (tmp = *list; tmp != NULL; tmp = tmp->next) {
+               PERL_SIGNAL_REC *rec = tmp->data;
+
+               if (sv_func_cmp(rec->func, func, PL_na)) {
+                       perl_signal_remove_list_one(list, rec);
+                       break;
+               }
+       }
+}
+
+void perl_signal_remove(const char *signal, SV *func)
+{
+       GSList **list;
+        void *signal_idp;
+       int n;
+
+       signal_idp = GINT_TO_POINTER(signal_get_uniq_id(signal));
+
+        func = perl_func_sv_inc(func, perl_get_package());
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               list = g_hash_table_lookup(signals[n], signal_idp);
+               if (list != NULL)
+                       perl_signal_remove_list(list, func);
+       }
+        SvREFCNT_dec(func);
+}
+
+void perl_command_bind_to(const char *cmd, const char *category,
+                         SV *func, int priority)
+{
+       char *signal;
+
+       command_bind_to(MODULE_NAME, priority, cmd, -1,
+                       category, priority_get_func(priority));
+
+       signal = g_strconcat("command ", cmd, NULL);
+       perl_signal_add_to_int(signal, func, priority, TRUE);
+       g_free(signal);
+}
+
+void perl_command_runsub(const char *cmd, const char *data, 
+                        SERVER_REC *server, WI_ITEM_REC *item)
+{
+       command_runsub(cmd, data, server, item);
+}
+
+void perl_command_unbind(const char *cmd, SV *func)
+{
+       char *signal;
+
+        /* perl_signal_remove() calls command_unbind() */
+       signal = g_strconcat("command ", cmd, NULL);
+       perl_signal_remove(signal, func);
+       g_free(signal);
+}
+
+static int signal_destroy_hash(void *key, GSList **list, PERL_SCRIPT_REC *script)
+{
+       GSList *tmp, *next;
+
+       for (tmp = *list; tmp != NULL; tmp = next) {
+               PERL_SIGNAL_REC *rec = tmp->data;
+
+               next = tmp->next;
+               if (script == NULL || rec->script == script) {
+                       *list = g_slist_remove(*list, rec);
+                       if (*list == NULL) {
+                               signal_remove_id(rec->signal_id,
+                                                perl_signal_get_func(rec));
+                       }
+                       perl_signal_destroy(rec);
+               }
+       }
+
+       if (*list != NULL)
+               return FALSE;
+
+       g_free(list);
+       return TRUE;
+}
+
+/* destroy all signals used by script */
+void perl_signal_remove_script(PERL_SCRIPT_REC *script)
+{
+       int n;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               g_hash_table_foreach_remove(signals[n],
+                                           (GHRFunc) signal_destroy_hash,
+                                           script);
+       }
+}
+
+void perl_signals_start(void)
+{
+       int n;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               signals[n] = g_hash_table_new((GHashFunc) g_direct_hash,
+                                             (GCompareFunc) g_direct_equal);
+       }
+}
+
+void perl_signals_stop(void)
+{
+       int n;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               g_hash_table_foreach(signals[n],
+                                    (GHFunc) signal_destroy_hash, NULL);
+               g_hash_table_destroy(signals[n]);
+                signals[n] = NULL;
+       }
+}
+
+void perl_signals_init(void)
+{
+       int n;
+
+       perl_signal_args_hash = g_hash_table_new((GHashFunc) g_direct_hash,
+                                                (GCompareFunc) g_direct_equal);
+        perl_signal_args_partial = NULL;
+
+       for (n = 0; perl_signal_args[n].signal != NULL; n++) {
+               PERL_SIGNAL_ARGS_REC *rec = &perl_signal_args[n];
+
+               if (rec->signal[strlen(rec->signal)-1] == ' ') {
+                       perl_signal_args_partial =
+                               g_slist_append(perl_signal_args_partial, rec);
+               } else {
+                        int signal_id = signal_get_uniq_id(rec->signal);
+                       g_hash_table_insert(perl_signal_args_hash,
+                                           GINT_TO_POINTER(signal_id),
+                                           rec);
+               }
+       }
+}
+
+void perl_signals_deinit(void)
+{
+        g_slist_free(perl_signal_args_partial);
+        g_hash_table_destroy(perl_signal_args_hash);
+}
diff --git a/apps/irssi/src/perl/perl-signals.h b/apps/irssi/src/perl/perl-signals.h
new file mode 100644 (file)
index 0000000..33e0c1b
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __PERL_SIGNALS_H
+#define __PERL_SIGNALS_H
+
+void perl_signal_add_to(const char *signal, SV *func, int priority);
+#define perl_signal_add_first(signal, func) \
+        perl_signal_add_to(signal, func, 0)
+#define perl_signal_add(signal, func) \
+        perl_signal_add_to(signal, func, 1)
+#define perl_signal_add_last(signal, func) \
+        perl_signal_add_to(signal, func, 2)
+
+void perl_signal_remove(const char *signal, SV *func);
+/* remove all signals used by script */
+void perl_signal_remove_script(PERL_SCRIPT_REC *script);
+
+void perl_command_bind_to(const char *cmd, const char *category,
+                         SV *func, int priority);
+#define perl_command_bind_first(cmd, category, func) \
+        perl_command_bind_to(cmd, category, func, 0)
+#define perl_command_bind(cmd, category, func) \
+        perl_command_bind_to(cmd, category, func, 1)
+#define perl_command_bind_last(cmd, category, func) \
+        perl_command_bind_to(cmd, category, func, 2)
+
+void perl_command_unbind(const char *cmd, SV *func);
+
+void perl_signals_start(void);
+void perl_signals_stop(void);
+
+void perl_signals_init(void);
+void perl_signals_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/perl/perl-sources.c b/apps/irssi/src/perl/perl-sources.c
new file mode 100644 (file)
index 0000000..be1a418
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ perl-sources.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "signals.h"
+
+#include "perl-core.h"
+#include "perl-common.h"
+
+typedef struct {
+        PERL_SCRIPT_REC *script;
+       int tag;
+        int refcount;
+
+       SV *func;
+       SV *data;
+} PERL_SOURCE_REC;
+
+static GSList *perl_sources;
+
+static void perl_source_ref(PERL_SOURCE_REC *rec)
+{
+        rec->refcount++;
+}
+
+static void perl_source_unref(PERL_SOURCE_REC *rec)
+{
+       if (--rec->refcount != 0)
+               return;
+
+        SvREFCNT_dec(rec->data);
+        SvREFCNT_dec(rec->func);
+       g_free(rec);
+}
+
+static void perl_source_destroy(PERL_SOURCE_REC *rec)
+{
+       perl_sources = g_slist_remove(perl_sources, rec);
+
+       g_source_remove(rec->tag);
+       rec->tag = -1;
+
+       perl_source_unref(rec);
+}
+
+static int perl_source_event(PERL_SOURCE_REC *rec)
+{
+       dSP;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+       XPUSHs(sv_mortalcopy(rec->data));
+       PUTBACK;
+
+        perl_source_ref(rec);
+       perl_call_sv(rec->func, G_EVAL|G_DISCARD);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+                char *error = g_strdup(SvPV(ERRSV, PL_na));
+               signal_emit("script error", 2, rec->script, error);
+                g_free(error);
+       }
+        perl_source_unref(rec);
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+
+       return 1;
+}
+
+int perl_timeout_add(int msecs, SV *func, SV *data)
+{
+        PERL_SCRIPT_REC *script;
+       PERL_SOURCE_REC *rec;
+       const char *pkg;
+
+        pkg = perl_get_package();
+       script = perl_script_find_package(pkg);
+        g_return_val_if_fail(script != NULL, -1);
+
+       rec = g_new0(PERL_SOURCE_REC, 1);
+       perl_source_ref(rec);
+
+        rec->script = script;
+       rec->func = perl_func_sv_inc(func, pkg);
+       rec->data = SvREFCNT_inc(data);
+       rec->tag = g_timeout_add(msecs, (GSourceFunc) perl_source_event, rec);
+
+       perl_sources = g_slist_append(perl_sources, rec);
+       return rec->tag;
+}
+
+int perl_input_add(int source, int condition, SV *func, SV *data)
+{
+        PERL_SCRIPT_REC *script;
+       PERL_SOURCE_REC *rec;
+       GIOChannel *channel;
+        const char *pkg;
+
+        pkg = perl_get_package();
+       script = perl_script_find_package(pkg);
+        g_return_val_if_fail(script != NULL, -1);
+
+       rec = g_new0(PERL_SOURCE_REC, 1);
+       perl_source_ref(rec);
+
+        rec->script =script;
+       rec->func = perl_func_sv_inc(func, pkg);
+       rec->data = SvREFCNT_inc(data);
+
+       channel = g_io_channel_unix_new(source);
+       rec->tag = g_input_add(channel, condition,
+                              (GInputFunction) perl_source_event, rec);
+       g_io_channel_unref(channel);
+
+       perl_sources = g_slist_append(perl_sources, rec);
+       return rec->tag;
+}
+
+void perl_source_remove(int tag)
+{
+       GSList *tmp;
+
+       for (tmp = perl_sources; tmp != NULL; tmp = tmp->next) {
+               PERL_SOURCE_REC *rec = tmp->data;
+
+               if (rec->tag == tag) {
+                       perl_source_destroy(rec);
+                       break;
+               }
+       }
+}
+
+void perl_source_remove_script(PERL_SCRIPT_REC *script)
+{
+       GSList *tmp, *next;
+
+       for (tmp = perl_sources; tmp != NULL; tmp = next) {
+               PERL_SOURCE_REC *rec = tmp->data;
+
+               next = tmp->next;
+                if (rec->script == script)
+                       perl_source_destroy(rec);
+       }
+}
+
+void perl_sources_start(void)
+{
+       perl_sources = NULL;
+}
+
+void perl_sources_stop(void)
+{
+       /* timeouts and input waits */
+       while (perl_sources != NULL)
+               perl_source_destroy(perl_sources->data);
+}
diff --git a/apps/irssi/src/perl/perl-sources.h b/apps/irssi/src/perl/perl-sources.h
new file mode 100644 (file)
index 0000000..db16568
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __PERL_SOURCES_H
+#define __PERL_SOURCES_H
+
+int perl_timeout_add(int msecs, SV *func, SV *data);
+int perl_input_add(int source, int condition, SV *func, SV *data);
+
+void perl_source_remove(int tag);
+/* remove all sources used by script */
+void perl_source_remove_script(PERL_SCRIPT_REC *script);
+
+void perl_sources_start(void);
+void perl_sources_stop(void);
+
+#endif
diff --git a/apps/irssi/src/perl/textui/.cvsignore b/apps/irssi/src/perl/textui/.cvsignore
new file mode 100644 (file)
index 0000000..517db94
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.PL
+TextUI.bs
+*.c
+*.o
+pm_to_blib
+blib
diff --git a/apps/irssi/src/perl/textui/Makefile.PL.in b/apps/irssi/src/perl/textui/Makefile.PL.in
new file mode 100644 (file)
index 0000000..9e80274
--- /dev/null
@@ -0,0 +1,8 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile('NAME' => 'Irssi::TextUI',
+              'LIBS' => '',
+             'OBJECT' => '$(O_FILES)',
+              'TYPEMAPS' => ['../common/typemap', '../ui/typemap'],
+              'INC' => '-I../../.. -I@top_srcdir@/src -I@top_srcdir@/src/core -I@top_srcdir@/src/fe-common/core -I@top_srcdir@/src/fe-text @GLIB_CFLAGS@',
+             'VERSION_FROM' => '@srcdir@/TextUI.pm');
diff --git a/apps/irssi/src/perl/textui/Statusbar.xs b/apps/irssi/src/perl/textui/Statusbar.xs
new file mode 100644 (file)
index 0000000..67b88e4
--- /dev/null
@@ -0,0 +1,164 @@
+#include "module.h"
+
+static GHashTable *perl_sbar_defs;
+
+static int check_sbar_destroy(char *key, char *value, char *script)
+{
+       if (strncmp(value, script, strlen(script)) == 0 &&
+           value[strlen(script)] == ':') {
+                statusbar_item_unregister(key);
+               g_free(key);
+                g_free(value);
+               return TRUE;
+       }
+
+        return FALSE;
+}
+
+static void script_unregister_statusbars(PERL_SCRIPT_REC *script)
+{
+       g_hash_table_foreach_remove(perl_sbar_defs,
+                                   (GHRFunc) check_sbar_destroy,
+                                   script->package);
+}
+
+void perl_statusbar_init(void)
+{
+       perl_sbar_defs = g_hash_table_new((GHashFunc) g_str_hash,
+                                         (GCompareFunc) g_str_equal);
+       signal_add("script destroyed", (SIGNAL_FUNC) script_unregister_statusbars);
+}
+
+static void statusbar_item_def_destroy(void *key, void *value)
+{
+       g_free(key);
+        g_free(value);
+}
+
+void perl_statusbar_deinit(void)
+{
+       signal_remove("script destroyed", (SIGNAL_FUNC) script_unregister_statusbars);
+
+       g_hash_table_foreach(perl_sbar_defs,
+                            (GHFunc) statusbar_item_def_destroy, NULL);
+       g_hash_table_destroy(perl_sbar_defs);
+}
+
+static void perl_statusbar_event(char *function, SBAR_ITEM_REC *item,
+                                int get_size_only)
+{
+       dSP;
+       int retcount;
+       SV *item_sv, **sv;
+        HV *hv;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+        item_sv = plain_bless(item, "Irssi::TextUI::StatusbarItem");
+       XPUSHs(sv_2mortal(item_sv));
+       XPUSHs(sv_2mortal(newSViv(get_size_only)));
+       PUTBACK;
+
+       retcount = perl_call_pv(function, G_EVAL|G_DISCARD);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+                PERL_SCRIPT_REC *script;
+                char *package;
+
+                package = perl_function_get_package(function);
+                script = perl_script_find_package(package);
+                g_free(package);
+
+               if (script != NULL) {
+                        /* make sure we don't get back here */
+                       script_unregister_statusbars(script);
+               }
+               signal_emit("script error", 2, script, SvPV(ERRSV, PL_na));
+       } else {
+               /* min_size and max_size can be changed, move them to SBAR_ITEM_REC */
+               hv = hvref(item_sv);
+               if (hv != NULL) {
+                       sv = hv_fetch(hv, "min_size", 8, 0);
+                       if (sv != NULL) item->min_size = SvIV(*sv);
+                       sv = hv_fetch(hv, "max_size", 8, 0);
+                       if (sv != NULL) item->max_size = SvIV(*sv);
+               }
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+
+static void sig_perl_statusbar(SBAR_ITEM_REC *item, int get_size_only)
+{
+       char *function;
+
+       function = g_hash_table_lookup(perl_sbar_defs, item->config->name);
+       if (function != NULL)
+               perl_statusbar_event(function, item, get_size_only);
+       else {
+               /* use default function - this shouldn't actually happen.. */
+               statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE);
+       }
+}
+
+MODULE = Irssi::TextUI::Statusbar  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+statusbar_item_register(name, value, func = NULL)
+       char *name
+       char *value
+       char *func
+CODE:
+       statusbar_item_register(name, value, func == NULL || *func == '\0' ? NULL : sig_perl_statusbar);
+       if (func != NULL) {
+               g_hash_table_insert(perl_sbar_defs, g_strdup(name),
+                                   g_strdup_printf("%s::%s", perl_get_package(), func));
+       }
+
+void
+statusbar_item_unregister(name)
+       char *name
+PREINIT:
+        gpointer key, value;
+CODE:
+       if (g_hash_table_lookup_extended(perl_sbar_defs, name, &key, &value)) {
+                g_hash_table_remove(perl_sbar_defs, name);
+               g_free(key);
+                g_free(value);
+       }
+       statusbar_item_unregister(name);
+
+void
+statusbar_items_redraw(name)
+       char *name
+
+void
+statusbars_recreate_items()
+
+#*******************************
+MODULE = Irssi::TextUI::Statusbar  PACKAGE = Irssi::TextUI::StatusbarItem  PREFIX = statusbar_item_
+#*******************************
+
+void
+statusbar_item_default_handler(item, get_size_only, str, data, escape_vars = TRUE)
+       Irssi::TextUI::StatusbarItem item
+       int get_size_only
+       char *str
+       char *data
+       int escape_vars
+PREINIT:
+       HV *hv;
+CODE:
+       statusbar_item_default_handler(item, get_size_only,
+                                      *str == '\0' ? NULL : str,
+                                      data, escape_vars);
+       hv = hvref(ST(0));
+       hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+       hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
diff --git a/apps/irssi/src/perl/textui/TextBuffer.xs b/apps/irssi/src/perl/textui/TextBuffer.xs
new file mode 100644 (file)
index 0000000..4fb92f1
--- /dev/null
@@ -0,0 +1,83 @@
+#include "module.h"
+
+MODULE = Irssi::TextUI::TextBuffer  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+Irssi::TextUI::TextBuffer
+textbuffer_create()
+
+#*******************************
+MODULE = Irssi::TextUI::TextBuffer  PACKAGE = Irssi::TextUI::TextBuffer  PREFIX = textbuffer_
+#*******************************
+
+void
+textbuffer_destroy(buffer)
+       Irssi::TextUI::TextBuffer buffer
+
+Irssi::TextUI::Line
+textbuffer_append(buffer, data, len, info)
+       Irssi::TextUI::TextBuffer buffer
+       char *data
+       int len
+       Irssi::TextUI::LineInfo info
+
+Irssi::TextUI::Line
+textbuffer_insert(buffer, insert_after, data, len, info)
+       Irssi::TextUI::TextBuffer buffer
+       Irssi::TextUI::Line insert_after
+       char *data
+       int len
+       Irssi::TextUI::LineInfo info
+
+void
+textbuffer_remove(buffer, line)
+       Irssi::TextUI::TextBuffer buffer
+       Irssi::TextUI::Line line
+
+void
+textbuffer_remove_all_lines(buffer)
+       Irssi::TextUI::TextBuffer buffer
+
+#*******************************
+MODULE = Irssi::TextUI::TextBuffer  PACKAGE = Irssi::TextUI::Line  PREFIX = textbuffer_line_
+#*******************************
+
+Irssi::TextUI::Line
+textbuffer_line_prev(line)
+       Irssi::TextUI::Line line
+CODE:
+       RETVAL = line->prev;
+OUTPUT:
+       RETVAL
+
+Irssi::TextUI::Line
+textbuffer_line_next(line)
+       Irssi::TextUI::Line line
+CODE:
+       RETVAL = line->next;
+OUTPUT:
+       RETVAL
+
+void
+textbuffer_line_ref(line)
+       Irssi::TextUI::Line line
+
+void
+textbuffer_line_unref(line, buffer)
+       Irssi::TextUI::Line line
+       Irssi::TextUI::TextBuffer buffer
+CODE:
+       textbuffer_line_unref(buffer, line);
+
+void
+textbuffer_line_get_text(line, coloring)
+       Irssi::TextUI::Line line
+       int coloring
+PREINIT:
+       GString *str;
+PPCODE:
+       str = g_string_new(NULL);
+       textbuffer_line2text(line, coloring, str);
+       XPUSHs(sv_2mortal(new_pv(str->str)));
+       g_string_free(str, TRUE);
+
diff --git a/apps/irssi/src/perl/textui/TextBufferView.xs b/apps/irssi/src/perl/textui/TextBufferView.xs
new file mode 100644 (file)
index 0000000..5b9b1cc
--- /dev/null
@@ -0,0 +1,108 @@
+#include "module.h"
+
+MODULE = Irssi::TextUI::TextBufferView  PACKAGE = Irssi::TextUI::TextBuffer  PREFIX = textbuffer_
+PROTOTYPES: ENABLE
+
+Irssi::TextUI::TextBufferView
+textbuffer_view_create(buffer, width, height, scroll, utf8)
+       Irssi::TextUI::TextBuffer buffer
+       int width
+       int height
+       int scroll
+       int utf8
+
+#*******************************
+MODULE = Irssi::TextUI::TextBufferView  PACKAGE = Irssi::TextUI::TextBufferView  PREFIX = textbuffer_view_
+#*******************************
+
+void
+textbuffer_view_destroy(view)
+       Irssi::TextUI::TextBufferView view
+
+void
+textbuffer_view_set_default_indent(view, default_indent, longword_noindent)
+       Irssi::TextUI::TextBufferView view
+       int default_indent
+       int longword_noindent
+CODE:
+       textbuffer_view_set_default_indent(view, default_indent, longword_noindent, NULL);
+
+void
+textbuffer_view_set_scroll(view, scroll)
+       Irssi::TextUI::TextBufferView view
+       int scroll
+
+void
+textbuffer_view_resize(view, width, height)
+       Irssi::TextUI::TextBufferView view
+       int width
+       int height
+
+void
+textbuffer_view_clear(view)
+       Irssi::TextUI::TextBufferView view
+
+Irssi::TextUI::Line
+textbuffer_view_get_lines(view)
+       Irssi::TextUI::TextBufferView view
+
+void
+textbuffer_view_scroll(view, lines)
+       Irssi::TextUI::TextBufferView view
+       int lines
+
+void
+textbuffer_view_scroll_line(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+Irssi::TextUI::LineCache
+textbuffer_view_get_line_cache(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_insert_line(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_remove_line(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_remove_all_lines(view)
+       Irssi::TextUI::TextBufferView view
+
+void
+textbuffer_view_set_bookmark(view, name, line)
+       Irssi::TextUI::TextBufferView view
+       char *name
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_set_bookmark_bottom(view, name)
+       Irssi::TextUI::TextBufferView view
+       char *name
+
+Irssi::TextUI::Line
+textbuffer_view_get_bookmark(view, name)
+       Irssi::TextUI::TextBufferView view
+       char *name
+
+void
+textbuffer_view_redraw(view)
+       Irssi::TextUI::TextBufferView view
+
+#*******************************
+MODULE = Irssi::TextUI::TextBufferView  PACKAGE = Irssi::UI::Window
+#*******************************
+
+Irssi::TextUI::TextBufferView
+view(window)
+       Irssi::UI::Window window
+CODE:
+       RETVAL = WINDOW_GUI(window)->view;
+OUTPUT:
+       RETVAL
diff --git a/apps/irssi/src/perl/textui/TextUI.pm b/apps/irssi/src/perl/textui/TextUI.pm
new file mode 100644 (file)
index 0000000..50f247c
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Perl interface to irssi functions.
+#
+
+package Irssi::TextUI;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+$VERSION = "0.9";
+
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw();
+@EXPORT_OK = qw();
+
+bootstrap Irssi::TextUI $VERSION if (!Irssi::Core::is_static());
+
+Irssi::TextUI::init();
+
+Irssi::EXPORT_ALL();
+
+1;
+
diff --git a/apps/irssi/src/perl/textui/TextUI.xs b/apps/irssi/src/perl/textui/TextUI.xs
new file mode 100644 (file)
index 0000000..8d49ac4
--- /dev/null
@@ -0,0 +1,160 @@
+#include "module.h"
+
+static int initialized = FALSE;
+
+static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window)
+{
+       hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0);
+
+       hv_store(hv, "first_line", 10, newSViv(window->first_line), 0);
+       hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+       hv_store(hv, "width", 5, newSViv(window->width), 0);
+       hv_store(hv, "height", 6, newSViv(window->height), 0);
+
+       hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 0);
+}
+
+static void perl_text_buffer_fill_hash(HV *hv, TEXT_BUFFER_REC *buffer)
+{
+       hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0);
+       hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0);
+}
+
+static void perl_text_buffer_view_fill_hash(HV *hv, TEXT_BUFFER_VIEW_REC *view)
+{
+       hv_store(hv, "buffer", 6, plain_bless(view->buffer, "Irssi::TextUI::TextBuffer"), 0);
+       hv_store(hv, "width", 5, newSViv(view->width), 0);
+       hv_store(hv, "height", 6, newSViv(view->height), 0);
+
+       hv_store(hv, "default_indent", 14, newSViv(view->default_indent), 0);
+       hv_store(hv, "longword_noindent", 17, newSViv(view->longword_noindent), 0);
+       hv_store(hv, "scroll", 6, newSViv(view->scroll), 0);
+
+       hv_store(hv, "ypos", 4, newSViv(view->ypos), 0);
+
+       hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "subline", 7, newSViv(view->subline), 0);
+
+       hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0);
+
+       hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0);
+       hv_store(hv, "bottom", 6, newSViv(view->bottom), 0);
+}
+
+static void perl_line_fill_hash(HV *hv, LINE_REC *line)
+{
+       hv_store(hv, "refcount", 8, newSViv(line->refcount), 0);
+       hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0);
+}
+
+static void perl_line_cache_fill_hash(HV *hv, LINE_CACHE_REC *cache)
+{
+       hv_store(hv, "last_access", 11, newSViv(cache->last_access), 0);
+       hv_store(hv, "count", 5, newSViv(cache->count), 0);
+       /*LINE_CACHE_SUB_REC lines[1];*/
+}
+
+static void perl_line_info_fill_hash(HV *hv, LINE_INFO_REC *info)
+{
+       hv_store(hv, "level", 5, newSViv(info->level), 0);
+       hv_store(hv, "time", 4, newSViv(info->time), 0);
+}
+
+static void perl_statusbar_item_fill_hash(HV *hv, SBAR_ITEM_REC *item)
+{
+       hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+       hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
+       hv_store(hv, "xpos", 4, newSViv(item->xpos), 0);
+       hv_store(hv, "size", 4, newSViv(item->size), 0);
+}
+
+static PLAIN_OBJECT_INIT_REC textui_plains[] = {
+       { "Irssi::TextUI::MainWindow", (PERL_OBJECT_FUNC) perl_main_window_fill_hash },
+       { "Irssi::TextUI::TextBuffer", (PERL_OBJECT_FUNC) perl_text_buffer_fill_hash },
+       { "Irssi::TextUI::TextBufferView", (PERL_OBJECT_FUNC) perl_text_buffer_view_fill_hash },
+       { "Irssi::TextUI::Line", (PERL_OBJECT_FUNC) perl_line_fill_hash },
+       { "Irssi::TextUI::LineCache", (PERL_OBJECT_FUNC) perl_line_cache_fill_hash },
+       { "Irssi::TextUI::LineInfo", (PERL_OBJECT_FUNC) perl_line_info_fill_hash },
+       { "Irssi::TextUI::StatusbarItem", (PERL_OBJECT_FUNC) perl_statusbar_item_fill_hash },
+
+       { NULL, NULL }
+};
+
+MODULE = Irssi::TextUI  PACKAGE = Irssi::TextUI
+
+PROTOTYPES: ENABLE
+
+void
+init()
+CODE:
+       if (initialized) return;
+       perl_api_version_check("Irssi::TextUI");
+       initialized = TRUE;
+
+        irssi_add_plains(textui_plains);
+        perl_statusbar_init();
+
+void
+deinit()
+CODE:
+       if (!initialized) return;
+        perl_statusbar_deinit();
+
+MODULE = Irssi::TextUI PACKAGE = Irssi
+
+void
+gui_printtext(xpos, ypos, str)
+       int xpos
+       int ypos
+       char *str
+
+MODULE = Irssi::TextUI PACKAGE = Irssi::UI::Window
+
+void
+gui_printtext_after(window, prev, level, str)
+       Irssi::UI::Window window
+       Irssi::TextUI::Line prev
+       int level
+       char *str
+PREINIT:
+       TEXT_DEST_REC dest;
+CODE:
+       format_create_dest(&dest, NULL, NULL, level, window);
+       gui_printtext_after(&dest, prev, str);
+
+Irssi::TextUI::Line
+last_line_insert(window)
+       Irssi::UI::Window window
+CODE:
+       RETVAL = WINDOW_GUI(window)->insert_after;
+OUTPUT:
+       RETVAL
+
+MODULE = Irssi::TextUI PACKAGE = Irssi::UI::Server
+
+void
+gui_printtext_after(server, target, prev, level, str)
+       Irssi::Server server
+       char *target
+       Irssi::TextUI::Line prev
+       int level
+       char *str
+PREINIT:
+       TEXT_DEST_REC dest;
+CODE:
+       format_create_dest(&dest, server, target, level, NULL);
+       gui_printtext_after(&dest, prev, str);
+
+BOOT:
+       irssi_boot(TextUI__Statusbar);
+       irssi_boot(TextUI__TextBuffer);
+       irssi_boot(TextUI__TextBufferView);
+
+void
+term_refresh_freeze()
+
+void
+term_refresh_thaw()
diff --git a/apps/irssi/src/perl/textui/module.h b/apps/irssi/src/perl/textui/module.h
new file mode 100644 (file)
index 0000000..9123afb
--- /dev/null
@@ -0,0 +1,16 @@
+#include "../ui/module.h"
+
+#include "mainwindows.h"
+#include "gui-windows.h"
+#include "gui-printtext.h"
+#include "statusbar.h"
+#include "textbuffer.h"
+#include "textbuffer-view.h"
+
+typedef MAIN_WINDOW_REC *Irssi__TextUI__MainWindow;
+typedef TEXT_BUFFER_REC *Irssi__TextUI__TextBuffer;
+typedef TEXT_BUFFER_VIEW_REC *Irssi__TextUI__TextBufferView;
+typedef LINE_REC *Irssi__TextUI__Line;
+typedef LINE_CACHE_REC *Irssi__TextUI__LineCache;
+typedef LINE_INFO_REC *Irssi__TextUI__LineInfo;
+typedef SBAR_ITEM_REC *Irssi__TextUI__StatusbarItem;
diff --git a/apps/irssi/src/perl/textui/typemap b/apps/irssi/src/perl/textui/typemap
new file mode 100644 (file)
index 0000000..364cdf3
--- /dev/null
@@ -0,0 +1,19 @@
+TYPEMAP
+Irssi::TextUI::MainWindow      T_PlainObj
+Irssi::TextUI::TextBuffer      T_PlainObj
+Irssi::TextUI::TextBufferView  T_PlainObj
+Irssi::TextUI::Line            T_PlainObj
+Irssi::TextUI::LineCache       T_PlainObj
+Irssi::TextUI::LineInfo                T_PlainObj
+Irssi::TextUI::StatusbarItem           T_PlainObj
+
+INPUT
+
+T_PlainObj
+       $var = irssi_ref_object($arg)
+
+OUTPUT
+
+T_PlainObj
+       $arg = plain_bless($var, \"$type\");
+
diff --git a/apps/irssi/src/perl/ui/.cvsignore b/apps/irssi/src/perl/ui/.cvsignore
new file mode 100644 (file)
index 0000000..335ef88
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.PL
+UI.bs
+*.c
+*.o
+pm_to_blib
+blib
diff --git a/apps/irssi/src/perl/ui/Formats.xs b/apps/irssi/src/perl/ui/Formats.xs
new file mode 100644 (file)
index 0000000..0ad8c86
--- /dev/null
@@ -0,0 +1,27 @@
+#include "module.h"
+
+MODULE = Irssi::UI::Formats  PACKAGE = Irssi::UI::Window
+PROTOTYPES: ENABLE
+
+void
+format_get_text(window, module, server, target, formatnum, ...)
+       Irssi::UI::Window window
+       char *module
+       Irssi::Server server
+       char *target
+       int formatnum
+PREINIT:
+       char **charargs;
+       char *ret;
+       int n;
+PPCODE:
+       charargs = g_new0(char *, items-5+1);
+       charargs[items-5] = NULL;
+        for (n = 5; n < items; n++) {
+               charargs[n-5] = (char *)SvPV(ST(n), PL_na);
+       }
+       ret = format_get_text(module, window, server, target, formatnum, charargs);
+       g_free(charargs);
+
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
diff --git a/apps/irssi/src/perl/ui/Makefile.PL.in b/apps/irssi/src/perl/ui/Makefile.PL.in
new file mode 100644 (file)
index 0000000..a349918
--- /dev/null
@@ -0,0 +1,8 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile('NAME' => 'Irssi::UI',
+              'LIBS' => '',
+             'OBJECT' => '$(O_FILES)',
+              'TYPEMAPS' => ['../common/typemap'],
+              'INC' => '-I../../.. -I@top_srcdir@/src -I@top_srcdir@/src/core -I@top_srcdir@/src/fe-common/core @GLIB_CFLAGS@',
+             'VERSION_FROM' => '@srcdir@/UI.pm');
diff --git a/apps/irssi/src/perl/ui/Themes.xs b/apps/irssi/src/perl/ui/Themes.xs
new file mode 100644 (file)
index 0000000..fc3165e
--- /dev/null
@@ -0,0 +1,225 @@
+#include "module.h"
+
+void printformat_perl(TEXT_DEST_REC *dest, char *format, char **arglist)
+{
+       THEME_REC *theme;
+       char *module, *str;
+       int formatnum;
+
+       module = g_strdup(perl_get_package());
+       formatnum = format_find_tag(module, format);
+       if (formatnum < 0) {
+               die("printformat(): unregistered format '%s'", format);
+                g_free(module);
+               return;
+       }
+
+       theme = dest->window->theme == NULL ? current_theme :
+               dest->window->theme;
+       signal_emit("print format", 5, theme, module,
+                   dest, GINT_TO_POINTER(formatnum), arglist);
+
+        str = format_get_text_theme_charargs(theme, module, dest, formatnum, arglist);
+       if (*str != '\0') printtext_dest(dest, "%s", str);
+       g_free(str);
+       g_free(module);
+}
+
+static void perl_unregister_theme(const char *package)
+{
+       FORMAT_REC *formats;
+       int n;
+
+       formats = g_hash_table_lookup(default_formats, package);
+       if (formats == NULL) return;
+
+       for (n = 0; formats[n].def != NULL; n++) {
+               g_free(formats[n].tag);
+               g_free(formats[n].def);
+       }
+       g_free(formats);
+       theme_unregister_module(package);
+}
+
+static void sig_script_destroyed(PERL_SCRIPT_REC *script)
+{
+       perl_unregister_theme(script->package);
+}
+
+void perl_themes_init(void)
+{
+       signal_add("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+}
+
+void perl_themes_deinit(void)
+{
+       signal_remove("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+}
+
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+Irssi::UI::Theme
+current_theme()
+CODE:
+       RETVAL = current_theme;
+OUTPUT:
+       RETVAL
+
+int
+EXPAND_FLAG_IGNORE_REPLACES()
+CODE:
+       RETVAL = EXPAND_FLAG_IGNORE_REPLACES;
+OUTPUT:
+       RETVAL
+
+int
+EXPAND_FLAG_IGNORE_EMPTY()
+CODE:
+       RETVAL = EXPAND_FLAG_IGNORE_EMPTY;
+OUTPUT:
+       RETVAL
+
+int
+EXPAND_FLAG_RECURSIVE_MASK()
+CODE:
+       RETVAL = EXPAND_FLAG_RECURSIVE_MASK;
+OUTPUT:
+       RETVAL
+
+void
+theme_register(formats)
+       SV *formats
+PREINIT:
+       AV *av;
+       FORMAT_REC *formatrecs;
+       char *key, *value;
+       int len, n, fpos;
+CODE:
+
+        if (!SvROK(formats))
+               croak("formats is not a reference to list");
+       av = (AV *) SvRV(formats);
+       len = av_len(av)+1;
+       if (len == 0 || (len & 1) != 0)
+               croak("formats list is invalid - not divisible by 2 (%d)", len);
+
+       formatrecs = g_new0(FORMAT_REC, len/2+2);
+       formatrecs[0].tag = g_strdup(perl_get_package());
+       formatrecs[0].def = g_strdup("Perl script");
+
+        for (fpos = 1, n = 0; n < len; n++, fpos++) {
+               key = SvPV(*av_fetch(av, n, 0), PL_na); n++;
+               value = SvPV(*av_fetch(av, n, 0), PL_na);
+
+               formatrecs[fpos].tag = g_strdup(key);
+               formatrecs[fpos].def = g_strdup(value);
+               formatrecs[fpos].params = MAX_FORMAT_PARAMS;
+       }
+
+       theme_register_module(perl_get_package(), formatrecs);
+
+void
+printformat(level, format, ...)
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, NULL, NULL, level, NULL);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 2; n < items && n < MAX_FORMAT_PARAMS+2; n++) {
+               arglist[n-2] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::Server
+#*******************************
+
+void
+printformat(server, target, level, format, ...)
+       Irssi::Server server
+       char *target
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, server, target, level, NULL);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 4; n < items && n < MAX_FORMAT_PARAMS+4; n++) {
+               arglist[n-4] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::UI::Window
+#*******************************
+
+void
+printformat(window, level, format, ...)
+       Irssi::UI::Window window
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, NULL, NULL, level, window);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 3; n < items && n < MAX_FORMAT_PARAMS+3; n++) {
+               arglist[n-3] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::Windowitem
+#*******************************
+
+void
+printformat(item, level, format, ...)
+       Irssi::Windowitem item
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, item->server, item->name, level, NULL);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 3; n < items && n < MAX_FORMAT_PARAMS+3; n++) {
+               arglist[n-3] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::UI::Theme  PREFIX = theme_
+#*******************************
+
+void
+theme_format_expand(theme, format, flags=0)
+       Irssi::UI::Theme theme
+       char *format
+        int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       if (flags == 0) {
+               ret = theme_format_expand(theme, format);
+       } else {
+               ret = theme_format_expand_data(theme, (const char **) &format, 'n', 'n',
+                                              NULL, NULL, EXPAND_FLAG_ROOT | flags);
+       }
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
diff --git a/apps/irssi/src/perl/ui/UI.pm b/apps/irssi/src/perl/ui/UI.pm
new file mode 100644 (file)
index 0000000..5bab7b6
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Perl interface to irssi functions.
+#
+
+package Irssi::UI;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+$VERSION = "0.9";
+
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw();
+@EXPORT_OK = qw();
+
+bootstrap Irssi::UI $VERSION if (!Irssi::Core::is_static());
+
+Irssi::UI::init();
+
+Irssi::EXPORT_ALL();
+
+1;
diff --git a/apps/irssi/src/perl/ui/UI.xs b/apps/irssi/src/perl/ui/UI.xs
new file mode 100644 (file)
index 0000000..83c2a36
--- /dev/null
@@ -0,0 +1,103 @@
+#include "module.h"
+
+static int initialized = FALSE;
+
+static void perl_process_fill_hash(HV *hv, PROCESS_REC *process)
+{
+       hv_store(hv, "id", 2, newSViv(process->id), 0);
+       hv_store(hv, "name", 4, new_pv(process->name), 0);
+       hv_store(hv, "args", 4, new_pv(process->args), 0);
+
+       hv_store(hv, "pid", 3, newSViv(process->pid), 0);
+       hv_store(hv, "target", 6, new_pv(process->target), 0);
+       if (process->target_win != NULL) {
+               hv_store(hv, "target_win", 10,
+                        plain_bless(process->target_win, "Irssi::UI::Window"), 0);
+       }
+       hv_store(hv, "shell", 5, newSViv(process->shell), 0);
+       hv_store(hv, "notice", 6, newSViv(process->notice), 0);
+       hv_store(hv, "silent", 6, newSViv(process->silent), 0);
+}
+
+static void perl_window_fill_hash(HV *hv, WINDOW_REC *window)
+{
+       hv_store(hv, "refnum", 6, newSViv(window->refnum), 0);
+       hv_store(hv, "name", 4, new_pv(window->name), 0);
+       hv_store(hv, "history_name", 12, new_pv(window->history_name), 0);
+
+       hv_store(hv, "width", 5, newSViv(window->width), 0);
+       hv_store(hv, "height", 6, newSViv(window->height), 0);
+
+       if (window->active)
+               hv_store(hv, "active", 6, iobject_bless(window->active), 0);
+       if (window->active_server)
+               hv_store(hv, "active_server", 13, iobject_bless(window->active_server), 0);
+
+       hv_store(hv, "servertag", 9, new_pv(window->servertag), 0);
+       hv_store(hv, "level", 5, newSViv(window->level), 0);
+
+       hv_store(hv, "sticky_refnum", 13, newSViv(window->sticky_refnum), 0);
+
+       hv_store(hv, "data_level", 10, newSViv(window->data_level), 0);
+       hv_store(hv, "hilight_color", 13, new_pv(window->hilight_color), 0);
+
+       hv_store(hv, "last_timestamp", 14, newSViv(window->last_timestamp), 0);
+       hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+
+       hv_store(hv, "theme", 5, plain_bless(window->theme, "Irssi::UI::Theme"), 0);
+       hv_store(hv, "theme_name", 10, new_pv(window->theme_name), 0);
+}
+
+static void perl_text_dest_fill_hash(HV *hv, TEXT_DEST_REC *dest)
+{
+       hv_store(hv, "window", 6, plain_bless(dest->window, "Irssi::UI::Window"), 0);
+       hv_store(hv, "server", 6, iobject_bless(dest->server), 0);
+       hv_store(hv, "target", 6, new_pv(dest->target), 0);
+       hv_store(hv, "level", 5, newSViv(dest->level), 0);
+
+       hv_store(hv, "hilight_priority", 16, newSViv(dest->hilight_priority), 0);
+       hv_store(hv, "hilight_color", 13, new_pv(dest->hilight_color), 0);
+}
+
+static PLAIN_OBJECT_INIT_REC fe_plains[] = {
+       { "Irssi::UI::Process", (PERL_OBJECT_FUNC) perl_process_fill_hash },
+       { "Irssi::UI::Window", (PERL_OBJECT_FUNC) perl_window_fill_hash },
+       { "Irssi::UI::TextDest", (PERL_OBJECT_FUNC) perl_text_dest_fill_hash },
+
+       { NULL, NULL }
+};
+
+MODULE = Irssi::UI  PACKAGE = Irssi::UI
+
+PROTOTYPES: ENABLE
+
+void
+processes()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::UI::Process")));
+       }
+
+
+void
+init()
+CODE:
+       if (initialized) return;
+       perl_api_version_check("Irssi::UI");
+       initialized = TRUE;
+
+        irssi_add_plains(fe_plains);
+        perl_themes_init();
+
+void
+deinit()
+CODE:
+       if (!initialized) return;
+        perl_themes_deinit();
+
+BOOT:
+       irssi_boot(UI__Formats);
+       irssi_boot(UI__Themes);
+       irssi_boot(UI__Window);
diff --git a/apps/irssi/src/perl/ui/Window.xs b/apps/irssi/src/perl/ui/Window.xs
new file mode 100644 (file)
index 0000000..d618378
--- /dev/null
@@ -0,0 +1,270 @@
+#include "module.h"
+
+MODULE = Irssi::UI::Window  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+windows()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::UI::Window")));
+       }
+
+
+Irssi::UI::Window
+active_win()
+CODE:
+       RETVAL = active_win;
+OUTPUT:
+       RETVAL
+
+Irssi::Server
+active_server()
+CODE:
+       RETVAL = active_win->active_server;
+OUTPUT:
+       RETVAL
+
+void
+print(str, level=MSGLEVEL_CLIENTNOTICE)
+       char *str
+        int level;
+CODE:
+       printtext_string(NULL, NULL, level, str);
+
+Irssi::UI::Window
+window_find_name(name)
+       char *name
+
+Irssi::UI::Window
+window_find_refnum(refnum)
+       int refnum
+
+int
+window_refnum_prev(refnum, wrap)
+       int refnum
+       int wrap
+
+int
+window_refnum_next(refnum, wrap)
+       int refnum
+       int wrap
+
+int
+windows_refnum_last()
+
+Irssi::UI::Window
+window_find_level(level)
+       int level
+CODE:
+       RETVAL = window_find_level(NULL, level);
+OUTPUT:
+       RETVAL
+
+Irssi::UI::Window
+window_find_item(name)
+       char *name
+CODE:
+       RETVAL = window_find_item(NULL, name);
+OUTPUT:
+       RETVAL
+
+Irssi::UI::Window
+window_find_closest(name, level)
+       char *name
+       int level
+CODE:
+       RETVAL = window_find_closest(NULL, name, level);
+OUTPUT:
+       RETVAL
+
+Irssi::Windowitem
+window_item_find(name)
+       char *name
+CODE:
+       RETVAL = window_item_find(NULL, name);
+OUTPUT:
+       RETVAL
+
+
+#*******************************
+MODULE = Irssi::UI::Window  PACKAGE = Irssi::Server
+#*******************************
+
+void
+print(server, channel, str, level=MSGLEVEL_CLIENTNOTICE)
+       Irssi::Server server
+       char *channel
+       char *str
+       int level
+CODE:
+       printtext_string(server, channel, level, str);
+
+Irssi::Windowitem
+window_item_find(server, name)
+       Irssi::Server server
+       char *name
+
+Irssi::UI::Window
+window_find_item(server, name)
+       Irssi::Server server
+       char *name
+
+Irssi::UI::Window
+window_find_level(server, level)
+       Irssi::Server server
+       int level
+
+Irssi::UI::Window
+window_find_closest(server, name, level)
+       Irssi::Server server
+       char *name
+       int level
+
+
+#*******************************
+MODULE = Irssi::UI::Window  PACKAGE = Irssi::UI::Window  PREFIX=window_
+#*******************************
+
+void
+items(window)
+       Irssi::UI::Window window
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+                CHANNEL_REC *rec = tmp->data;
+
+               XPUSHs(sv_2mortal(iobject_bless(rec)));
+       }
+
+void
+print(window, str, level=MSGLEVEL_CLIENTNOTICE)
+       Irssi::UI::Window window
+       char *str
+        int level;
+CODE:
+       printtext_string_window(window, level, str);
+
+void
+command(window, cmd)
+       Irssi::UI::Window window
+       char *cmd
+PREINIT:
+       WINDOW_REC *old;
+CODE:
+       old = active_win;
+       active_win = window;
+       perl_command(cmd, window->active_server, window->active);
+        active_win = old;
+
+void
+window_item_add(window, item, automatic)
+       Irssi::UI::Window window
+       Irssi::Windowitem item
+       int automatic
+
+void
+window_item_remove(item)
+       Irssi::Windowitem item
+
+void
+window_item_destroy(item)
+       Irssi::Windowitem item
+
+void
+window_item_prev(window)
+       Irssi::UI::Window window
+
+void
+window_item_next(window)
+       Irssi::UI::Window window
+
+void
+window_destroy(window)
+       Irssi::UI::Window window
+
+void
+window_set_active(window)
+       Irssi::UI::Window window
+
+void
+window_change_server(window, server)
+       Irssi::UI::Window window
+       Irssi::Server server
+
+void
+window_set_refnum(window, refnum)
+       Irssi::UI::Window window
+       int refnum
+
+void
+window_set_name(window, name)
+       Irssi::UI::Window window
+       char *name
+
+void
+window_set_history(window, name)
+       Irssi::UI::Window window
+       char *name
+
+void
+window_set_level(window, level)
+       Irssi::UI::Window window
+       int level
+
+char *
+window_get_active_name(window)
+       Irssi::UI::Window window
+
+Irssi::Windowitem
+window_item_find(window, server, name)
+       Irssi::UI::Window window
+       Irssi::Server server
+       char *name
+CODE:
+       RETVAL = window_item_find_window(window, server, name);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::UI::Window  PACKAGE = Irssi::Windowitem  PREFIX = window_item_
+#*******************************
+
+void
+print(item, str, level=MSGLEVEL_CLIENTNOTICE)
+       Irssi::Windowitem item
+       int level
+       char *str
+CODE:
+       printtext_string(item->server, item->name, level, str);
+
+Irssi::UI::Window
+window_create(item, automatic)
+       Irssi::Windowitem item
+       int automatic
+
+Irssi::UI::Window
+window(item)
+       Irssi::Windowitem item
+CODE:
+       RETVAL = window_item_window(item);
+OUTPUT:
+       RETVAL
+
+void
+window_item_change_server(item, server)
+       Irssi::Windowitem item
+       Irssi::Server server
+
+int
+window_item_is_active(item)
+       Irssi::Windowitem item
+
+void
+window_item_set_active(item)
+       Irssi::Windowitem item
+CODE:
+       window_item_set_active(window_item_window(item), item);
diff --git a/apps/irssi/src/perl/ui/module.h b/apps/irssi/src/perl/ui/module.h
new file mode 100644 (file)
index 0000000..3177503
--- /dev/null
@@ -0,0 +1,14 @@
+#include "../common/module.h"
+
+#include "fe-windows.h"
+#include "fe-exec.h"
+#include "formats.h"
+#include "printtext.h"
+#include "window-items.h"
+#include "themes.h"
+#include "keyboard.h"
+
+typedef WINDOW_REC *Irssi__UI__Window;
+typedef TEXT_DEST_REC *Irssi__UI__TextDest;
+typedef THEME_REC *Irssi__UI__Theme;
+typedef KEYINFO_REC *Irssi__UI__Keyinfo;
diff --git a/apps/irssi/src/perl/ui/typemap b/apps/irssi/src/perl/ui/typemap
new file mode 100644 (file)
index 0000000..2a16fe4
--- /dev/null
@@ -0,0 +1,16 @@
+TYPEMAP
+Irssi::UI::Theme               T_PlainObj
+Irssi::UI::Window              T_PlainObj
+Irssi::UI::Keyinfo             T_PlainObj
+Irssi::UI::TextDest            T_PlainObj
+
+INPUT
+
+T_PlainObj
+       $var = irssi_ref_object($arg)
+
+OUTPUT
+
+T_PlainObj
+       $arg = plain_bless($var, \"$type\");
+
index 4878c7168ae5dea9524e2e0555c5cdf398dbdbe6..2876a3a6268d0cbdc926fdc668e76ca15a8c75e6 100644 (file)
@@ -164,46 +164,15 @@ static bool silc_log_misc(SilcLogType type, char *message, void *context)
   return TRUE;
 }
 
   return TRUE;
 }
 
-/* Init SILC. Called from src/fe-text/silc.c */
-
-void silc_core_init(void)
-{
-  static struct poptOption options[] = {
-    { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, 
-      "Create new public key pair", NULL },
-    { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, 
-      "Set the PKCS of the public key pair", "PKCS" },
-    { "bits", 0, POPT_ARG_INT, &opt_bits, 0, 
-      "Set the length of the public key pair", "VALUE" },
-    { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
-      "Show the contents of the public key", "FILE" },
-    { "list-ciphers", 'C', POPT_ARG_NONE, &opt_list_ciphers, 0,
-      "List supported ciphers", NULL },
-    { "list-hash-funcs", 'H', POPT_ARG_NONE, &opt_list_hash, 0,
-      "List supported hash functions", NULL },
-    { "list-hmacs", 'H', POPT_ARG_NONE, &opt_list_hmac, 0,
-      "List supported HMACs", NULL },
-    { "list-pkcs", 'P', POPT_ARG_NONE, &opt_list_pkcs, 0,
-      "List supported PKCSs", NULL },
-    { "debug", 'd', POPT_ARG_STRING, &opt_debug, 0,
-      "Enable debugging", "STRING" },
-    { "version", 'V', POPT_ARG_NONE, &opt_version, 0,
-      "Show version", NULL },
-    { NULL, '\0', 0, NULL }
-  };
-
-  args_register(options);
-}
-
 static void silc_nickname_format_parse(const char *nickname,
                                       char **ret_nickname)
 {
   silc_parse_userfqdn(nickname, ret_nickname, NULL);
 }
 
 static void silc_nickname_format_parse(const char *nickname,
                                       char **ret_nickname)
 {
   silc_parse_userfqdn(nickname, ret_nickname, NULL);
 }
 
-/* Finalize init. Called from src/fe-text/silc.c */
+/* Finalize init. Init finish signal calls this. */
 
 
-void silc_core_init_finish(void)
+void silc_core_init_finish(SERVER_REC *server)
 {
   CHAT_PROTOCOL_REC *rec;
   SilcClientParams params;
 {
   CHAT_PROTOCOL_REC *rec;
   SilcClientParams params;
@@ -369,6 +338,39 @@ void silc_core_init_finish(void)
   idletag = g_timeout_add(5, (GSourceFunc) my_silc_scheduler, NULL);
 }
 
   idletag = g_timeout_add(5, (GSourceFunc) my_silc_scheduler, NULL);
 }
 
+/* Init SILC. Called from src/fe-text/silc.c */
+
+void silc_core_init(void)
+{
+  static struct poptOption options[] = {
+    { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, 
+      "Create new public key pair", NULL },
+    { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, 
+      "Set the PKCS of the public key pair", "PKCS" },
+    { "bits", 0, POPT_ARG_INT, &opt_bits, 0, 
+      "Set the length of the public key pair", "VALUE" },
+    { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
+      "Show the contents of the public key", "FILE" },
+    { "list-ciphers", 'C', POPT_ARG_NONE, &opt_list_ciphers, 0,
+      "List supported ciphers", NULL },
+    { "list-hash-funcs", 'H', POPT_ARG_NONE, &opt_list_hash, 0,
+      "List supported hash functions", NULL },
+    { "list-hmacs", 'H', POPT_ARG_NONE, &opt_list_hmac, 0,
+      "List supported HMACs", NULL },
+    { "list-pkcs", 'P', POPT_ARG_NONE, &opt_list_pkcs, 0,
+      "List supported PKCSs", NULL },
+    { "debug", 'd', POPT_ARG_STRING, &opt_debug, 0,
+      "Enable debugging", "STRING" },
+    { "version", 'V', POPT_ARG_NONE, &opt_version, 0,
+      "Show version", NULL },
+    { NULL, '\0', 0, NULL }
+  };
+
+  signal_add("irssi init finished", (SIGNAL_FUNC) silc_core_init_finish);
+
+  args_register(options);
+}
+
 /* Deinit SILC. Called from src/fe-text/silc.c */
 
 void silc_core_deinit(void)
 /* Deinit SILC. Called from src/fe-text/silc.c */
 
 void silc_core_deinit(void)
index cb41470edfca631dffacfee201f73dd5c8f692f6..86e3082b1dbc3be880a78c4101e7c49770e08361 100644 (file)
@@ -164,7 +164,7 @@ static int isnickflag_func(char flag)
   return flag == '@' || flag == '+';
 }
 
   return flag == '@' || flag == '+';
 }
 
-static int ischannel_func(const char *data)
+static int ischannel_func(SERVER_REC *server, const char *data)
 {
   return *data == '#';
 }
 {
   return *data == '#';
 }
@@ -246,8 +246,10 @@ SILC_SERVER_REC *silc_server_connect(SILC_SERVER_CONNECT_REC *conn)
   if (server->connrec->port <= 0) 
     server->connrec->port = 706;
 
   if (server->connrec->port <= 0) 
     server->connrec->port = 706;
 
+  server_connect_ref(SERVER_CONNECT(conn));
+
   if (!server_start_connect((SERVER_REC *) server)) {
   if (!server_start_connect((SERVER_REC *) server)) {
-    server_connect_free(SERVER_CONNECT(conn));
+    server_connect_unref(SERVER_CONNECT(conn));
     g_free(server);
     return NULL;
   }
     g_free(server);
     return NULL;
   }
index 63e2a1ff0995c34f4d8f999ccaa302043932933b..8472feb028e7ea326f5c7c2dbb2b316de15ee5a6 100644 (file)
@@ -604,7 +604,7 @@ AC_SUBST(PIDFILE)
 AC_ARG_WITH(win32,
 [  --with-win32            Compile native WIN32 code (-mno-cygwin)],
 [ AC_DEFINE(SILC_WIN32)
 AC_ARG_WITH(win32,
 [  --with-win32            Compile native WIN32 code (-mno-cygwin)],
 [ AC_DEFINE(SILC_WIN32)
-  win32-support = true
+  win32-support=true
   CFLAGS="-mno-cygwin $CFLAGS" 
   LIBS="$LIBS -lwsock32" ])
 
   CFLAGS="-mno-cygwin $CFLAGS" 
   LIBS="$LIBS -lwsock32" ])
 
@@ -613,8 +613,7 @@ AM_CONDITIONAL(SILC_WIN32, test x$win32-support = xtrue)
 #
 # Native EPOC support (disabled by default)
 #
 #
 # Native EPOC support (disabled by default)
 #
-epoc-support = false
-AM_CONDITIONAL(SILC_EPOC, test x$epoc-support = xtrue)
+AM_CONDITIONAL(SILC_EPOC, test xfalse = xtrue)
 
 #
 # IPv6 support
 
 #
 # IPv6 support
diff --git a/prepare b/prepare
index 191a915d22ccb0e35d664deec5a6ce8e6e81f895..e61e74fe26193554110320dc2e9194e1eaedb8ae 100755 (executable)
--- a/prepare
+++ b/prepare
@@ -154,6 +154,7 @@ file=irssi/irssi-version.h.in
 version_date=`date +%Y%m%d`
 echo "/* automatically created by autogen.sh */" > $file
 echo "#define IRSSI_VERSION \"$dist_version (Irssi base: @VERSION@ - SILC base: SILC Toolkit $version)\"" >>$file
 version_date=`date +%Y%m%d`
 echo "/* automatically created by autogen.sh */" > $file
 echo "#define IRSSI_VERSION \"$dist_version (Irssi base: @VERSION@ - SILC base: SILC Toolkit $version)\"" >>$file
-echo "#define IRSSI_VERSION_DATE \"$version_date\"" >> $file
+echo "#define IRSSI_VERSION_DATE $version_date" >> $file
+echo "#define IRSSI_VERSION_TIME $version_date" >> $file
 
 echo "Done, now run ./configure and make."
 
 echo "Done, now run ./configure and make."