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
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.
-     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.
 
@@ -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.>
-    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
@@ -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:
 
-    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.
index a8a1eff4a59bbf8e340cab5ba9030bb94e7d613d..5d3888cf8347bad23e908322ef00bf600215eaf8 100644 (file)
@@ -1,29 +1,43 @@
 # 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
 
-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
 
-#confdir = $(sysconfdir)/irssi
+#confdir = $(sysconfdir)
 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 \
+       curses.m4 \
        README \
+       README.cygwin \
        file2header.sh \
        irssi.spec \
        irssi.spec.in \
        $(conf_DATA) \
+       $(theme_DATA) \
        irssi-config.in \
+       irssi-icon.png \
        Makefile.defines.in \
        Makefile.defines_int.in
 
index 753505964ac99bb768cd9dd566a8db7758278a4e..33f02000a4bb60cae063148eac3a4eca25b364e5 100644 (file)
@@ -4,7 +4,6 @@
 #undef PLUGINSDIR
 
 /* misc.. */
-#undef MEM_DEBUG
 #undef HAVE_IPV6
 #undef HAVE_POPT_H
 #undef HAVE_SOCKS_H
 #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 USE_CURSES_WINDOWS
+
+/* terminfo/termcap */
+#undef HAVE_TERMINFO
 
 /* nls */
 #undef ENABLE_NLS
index d968658d38002ab464b0a03f81c36004d9aaa722..5ad69a2724375d696038fbb29bc27b8967ea7a45 100755 (executable)
@@ -15,10 +15,6 @@ fi
 # 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
@@ -80,6 +76,13 @@ if test "$DIE" -eq 1; then
   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;;
@@ -88,9 +91,9 @@ esac
 rm -f aclocal.m4
 if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
   echo "Running libtoolize..."
-  libtoolize --copy --force
+  libtoolize --force --copy
 fi
-aclocalinclude="$ACLOCAL_FLAGS"
+aclocalinclude="$ACLOCAL_FLAGS -I ."
 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
-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)
 
+# 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_INIT_AUTOMAKE(Irssi-SILC, 0.7.98.3)
+AM_INIT_AUTOMAKE(Irssi-SILC, 0.7.99)
 
 AM_MAINTAINER_MODE
 
@@ -9,25 +15,17 @@ AC_ISC_POSIX
 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
 
-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)
@@ -97,6 +95,18 @@ AC_ARG_WITH(proxy,
        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],
@@ -104,39 +114,73 @@ AC_ARG_WITH(modules,
                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
 
-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
-               if test "x$enableval" = xno; then
-                       want_perl=no
+               if test "x$withval" = xno; then
+                       want_staticperllib=no
                else
-                       want_perl=yes
-                       PERL_LIB_DIR="$enableval"
-                       perl_lib_dir_given=yes
+                       want_staticperllib=yes
                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
-       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
+       elif test x$withval = xstatic; then
+               want_perl=static
+       elif test x$withval = xmodule; then
+               want_perl=module
        else
                want_perl=no
        fi,
-       want_perl=no)
+       want_perl=static)
 
 AC_ARG_WITH(tests,
 [  --with-tests           Run all the tests],
@@ -146,26 +190,6 @@ AC_ARG_WITH(tests,
        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
@@ -183,30 +207,35 @@ 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_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 * 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])
@@ -229,7 +258,7 @@ dnl **
 
 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"
@@ -245,13 +274,11 @@ 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`
-  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])
 
@@ -294,9 +321,9 @@ AC_DEFUN(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)
-  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)
@@ -304,7 +331,7 @@ if test "x$GLIB_DIR" = "x"; then
     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,"
@@ -319,14 +346,16 @@ if test "x$GLIB_DIR" = "x"; then
     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
-    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
-    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
+      echo "*** Press ENTER to continue"
       read answer
       eval $dlcmd
       if `gunzip $glib_file`; then
@@ -338,344 +367,109 @@ if test "x$GLIB_DIR" = "x"; then
       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
 
-PROG_LIBS="$PROG_LIBS $GLIB_LIBS"
+LIBS="$LIBS $GLIB_LIBS"
 
 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 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
+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$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
-               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
-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 **
@@ -683,17 +477,18 @@ dnl ** perl checks
 dnl **
 
 if test "$want_perl" != "no"; then
-       AC_PATH_PROG(perlpath, perl)
        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
 
-       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
@@ -718,10 +513,10 @@ if test "$want_perl" != "no"; then
                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 } } };'`
-                       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
@@ -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
-                       $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
@@ -763,22 +558,23 @@ if test "$want_perl" != "no"; 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
+               rm -f perl.error.tmp
        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/'`
-                       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)
@@ -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
-                       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)
@@ -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=
-                       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
-                       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_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
+
+               # 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_LINK_FLAGS)
                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_LIB_DIR)
+
+               AC_SUBST(PERL_USE_LIB)
+               AC_SUBST(PERL_MM_PARAMS)
+               AC_SUBST(PERL_STATIC_LIBS)
        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_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(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 **
@@ -844,10 +665,9 @@ dnl ** (these could be made configurable)
 
 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
-#PROG_LIBS="$PROG_LIBS -lsilc"
 
 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
-               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
-                       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
@@ -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
-       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 "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
-                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
@@ -909,15 +728,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_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 **
@@ -931,9 +741,35 @@ dnl ** IPv6 support
 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
 
+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)
 
@@ -949,20 +785,30 @@ src/lib-config/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
+irssi-version.h
 stamp.h
 irssi.spec
-irssi-version.h
 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
@@ -973,7 +819,16 @@ fi
 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"
@@ -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)"
-elif test "x$want_perl" = "xyes"; then
+elif test "x$want_perl" = "xmodule"; then
        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!"
@@ -1003,18 +858,20 @@ if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then
        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"
+
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
   ##
 
-  # 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)";
-  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)
    -------------------------------------------------------
-    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
-    4          light red           red         + blinking fg
-    5          orange              orange
+    4          light red           red          + blinking fg
+    5          red                 red
     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):
  
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-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)
-       --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
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><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>
@@ -83,19 +84,29 @@ to bot after joined to efnet/#irssi:</p>
 
 <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>
 
+If you want lines containing your nick to hilight:
+
+<pre>
+     /HILIGHT nick
+</pre>
+
 <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
-     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>
@@ -117,10 +128,16 @@ want to use ALT instead of Windows key for it, use:</p>
      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
-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>  
 
@@ -216,7 +233,7 @@ same network if the -auto server fails.</p>
 <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>
@@ -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>
-     /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>
 
 
-<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
@@ -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
-  &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
@@ -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>
-
+</dl>
 
 <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@"
-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
-Summary(pl):   Irssi - klient IRC
 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
-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
-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/.
 
-%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
-CPPFLAGS="-I/usr/X11R6/include"; export CPPFLAGS
-LDFLAGS="-s -L/usr/X11R6/lib"; export LDFLAGS
+export NOCONFIGURE=x
+./autogen.sh       
 %configure \
-       --with-gnome \
-       --with-gnome-panel \
        --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 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)
-%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
-* %{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
 
+if BUILD_IRSSIBOT
+BOTUI=#
+endif
+
+if HAVE_PERL
+PERLDIR=perl
+endif
+
 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_DIR_SHORT "~/.silc"
+#define IRSSI_DIR_FULL "%s/.silc" /* %s == g_get_home_dir() */
+
 #ifdef HAVE_CONFIG_H
 #include "config.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)
 
@@ -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);
 
+/* 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 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;
@@ -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 _WINDOW_REC WINDOW_REC;
+
 #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 = \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/core \
        $(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 \
@@ -29,10 +23,11 @@ libcore_a_SOURCES = \
         levels.c \
        line-split.c \
        log.c \
+       log-away.c \
        masks.c \
-        $(memdebug_src) \
        misc.c \
        modules.c \
+       modules-load.c \
        net-disconnect.c \
        net-nonblock.c \
        net-sendbuffer.c \
@@ -44,8 +39,8 @@ libcore_a_SOURCES = \
        rawlog.c \
        servers.c \
        servers-reconnect.c \
-       servers-redirect.c \
        servers-setup.c \
+       session.c \
        settings.c \
        signals.c \
        special-vars.c \
@@ -75,10 +70,10 @@ noinst_HEADERS = \
        line-split.h \
        log.h \
        masks.h \
-        memdebug.h \
        misc.h \
        module.h \
        modules.h \
+       modules-load.h \
        net-disconnect.h \
        net-nonblock.h \
        net-sendbuffer.h \
@@ -91,14 +86,11 @@ noinst_HEADERS = \
        rawlog.h \
        servers.h \
        servers-reconnect.h \
-       servers-redirect.h \
        servers-setup.h \
+       session.h \
        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;
+
+        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 session_rejoin:1; /* This channel was joined with /UPGRADE */
 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);
-       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) {
@@ -158,7 +151,8 @@ static void channels_read_config(void)
        /* 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);
        }
 }
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");
+        channel->destroy = (void (*) (WI_ITEM_REC *)) channel_destroy;
         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);
+
+        channel->type = 0;
        g_free(channel);
 }
 
@@ -113,17 +116,48 @@ CHANNEL_REC *channel_find(SERVER_REC *server, const char *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;
-       GSList *tmp;
+       GSList *tmp, *chatnet_servers;
 
        g_return_if_fail(SERVER(server));
 
-       if (server->connrec->reconnection)
+       if (server->connrec->reconnection ||
+           server->connrec->no_autojoin_channels)
                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) {
@@ -134,8 +168,12 @@ static void event_connected(SERVER_REC *server)
                                           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);
@@ -165,6 +203,9 @@ void channel_send_autocommands(CHANNEL_REC *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;
@@ -180,6 +221,9 @@ void channel_send_autocommands(CHANNEL_REC *channel)
        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);
index 78a05a70b39a8a7e0840370f171cfbe793713fbe..8b1b37389a0d3fde42589fe48e46d9e51aa95a44 100644 (file)
 #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;
@@ -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);
-               server_connect_free(conn);
+               server_connect_unref(conn);
                 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;
 
+       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;
@@ -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;
+       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,
@@ -147,6 +166,7 @@ static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server)
 
        if (server != NULL) {
                oldconn = server->connrec;
+                server_connect_ref(oldconn);
                 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;
-               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);
@@ -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);
 
+       server_connect_unref(oldconn);
        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>]]] */
-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;
+        char *rawlog_file;
        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);
-               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);
-       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;
 
+       /* -<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);
-
-               /* -<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);
 }
 
-/* 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;
-       int free_ret;
+       int free_ret, target_type;
 
        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;
-       } 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;
+       }
+       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)
-               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);
 
@@ -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)
 {
-       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)
 {
-       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);
+                list = g_slist_remove(list, list->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);
+                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);
+       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);
@@ -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_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("msg", "channel nick");
 }
 
 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);
@@ -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);
+
+        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);
-       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 */
@@ -190,6 +196,10 @@ static SERVER_CONNECT_REC *create_server_connect(void)
         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)
@@ -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->destroy_server_connect = destroy_server_connect;
 
        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;
+       unsigned int case_insensitive:1;
 
        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);
+        void (*destroy_server_connect) (SERVER_CONNECT_REC *);
 
         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;
+        GSList *tmp;
 
        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;
 
-       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)
 {
-       while (chatnets != NULL)
-               chatnet_destroy(chatnets->data);
        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 "servers-redirect.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;
 
-                if (g_slist_find(rec->signals, func) != NULL)
+                if (g_slist_find(rec->signals, (void *) func) != NULL)
                        return rec;
        }
 
@@ -111,8 +110,8 @@ int command_have_sub(const char *command)
        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;
 
@@ -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);
+                modrec->protocol = -1;
                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,
-                    const char *category, SIGNAL_FUNC func)
+                    int protocol, const char *category, SIGNAL_FUNC func)
 {
        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);
        }
-        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);
@@ -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);
-               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);
        }
@@ -283,6 +289,8 @@ void command_runsub(const char *cmd, const char *data,
 
        g_return_if_fail(data != NULL);
 
+        while (*data == ' ') data++;
+
        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);
-        modrec = command_module_get(rec, module);
+        modrec = command_module_get(rec, module, -1);
 
        reload = modrec->options != NULL;
         if (reload) {
@@ -550,15 +558,17 @@ static int get_cmd_options(char **data, int ignore_unknown,
                        }
 
                        (*data)++;
-                       if (**data == '-' && isspace((*data)[1])) {
+                       if (**data == '-' && i_isspace((*data)[1])) {
                                /* -- option means end of options even
                                   if next word starts with - */
                                (*data)++;
-                               while (isspace(**data)) (*data)++;
+                               while (i_isspace(**data)) (*data)++;
                                break;
                        }
 
-                       if (!isspace(**data))
+                       if (**data == '\0')
+                               option = "-";
+                       else if (!i_isspace(**data))
                                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);
+
+                       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;
@@ -584,21 +606,21 @@ static int get_cmd_options(char **data, int ignore_unknown,
                                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;
 
-                       while (isspace(**data)) (*data)++;
+                       while (i_isspace(**data)) (*data)++;
                        continue;
                }
 
                if (option == NULL)
                        break;
 
-               if (*optlist[pos] == '@' && !isdigit(**data))
+               if (*optlist[pos] == '@' && !i_isdigit(**data))
                        break; /* expected a numeric argument */
 
                /* save the argument */
@@ -609,7 +631,7 @@ static int get_cmd_options(char **data, int ignore_unknown,
                }
                option = NULL;
 
-               while (isspace(**data)) (*data)++;
+               while (i_isspace(**data)) (*data)++;
        }
 
        return 0;
@@ -620,7 +642,8 @@ typedef struct {
         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;
@@ -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);
 
-       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);
 
-                chanrec = channel_find(active_item->server, channel);
+               chanrec = channel_find(active_item->server, channel);
                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;
-       int cnt, error, ignore_unknown;
+       int cnt, error, ignore_unknown, require_name;
 
        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;
 
-        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 *);
@@ -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 */
-                        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;
@@ -741,7 +769,7 @@ static void command_module_unbind_all(COMMAND_REC *rec,
        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) {
@@ -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)
 
@@ -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)
 {
+        COMMAND_REC *rec;
        const char *alias, *newcmd;
        char *cmd, *orig, *args, *oldcmd;
 
@@ -808,17 +859,32 @@ static void parse_command(const char *command, int expand_aliases,
                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);
+
        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);
        }
+       if (server != NULL) {
+               if (server->connection_lost)
+                       server_disconnect(server);
+               server_unref(server);
+       }
        current_command = oldcmd;
 
        g_free(cmd);
index 9dee2e8035f8cfee86f01abc0ca9875d8e62edd0..3e55a2adc04f0e7d9ab40504e5251b402786309b 100644 (file)
@@ -6,6 +6,7 @@
 typedef struct {
        char *name;
        char *options;
+        int protocol; /* chat protocol required for this command */
         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_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_ILLEGAL_PROTO, /* requires different chat protocol than the active server */
        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,
-                    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);
 
@@ -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
+/* 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
index fd7459db8d0b1997bc376f0a6a18b0c295783a8e..2321208eb086c999c7dd21e00d44ef88671eccf0 100644 (file)
 */
 
 #include "module.h"
+#include <signal.h>
 
+#include "args.h"
 #include "pidwait.h"
+#include "misc.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 "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 log_away_init(void);
+void log_away_deinit(void);
+
 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();
@@ -57,9 +207,14 @@ void core_init(void)
        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();
-        nickmatch_cache_init();
+       nickmatch_cache_init();
+        session_init();
 
        chat_protocols_init();
        chatnets_init();
@@ -68,6 +223,7 @@ void core_init(void)
        servers_init();
         write_buffer_init();
        log_init();
+       log_away_init();
        rawlog_init();
 
        channels_init();
@@ -75,11 +231,27 @@ void core_init(void)
        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)
 {
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished);
+
        chat_commands_deinit();
 
        nicklist_deinit();
@@ -87,6 +259,7 @@ void core_deinit(void)
        channels_deinit();
 
        rawlog_deinit();
+       log_away_deinit();
        log_deinit();
         write_buffer_deinit();
        servers_deinit();
@@ -95,6 +268,7 @@ void core_deinit(void)
        chatnets_deinit();
        chat_protocols_deinit();
 
+        session_deinit();
         nickmatch_cache_deinit();
        commands_deinit();
        settings_deinit();
@@ -106,4 +280,7 @@ void core_deinit(void)
        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;
+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);
index 21ebae6b8b8ab020ebcf798cfd9c7d0a16bf8008..777d50e1e38d61d565c3e0633cdd69f86a3cd341 100644 (file)
@@ -48,16 +48,19 @@ typedef struct {
 
 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 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, ...)
@@ -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 = char_expandos[(int) *key];
+               rec = CHAR_EXPANDO(*key);
        }
 
        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
-                       char_expandos[(int) *key] = rec;
+                       char_expandos[(int) (unsigned char) *key] = rec;
        }
 
        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
-               return char_expandos[(int) *key];
+               return CHAR_EXPANDO(*key);
 }
 
 /* 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 */
-               rec = char_expandos[(int) *key];
+               rec = CHAR_EXPANDO(*key);
                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,
@@ -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)
@@ -327,10 +360,18 @@ static char *expando_target(SERVER_REC *server, void *item, int *free_ret)
        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)
 {
-       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 */
@@ -439,13 +480,42 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg,
 
 static int sig_timer(void)
 {
+       time_t now;
+       struct tm *tm;
+        int last_min;
+
         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)
 {
-        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)
@@ -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;
+        last_timestamp = 0;
 
         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);
@@ -521,11 +592,14 @@ void expandos_init(void)
                       "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("Z", expando_time, NULL);
+       expando_create("Z", expando_time,
+                      "time changed", EXPANDO_ARG_NONE, NULL);
        expando_create("$", expando_dollar,
                       "", EXPANDO_NEVER, NULL);
 
@@ -549,7 +623,7 @@ void expandos_init(void)
 
        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);
@@ -560,7 +634,7 @@ void expandos_deinit(void)
 {
        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);
index 3dcb527e7480528afdf0f4307dca773039e17233..cf057994e7c814f2c7c200a7b8613713fb82361b 100644 (file)
@@ -5,7 +5,7 @@
 
 /* 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,
@@ -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);
 
+/* 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);
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 = "";
 
-       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 */
@@ -317,18 +316,24 @@ static void ignore_remove_config(IGNORE_REC *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
+}
+
+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);
+       nickmatch_rebuild(nickmatch);
 }
 
 static void ignore_destroy(IGNORE_REC *rec, int send_signal)
@@ -402,7 +407,8 @@ static void read_ignores(void)
                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)
@@ -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));
-               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);
-               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);
+
+               ignore_init_rec(rec);
        }
 
        nickmatch_rebuild(nickmatch);
index aae506a8212c23b3c8baf67a51d7526813a8ff27..0caf58b650438ea1c0ce66c9e67a851562f521cd 100644 (file)
@@ -43,7 +43,7 @@ static const char *levels[] = {
        "CLIENTNOTICES",
        "CLIENTCRAP",
        "CLIENTERRORS",
-       "HILIGHT",
+       "HILIGHTS",
 
        "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"
 
-#define DEFAULT_LOG_FILE_CREATE_MODE 644
+#define DEFAULT_LOG_FILE_CREATE_MODE 600
 
 #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)
 {
-       char *new_fname;
+       char *new_fname, *dir;
 
        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);
+               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);
@@ -183,6 +189,7 @@ static void log_rotate_check(LOG_REC *log)
 
 void log_write_rec(LOG_REC *log, const char *str, int level)
 {
+        char *colorstr;
        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;
 
+       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
@@ -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);
+
+        g_free_not_null(colorstr);
 }
 
 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;
 }
 
-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;
-       char *tmpstr, *servertag;
+       char *tmpstr;
        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;
 
-       servertag = server == NULL ? NULL : server->tag;
        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,
-                                      servertag) != NULL)
+                                      server_tag) != NULL)
                        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);
+
+       signal_emit("log config save", 2, log, node);
 }
 
 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;
 
-       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)
@@ -500,7 +516,8 @@ static void log_read_config(void)
        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)
@@ -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));
 
+               signal_emit("log config read", 2, log, node);
+
                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();
-       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)
@@ -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("irssi init finished", (SIGNAL_FUNC) log_read_config);
 }
index 7361b6a09abcb71f98e89aa2f09a4ac5fbd10ff8..6ada7c65b0de325bc2a9f7f0efb5874fb093650b 100644 (file)
@@ -1,11 +1,15 @@
 #ifndef __LOG_H
 #define __LOG_H
 
+#define LOG_DIR_CREATE_MODE 0700
+
 enum {
        LOG_ITEM_TARGET, /* channel, query, .. */
        LOG_ITEM_WINDOW_REFNUM
 };
 
+typedef char *(*COLORIZE_FUNC)(const char *str);
+
 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 */
+        COLORIZE_FUNC colorizer;
 
        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);
 
-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);
 
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 (;;) {
-               while (isspace((gint) *list)) list++;
+               while (i_isspace(*list)) 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 (toupper(data[pos]) == toupper(key[pos]))
+               if (i_toupper(data[pos]) == i_toupper(key[pos]))
                        pos++;
                else {
                        data++;
@@ -337,7 +337,7 @@ char *stristr(const char *data, const char *key)
 
 #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)
 {
@@ -364,7 +364,7 @@ char *strstr_full_case(const char *data, const char *key, int icase)
                        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])))
@@ -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
-                       if (mkdir(dir, mode) == -1) {
+                       if (mkdir(dir, mode) == -1)
 #else
-                       if (_mkdir(dir) == -1) {
+                       if (_mkdir(dir) == -1)
 #endif
+                       {
                                g_free(dir);
                                return -1;
                        }
@@ -472,7 +473,7 @@ unsigned int g_istr_hash(gconstpointer v)
        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;
@@ -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 != '*') {
-                       if (*mask != '?' && toupper(*mask) != toupper(*data))
+                       if (*mask != '?' && i_toupper(*mask) != i_toupper(*data))
                                break;
 
                        data++;
@@ -538,7 +539,7 @@ int is_numeric(const char *str, char end_char)
                return FALSE;
 
        while (*str != '\0' && *str != end_char) {
-               if (!isdigit(*str)) return FALSE;
+               if (!i_isdigit(*str)) return FALSE;
                str++;
        }
 
@@ -725,3 +726,47 @@ GSList *columns_sort_list(GSList *list, int rows)
                             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);
 
+/* 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
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
 
-    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
@@ -22,9 +22,6 @@
 #include "modules.h"
 #include "signals.h"
 
-#include "commands.h"
-#include "settings.h"
-
 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)
index 7a83819f61a6f5b1030f5ef7b362904ef2392c21..75a77c7752dc74d649c4a25d74155d3ad42c7872 100644 (file)
 #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)
 
+
+#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
 };
 
+typedef struct _MODULE_REC MODULE_REC;
+
 typedef struct {
+       MODULE_REC *root;
        char *name;
+        char *defined_module_name;
+       void (*module_deinit) (void);
+
 #ifdef HAVE_GMODULE
-       GModule *gmodule;
+       GModule *gmodule; /* static, if NULL */
 #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;
 
-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))
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);
+        net_disconnect(rec->handle);
        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);
 }
 
-/* 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;
@@ -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;
 }
 
+/* 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)
 {
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);
 
+/* 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);
 
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));
 
-       /* set our own address, ignore if bind() fails */
+       /* set our own address */
        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 */
@@ -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
-       if (ret < 0 && errno != EINPROGRESS) {
+       if (ret < 0 && errno != EINPROGRESS)
 #else
-       if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) {
+       if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
 #endif
+       {
                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
-       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;
@@ -373,9 +378,8 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
 {
 #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
@@ -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 */
-       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) {
@@ -409,18 +409,20 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
                        sin_get_ip(so, ip4);
                         count++;
                }
-                ai = ai->ai_next;
        }
-       freeaddrinfo(origai);
+       freeaddrinfo(ailist);
+       return count > 0 ? 0 : 1;
 #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);
-#endif
 
        return 0;
+#endif
 }
 
 /* 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') {
-               if (*host != '.' && !isdigit(*host))
+               if (*host != '.' && !i_isdigit(*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;
 
+/*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 */
index 96f6a8ea2b870e7398d338d6506e06aa522f07d3..b7e8540725c4b5c8902e710437e75f1ae9b95dd6 100644 (file)
@@ -28,7 +28,7 @@
 #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)
 {
@@ -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)
 {
+       /*MODULE_DATA_INIT(nick);*/
+
        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);
 
+        /*MODULE_DATA_DEINIT(nick);*/
        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)
 {
+       int status1, status2;
+       
        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);
 }
 
@@ -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') {
-                       if (toupper(*nick) == toupper(*msg)) {
+                       if (i_toupper(*nick) == i_toupper(*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
@@ -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;
-                               while (*nick != '\0' && !isalnum(*nick))
+                               while (*nick != '\0' && !i_isalnum(*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));
 }
 
+/* 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;
index 3f6b84cdf7ac4e1ad6ffa6dcd80cd9d8dddf3627..ae1ba01e2fe9e75cc3b149ecc052d12b01017db5 100644 (file)
@@ -9,4 +9,8 @@ void pidwait_add(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
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");
+        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) {
@@ -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);
+
+        query->type = 0;
        g_free(query);
 }
 
index 4e47040cb1e28fe443e4f2bfed193a4d1a75f1c1..d605ccb5934e3791910ebfcd221d24851fe24d96 100644 (file)
 #include "rawlog.h"
 #include "modules.h"
 #include "signals.h"
+#include "commands.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;
@@ -158,6 +161,43 @@ static void read_settings(void)
        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");
@@ -166,9 +206,19 @@ void rawlog_init(void)
        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);
+
+       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 refcount;
+
 /* 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 */
+char *tag; /* try to keep this tag when connected to server */
 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 */
+unsigned int no_autojoin_channels:1; /* don't autojoin any channels */
 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 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 */
@@ -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 */
 
-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 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 */
@@ -21,11 +26,6 @@ GIOChannel *connect_pipe[2];
 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;
@@ -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 */
 
-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 */
 
@@ -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 */
-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 */
-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);
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;
+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 */
index a9491f92ba4be3411a2407ee6e2f5f5c6a14e726..e51a3fa23f03393d6a46a05ada427c9c804307d9 100644 (file)
@@ -37,6 +37,9 @@ static int reconnect_time;
 
 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);
@@ -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->conn = conn;
        rec->next_connect = next_connect;
 
+       rec->conn = conn;
+       server_connect_ref(conn);
+
        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);
-       if (free_conn) server_connect_free(rec->conn);
+       server_connect_unref(rec->conn);
        g_free(rec);
 
        if (reconnects == NULL)
@@ -92,8 +97,10 @@ static int server_reconnect_timeout(void)
 
                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);
+                       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 (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 *
@@ -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);
 
+        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->proxy_string_after = g_strdup(src->proxy_string_after);
        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);
@@ -161,8 +167,9 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
 }
 
 #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 && \
@@ -192,7 +199,8 @@ static void sig_reconnect(SERVER_REC *server)
        }
 
        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 */
@@ -210,21 +218,14 @@ static void sig_reconnect(SERVER_REC *server)
                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
-          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);
@@ -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.. */
-                        server_connect_free(conn);
+                       server_connect_unref(conn);
                        break;
                }
 
@@ -288,7 +289,7 @@ static void sig_connected(SERVER_REC *server)
 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)
@@ -319,7 +320,8 @@ static void reconnect_all(void)
                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);
+                server_connect_unref(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);
 
-               if (server->connected) {
+               if (server->connected)
                        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);
+               server_connect_unref(conn);
                 return;
        }
 
@@ -378,25 +380,25 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
        }
 
        conn = rec->conn;
-       server_reconnect_destroy(rec, FALSE);
+       server_connect_ref(conn);
+       server_reconnect_destroy(rec);
        CHAT_PROTOCOL(conn)->server_connect(conn);
+       server_connect_unref(conn);
 }
 
 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 */
 
-       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
-               server_reconnect_destroy(rec, TRUE);
+               server_reconnect_destroy(rec);
        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)
-                       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 {
-       int tag;
+        int tag;
        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);
-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);
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_string_after = g_strdup(settings_get_str("proxy_string_after"));
                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));
        }
+
+       signal_emit("server setup fill connect", 1, 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);
 
+        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)
@@ -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));
 
-       if (chatnet->nick) {
+       if (chatnet->nick != NULL) {
                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);;
        }
-       if (chatnet->realname) {
+       if (chatnet->realname != NULL) {
                 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);
 
-       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;
@@ -212,6 +218,8 @@ create_addr_conn(int chat_type, const char *address, int port,
                 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);
@@ -240,7 +248,6 @@ create_addr_conn(int chat_type, const char *address, int port,
                conn->nick = g_strdup(nick);
        }
 
-       signal_emit("server setup fill connect", 1, 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;
+        CHATNET_REC *chatrec;
 
        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;
        }
 
+       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. */
-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;
@@ -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;
 
-               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;
@@ -329,7 +345,7 @@ SERVER_SETUP_REC *server_setup_find_port(const char *address, int port)
 {
        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;
 }
 
@@ -355,14 +371,6 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
 
        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) {
@@ -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->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);
@@ -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->no_proxy)
+               iconfig_node_set_bool(node, "no_proxy", TRUE);
 
        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) {
-               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);
        }
 }
@@ -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_string_after", "");
        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. */
-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);
 
index f74e7f58906f4db6b39ff122e280ae620653265b..499f1f919f1165f5ea358f800d13e4be4c60279f 100644 (file)
@@ -31,7 +31,6 @@
 #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"
@@ -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);
-       if (server->connect_tag != -1)
+
+       if (server->connect_tag != -1) {
                g_source_remove(server->connect_tag);
-       if (server->handle != NULL)
+               server->connect_tag = -1;
+       }
+       if (server->handle != NULL) {
                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]);
+               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 */
@@ -101,12 +103,24 @@ static char *server_create_tag(SERVER_CONNECT_REC *conn)
        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);
 
+       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++)
@@ -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);
-       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);
@@ -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);
 
-       if (ip != NULL)
+       handle = NULL;
+       if (ip != NULL) {
                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)) {
@@ -215,7 +229,8 @@ static void server_connect_callback_readpipe(SERVER_REC *server)
                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,
@@ -229,6 +244,7 @@ void server_connect_init(SERVER_REC *server)
 
        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') {
@@ -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);
+       server->rawlog = rawlog_create();
 
        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));
 
+       if (server->disconnected)
+               return;
+
        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);
 
+       server->disconnected = TRUE;
        signal_emit("server disconnected", 1, server);
 
        /* close all channels */
@@ -345,18 +366,46 @@ void server_disconnect(SERVER_REC *server)
                server->handle = NULL;
        }
 
-       if (server->readtag > 0)
+       if (server->readtag > 0) {
                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);
-       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);
+
+       server->type = 0;
        g_free(server);
+        return FALSE;
 }
 
 SERVER_REC *server_find_tag(const char *tag)
@@ -401,15 +450,30 @@ SERVER_REC *server_find_chatnet(const char *chatnet)
        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));
 
-       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_after);
        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);
 
@@ -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(conn);
+
+        conn->type = 0;
+       g_free(conn);
 }
 
 void server_change_nick(SERVER_REC *server, const char *nick)
 {
-       g_free(server->connrec->nick);
        g_free(server->nick);
-       server->connrec->nick = g_strdup(nick);
        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();
-       servers_redirect_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();
-       servers_redirect_deinit();
        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 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 {
@@ -28,6 +31,9 @@ struct _SERVER_REC {
 #include "server-rec.h"
 };
 
+#define SEND_TARGET_CHANNEL    0
+#define SEND_TARGET_NICK       1
+
 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);
 
+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);
-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);
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;
-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) */
@@ -274,11 +273,6 @@ static void sig_init_finished(void)
                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 */
@@ -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;
@@ -312,9 +292,9 @@ static void settings_clean_invalid_module(const char *module)
        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;
-                next = tmp->next;
+                next = config_node_next(tmp);
 
                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;
-       GSList *tmp, *next;
+       GSList *tmp;
         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;
 
@@ -371,7 +338,8 @@ void settings_check_module(const char *module)
                         "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);
@@ -489,18 +457,17 @@ static CONFIG_REC *parse_configfile(const char *fname)
        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 */
-                path = SYSCONFDIR"/irssi/config";
+                path = SYSCONFDIR"/irssi.conf";
                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) {
-               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);
        }
 
@@ -521,9 +490,8 @@ static CONFIG_REC *parse_configfile(const char *fname)
         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;
 }
 
@@ -532,27 +500,26 @@ static void init_configfile(void)
        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. */
-               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"
-                       "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) {
-               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);
@@ -563,11 +530,9 @@ int settings_reread(const char *fname)
        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);
-       g_free(str);
+        g_free_not_null(str);
 
        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);
-       signal_emit("setup reread", 0);
+       signal_emit("setup reread", 1, mainconfig->fname);
         return TRUE;
 }
 
-int settings_save(const char *fname)
+int settings_save(const char *fname, int autosave)
 {
        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("setup saved", 2, fname, GINT_TO_POINTER(autosave));
         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)
-               return;
+               return 1;
 
        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 "
@@ -633,18 +599,19 @@ static void sig_autosave(void)
                signal_emit("gui dialog", 2, "warning", str);
                g_free(str);
 
-                settings_save(fname);
+                settings_save(fname, TRUE);
                g_free(fname);
        }
+
+        return 1;
 }
 
 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_config_error_msg = NULL;
         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;
+extern const char *default_config;
 
 /* 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);
-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);
index cb964eb0d4d52661fa258a96028ff11114e15ac1..92064e8d4cf0d272180baee97f151145d63917bd 100644 (file)
@@ -26,9 +26,9 @@
 
 typedef struct {
        int id; /* signal id */
+        int refcount;
 
        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
@@ -43,6 +43,37 @@ static GMemChunk *signals_chunk;
 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)
 {
@@ -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->id = signal_id;
+               rec->id = signal_id;
                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);
-}
-
-/* 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)
@@ -102,23 +122,28 @@ static int signal_list_find(GPtrArray *array, void *data)
        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 */
-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;
 
@@ -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 */
-                       signal_remove_from_list(rec, signal_id, n, index);
+                       signal_remove_from_list(rec, n, index);
                        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)
-               signal_remove_from_lists(rec, signal_id, func);
+               signal_remove_from_lists(rec, func);
 }
 
 /* unbind signal */
@@ -157,34 +182,22 @@ void signal_remove(const char *signal, SIGNAL_FUNC 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;
 
+       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_ref(rec);
+
        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);
 
-                       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
-#  error SIGNAL_MAX_ARGS changed - update code
+#  error SIGNAL_MAX_ARGUMENTS changed - update code
 #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;
@@ -220,60 +231,48 @@ static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist)
                        /* 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;
 }
 
-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;
-       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);
 
-       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));
-       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;
 }
 
-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, ...)
 {
+       SIGNAL_REC *rec;
        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 */
@@ -332,28 +331,37 @@ int signal_is_stopped(int signal_id)
 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 (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);
 
+       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_remove(signals, (GHRFunc) signal_foreach_unref, NULL);
 }
 
 void signals_init(void)
@@ -367,15 +375,19 @@ static void signal_free(void *key, SIGNAL_REC *rec)
 {
        int n;
 
+        signal_ref(rec);
+
        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)
index 40372f93f98b2c5e3e4750d2bc71066807f808bd..4b1f535b919450695fa1537b94d05282a3d4f64e 100644 (file)
@@ -23,6 +23,7 @@
 #include "special-vars.h"
 #include "expandos.h"
 #include "settings.h"
+#include "servers.h"
 #include "misc.h"
 
 #define ALIGN_RIGHT 0x01
 #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;
 
@@ -43,7 +47,7 @@ static char *get_argument(char **cmd, char **arglist)
        arg = 0;
        max = -1;
 
-       argcount = strarray_length(arglist);
+       argcount = arglist == NULL ? 0 : strarray_length(arglist);
 
        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 {
-               if (isdigit(**cmd)) {
+               if (i_isdigit(**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 (!isdigit(**cmd))
+                       if (!i_isdigit(**cmd))
                                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);
-       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++;
@@ -152,7 +156,7 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item,
 {
        EXPANDO_FUNC func;
 
-       if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
+       if (isarg(**cmd)) {
                /* 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);
        }
 
-       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);
        }
@@ -186,7 +190,7 @@ static char *get_history(char **cmd, void *item, int *free_ret)
        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);
        }
@@ -202,6 +206,11 @@ static char *get_special_value(char **cmd, SERVER_REC *server, void *item,
        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)
@@ -279,7 +288,7 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
 
        /* '!' = 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 == '-')
@@ -288,11 +297,11 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
                          *flags &= ~ALIGN_PAD;
                str++;
        }
-       if (!isdigit(*str))
+       if (!i_isdigit(*str))
                return FALSE; /* expecting number */
 
        /* get the alignment size */
-       while (isdigit(*str)) {
+       while (i_isdigit(*str)) {
                *align = (*align) * 10 + (*str-'0');
                str++;
        }
@@ -435,11 +444,31 @@ char *parse_special(char **cmd, SERVER_REC *server, void *item,
        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') {
-               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++;
        }
@@ -451,7 +480,7 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
 {
        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);
@@ -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') {
-               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 == '$') {
@@ -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) {
-                                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;
@@ -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 {
-               if (is_split_char(str, start))
+               if (is_split_char(str, start)) {
                        *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);
-               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');
 
@@ -558,8 +583,20 @@ void eval_special_string(const char *cmd, const char *data,
                        ret = g_strconcat(old, " ", data, NULL);
                        g_free(old);
                }
+
+                if (server != NULL)
+                       server_ref(server);
                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);
        }
@@ -571,41 +608,126 @@ void special_history_func_set(SPECIAL_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') {
+                        /* escape */
                        text += 2;
                } else if (*text == '$' && text[1] != '\0') {
+                        /* expando */
                        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)
 {
-        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)
 {
-        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_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);
@@ -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);
+/* Returns [<signal id>, EXPANDO_ARG_xxx, <signal id>, ..., -1] */
+int *special_vars_get_signals(const char *text);
 
 #endif
index 5c09a5b08a2ddc752c51e8a833e741de25b4aa13..eeb465f4743aa7d4078358f0626fcca8a773b3f5 100644 (file)
@@ -12,4 +12,6 @@ time_t createtime;
 int data_level;
 char *hilight_color;
 
+void (*destroy)(WI_ITEM_REC *item);
+
 #undef STRUCT_SERVER_REC
index 762fc24eb8382722cd91f79ae546fcfd24b55ada..1c3eef824b4a54ebca451ae99ec71d00a71e9e5b 100644 (file)
@@ -126,13 +126,16 @@ void write_buffer_flush(void)
         block_count = 0;
 }
 
+static int flush_timeout(void)
+{
+       write_buffer_flush();
+        return 1;
+}
+
 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") *
@@ -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;
-               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 = \
-       $(GLIB_CFLAGS) \
        -I$(top_srcdir)/src -I$(top_srcdir)/src/core/ \
+       $(GLIB_CFLAGS) \
        -DHELPDIR=\""$(silc_helpdir)"\" \
-       -DSYSCONFDIR=\""$(silc_etcdir)"\"
+       -DTHEMESDIR=\""$(silc_etcdir)"\"
 
 libfe_common_core_a_SOURCES = \
        autorun.c \
@@ -41,6 +41,7 @@ libfe_common_core_a_SOURCES = \
        fe-windows.c
 
 noinst_HEADERS = \
+       autorun.h \
        command-history.h \
        chat-completion.h \
        completion.h \
index f49b6c30b12b5508c7c809cba4ab8feb68bb7d5a..9c1050db313cdb123a9bd1814676f64dea7f5d49 100644 (file)
@@ -1,7 +1,7 @@
 /*
  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
 
 #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;
 
-       /* 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) {
@@ -44,19 +44,13 @@ static void sig_autorun(void)
                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);
 }
-
-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 "servers.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)
 {
@@ -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') {
-                       if (isalnum(*in))
+                       if (i_isalnum(*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,
-                             const char *word, const char *linestart)
+                             const char *word, const char *linestart,
+                             int *want_space)
 {
        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 && server->ischannel(word)) {
+       if (server != NULL && server_ischannel(server, word)) {
                /* 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 */
-                complete_window_nicks(list, window, word, linestart);
+               complete_window_nicks(list, window, word, linestart);
        }
 
        if (*list != NULL) signal_stop();
@@ -634,6 +646,41 @@ static void sig_complete_msg(GList **list, WINDOW_REC *window,
        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;
@@ -688,11 +735,30 @@ static void sig_complete_connect(GList **list, WINDOW_REC *window,
        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;
+        int chr;
 
        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;
                }
 
-               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..) */
-                       *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;
-                       break;
                }
        }
 
@@ -733,48 +797,62 @@ static char *expand_escapes(const char *line, SERVER_REC *server,
        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;
-       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);
-       comp_char = *completion_char;
 
        /* 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);
 
-       if (comp != NULL) {
-               g_list_foreach(comp, (GFunc) g_free, NULL);
-               g_list_free(comp);
-       }
-
        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");
+
+       if (*completion_char == '\0') {
+                /* this would break.. */
+               completion_auto = FALSE;
+       }
 }
 
 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);
+       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 topic", (SIGNAL_FUNC) sig_complete_topic);
        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);
@@ -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 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 topic", (SIGNAL_FUNC) sig_complete_topic);
        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);
index cdf4b5a5b70663173beeb87ced14a98dca66cb55..70f68594296dc6faf28a7a034454459658d1ab67 100644 (file)
 #include "fe-windows.h"
 #include "window-items.h"
 
+#include "command-history.h"
+
 /* command history */
-static GList *history, *history_pos;
-static int history_lines, history_over_counter;
+static HISTORY_REC *global_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);
 
-       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 {
-               link = *phistory;
+               link = history->list;
                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);
        }
 
-       *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 {
-               *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 */
-               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)
 {
-       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)
-               *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 */
-               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)
 {
-       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)
 {
-       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;
+       HISTORY_REC *history;
        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;
 
-       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)) {
@@ -174,19 +272,21 @@ void command_history_init(void)
 
        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();
+       signal_add("window created", (SIGNAL_FUNC) sig_window_created);
        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_remove("window created", (SIGNAL_FUNC) sig_window_created);
        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);
 
-       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
 
-#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_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);
 
+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
index cd86ab4f7438b8a6b2375b4ffc0408eff64dfbb8..f10fbb6dec0414866ecd35da3df8d0607e1eae9f 100644 (file)
@@ -41,7 +41,7 @@ static int last_want_space, last_line_pos;
         ((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);
@@ -113,27 +113,28 @@ static void free_completions(void)
 }
 
 /* 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;
+        int old_startpos, old_wordlen;
 
        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);
 
-       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);
@@ -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. */
-               if (*pos > 0 && line[*pos-1] == ' ') {
+               if (!erase && *pos > 0 && line[*pos-1] == ' ' &&
+                   (*linestart == '\0' || wordstart[-1] != ' ')) {
                        char *old;
 
                         old = linestart;
@@ -171,14 +173,38 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos)
                        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;
-
-               g_free(linestart);
-               g_free(word);
        }
 
+       g_free(linestart);
+       g_free(word);
+
        if (complist == NULL)
                return NULL;
 
@@ -207,7 +233,14 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos)
        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;
@@ -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);
+       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));
@@ -224,7 +262,7 @@ GList *list_add_file(GList *list, const char *name)
        return list;
 }
 
-GList *filename_complete(const char *path)
+GList *filename_complete(const char *path, const char *default_path)
 {
         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);
-       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 */
+       dir = g_dirname(realpath);
        dirp = opendir(dir);
        g_free(dir);
-       if (dirp == NULL) return NULL;
+        g_free(realpath);
+
+       if (dirp == NULL)
+               return NULL;
 
        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);
 
@@ -264,14 +316,15 @@ GList *filename_complete(const char *path)
                }
 
                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(dir);
+       g_free_not_null(dir);
         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);
-       tmp = node == NULL ? NULL : node->value;
+       tmp = node == NULL ? NULL : config_node_first(node->value);
 
        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)
@@ -461,7 +514,7 @@ static char *line_get_command(const char *line, char **args, int aliases)
                } else {
                        checkcmd = g_strndup(line, (int) (ptr-line));
 
-                       while (isspace(*ptr)) ptr++;
+                       while (i_isspace(*ptr)) 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);
 
+        if (cmd != NULL)
+               g_strdown(cmd);
        return cmd;
 }
 
@@ -502,7 +557,8 @@ static char *expand_aliases(const char *line)
 }
 
 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;
@@ -522,7 +578,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
 
        /* 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);
 
@@ -578,6 +634,39 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
        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)
 {
@@ -614,7 +703,7 @@ static void sig_complete_filename(GList **list, WINDOW_REC *window,
 
        if (*line != '\0') return;
 
-       *list = filename_complete(word);
+       *list = filename_complete(word, NULL);
        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);
+       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 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);
@@ -670,10 +759,10 @@ void completion_deinit(void)
        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 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);
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);
-/* 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);
index 67557b91e783acdd3632bfb2f76e432c1aa40e9c..fe41ea3004b8d5a7fbf4ac603c39c83bcc77b9f9 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "chat-protocols.h"
 #include "chatnets.h"
+#include "servers.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);
 }
 
-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;
@@ -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();
-       else
+       else if (server != NULL && server_ischannel(server, data)) {
+               signal_emit("command join", 3, data, server, item);
+       } else {
                command_runsub("channel", data, server, item);
+       }
 }
 
 /* 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;
-        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;
-        int item_extra, linebuf_size;
+        int item_extra, linebuf_size, formatnum;
 
        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 &&
-           max_width > settings_get_int("names_max_width"))
+           settings_get_int("names_max_width") < 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));
@@ -369,6 +363,22 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
                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"),
@@ -380,15 +390,22 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
        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;
 
-               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);
@@ -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));
 
+               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,
-                                        TXT_NAMES_NICK, nickmode, linebuf);
+                                        formatnum, nickmode, linebuf);
                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);
+                       if (prefix_format != NULL)
+                                g_string_assign(str, prefix_format);
                        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_free_not_null(prefix_format);
        g_free(linebuf);
 }
 
@@ -429,9 +453,9 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
 {
        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;
 
@@ -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) {
+                       halfops++;
                        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 */
-       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,
-                   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;
@@ -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;
+       if (g_hash_table_lookup(optlist, "count") != NULL)
+               flags |= CHANNEL_NICKLIST_FLAG_COUNT;
 
         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_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);
 
@@ -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_set_options("names", "ops halfops voices normal");
+       command_set_options("names", "count ops halfops voices normal");
        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("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);
 
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_COUNT     0x10
 
 void fe_channels_nicklist(CHANNEL_REC *channel, int flags);
 
index 248369057f595da7264b12081a421e29f223eecb..6b57098abc17a3cebbe4794962c52a5df7c776bb 100644 (file)
 #include "misc.h"
 #include "levels.h"
 #include "settings.h"
+#include "irssi-version.h"
 
+#include "servers.h"
 #include "channels.h"
 #include "servers-setup.h"
 
+#include "autorun.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;
 
-void autorun_init(void);
-void autorun_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);
 
+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));
@@ -104,6 +111,7 @@ static void sig_connected(SERVER_REC *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)
@@ -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));
+       MODULE_DATA_UNSET(channel);
 }
 
 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[] = {
+               { 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" },
-               { "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 },
-               /*
                { "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 }
        };
 
@@ -139,7 +154,7 @@ void fe_common_core_init(void)
        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);
@@ -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", "hide_mirc_colors", FALSE);
        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);
 
-       autorun_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);
+
+       module_register("core", "fe");
 }
 
 void fe_common_core_deinit(void)
 {
-       autorun_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)
 {
+       int setup_changed;
+
        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);
+               setup_changed = TRUE;
        }
 
        if (cmdline_hostname != NULL) {
                /* override host name found from setup */
                settings_set_str("hostname", cmdline_hostname);
+               setup_changed = TRUE;
        }
 
        create_windows();
@@ -355,5 +376,9 @@ void fe_common_core_finish_init(void)
                                            G_LOG_LEVEL_WARNING),
                          (GLogFunc) glog_func, NULL);
 
+       if (setup_changed)
+                signal_emit("setup changed", 0);
+
+        autorun_startup();
        autoconnect_servers();
 }
index 7b8f7f9bc55ceb1bca0338c66fd300de9a240882..cf7938eaf0ec533d0efb381e0e88dad483f7ef48 100644 (file)
@@ -27,6 +27,7 @@
 #include "line-split.h"
 #include "settings.h"
 #include "irssi-version.h"
+#include "servers.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_ILLEGAL_PROTO,
        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)
 {
+       char time[10];
+
        g_return_if_fail(data != NULL);
 
        if (*data == '\0') {
+                g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME);
                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);
 }
 
+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();
@@ -153,6 +196,12 @@ static void event_command(const char *data)
 {
        const char *cmdchar;
 
+       if (*data == '\0') {
+               /* empty line, forget it. */
+                signal_stop();
+               return;
+       }
+
        /* 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);
-               signal_add_first("print text stripped", (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);
-               signal_remove("print text stripped", (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_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);
@@ -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("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);
index bc8f46919168cbe3cc2e73482b601ad8208fe63c..809726341d4fc99983cc223601b7f25de0b1c463 100644 (file)
@@ -1,7 +1,7 @@
 /*
  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
@@ -19,6 +19,7 @@
 */
 
 #include "module.h"
+#include "modules.h"
 #include "signals.h"
 #include "commands.h"
 #include "pidwait.h"
 #include <signal.h>
 #include <sys/wait.h>
 
-static GSList *processes;
+GSList *processes;
 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;
@@ -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->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->window = window;
        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;
 }
 
-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;
@@ -368,7 +369,7 @@ static void handle_exec(const char *args, GHashTable *optlist,
                        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
@@ -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"));
 
+       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);
@@ -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;
+        char *str;
        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);
@@ -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) {
-               printtext(NULL, rec->target_item->name, MSGLEVEL_CLIENTCRAP,
-                         "%s", text);
+               printtext(NULL, rec->target_item->name,
+                         rec->level, "%s", text);
        } 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;
@@ -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);
-       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_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroyed);
        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("window item destroy", (SIGNAL_FUNC) sig_window_item_destroyed);
        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;
 
+        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 */
@@ -39,6 +40,8 @@ struct PROCESS_REC {
        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);
 
index 7eb145dbd31e721e8c7671eff8edc6046ab433ea..c14ac7c3e51f5ad9ab58604530c76f5c72902bec 100644 (file)
@@ -25,6 +25,9 @@
 /* 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);
 }
@@ -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)
 {
+       if (active_win == NULL)
+               return "";
+
        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 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 ");
-       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->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,
@@ -73,6 +82,12 @@ static void cmd_ignore_show(void)
        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++) {
@@ -112,7 +127,7 @@ static void cmd_ignore(const char *data)
         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;
        }
@@ -134,6 +149,18 @@ static void cmd_ignore(const char *data)
        }
 
        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;
@@ -144,11 +171,6 @@ static void cmd_ignore(const char *data)
         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
@@ -162,24 +184,29 @@ static void cmd_unignore(const char *data)
 {
        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);
 
-       if (is_numeric(data, ' ')) {
+       if (is_numeric(mask, ' ')) {
                /* 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 &&
-                   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) {
@@ -187,8 +214,9 @@ static void cmd_unignore(const char *data)
                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)
@@ -196,7 +224,7 @@ static void sig_ignore_created(IGNORE_REC *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);
index 60d96327ddba918c357405a95d7712ea0999c537..a0967e1ab7f0165626428f6f1c7478d0d9c68fb2 100644 (file)
 #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 "lib-config/iconfig.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)
 
-#define LOG_DIR_CREATE_MODE 0770
-
 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 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;
@@ -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>]
-                    [-targets <targets>] <fname> [<levels>] */
+                    [-targets <targets>] [-colors]
+                   <fname> [<levels>] */
 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# */
-               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");
@@ -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, "colors") == NULL)
+               log->colorizer = log_colorizer_strip;
+
        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);
+               log->colorizer = log_colorizer_strip;
                 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->colorizer = log_colorizer_strip;
        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 &&
-                   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);
        }
 }
@@ -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;
-       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
-          #../../../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);
-               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);
@@ -399,23 +444,27 @@ static void autolog_open(SERVER_REC *server, const char *target)
        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;
 
-       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_open(server, *tmp);
+               autolog_open(server, server_tag, *tmp);
        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];
@@ -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);
-       if (log != NULL) log_write_rec(log, text, level);
+       if (log != NULL) {
+               log_write_rec(log, text, level);
+       }
 
        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++)
-                       log_file_write(server, *tmp, level, text, FALSE);
+                       log_file_write(server_tag, *tmp, level, text, FALSE);
                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;
 
-       if (level == MSGLEVEL_NEVER)
+       if (dest->level == MSGLEVEL_NEVER)
                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;
@@ -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++)
-               log_single_line(window, server, target, level, *tmp);
+               log_single_line(dest->window, dest->server_tag,
+                               dest->target, dest->level, *tmp);
        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;
        }
 
-       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)
 {
-       char *str, *stripped, *linestart, *tmp;
+       char *str, *linestart, *tmp;
 
        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. */
-               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);
 
@@ -532,7 +581,7 @@ static int sig_autoremove(void)
 
                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;
@@ -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,
-                   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)
@@ -615,9 +686,11 @@ void fe_log_init(void)
        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_bool("log", "autolog", FALSE);
         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);
-       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("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);
 
-       command_set_options("log open", "noopen autoopen -targets window");
+       command_set_options("log open", "noopen autoopen -targets window colors");
 }
 
 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);
-       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("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);
index fa5e88af0200cf9852398c527e4895bbaf2d6744..4b4e4db4f758776a561bdc1cd9324b8354f604b8 100644 (file)
 #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 "hilight-text.h"
 #include "ignore.h"
+
+#include "window-items.h"
+#include "fe-queries.h"
+#include "hilight-text.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;
 
@@ -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 */
-               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;
@@ -111,35 +113,28 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
        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;
 
-       g_return_val_if_fail(nick != NULL, NULL);
-
        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 :
-               (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,
@@ -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;
 
-       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);
@@ -408,11 +404,16 @@ static void print_nick_change_channel(SERVER_REC *server, const char *channel,
                                      const char *address,
                                      int ownnick)
 {
+       int level;
+
        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);
 }
@@ -444,20 +445,6 @@ static void print_nick_change(SERVER_REC *server, const char *newnick,
                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) {
@@ -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)
 {
-       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,
@@ -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);
-
-       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);
index e351dc7142f481b9054a6972be92d2db36ced374..5bbcf0b53ab61a5e54898a6ca845e8db1eb3cc28 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "module.h"
 #include "modules.h"
+#include "modules-load.h"
 #include "module-formats.h"
 #include "signals.h"
 #include "commands.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,
-                           TXT_MODULE_ALREADY_LOADED, module);
+                           TXT_MODULE_ALREADY_LOADED, rootmodule, submodule);
                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,
-                           TXT_MODULE_INVALID, module);
+                           TXT_MODULE_INVALID, rootmodule, submodule);
                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,
-                   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;
+       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;
 
-               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)
@@ -108,36 +159,67 @@ static void module_prefixes_free(char **list)
         g_free(list);
 }
 
-/* SYNTAX: LOAD <module> */
+/* SYNTAX: LOAD <module> [<submodule>] */
 static void cmd_load(const char *data)
 {
-#ifdef HAVE_GMODULE
+        char *rootmodule, *submodule;
        char **module_prefixes;
+       void *free_arg;
 
        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();
-               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);
        }
-#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)
 {
-       MODULE_REC *rec;
+       MODULE_REC *module;
+        MODULE_FILE_REC *file;
+        char *rootmodule, *submodule;
+       void *free_arg;
 
        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)
@@ -159,3 +241,22 @@ void fe_modules_deinit(void)
        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 "servers.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)
 {
+        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));
-               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)
@@ -76,16 +82,22 @@ static void signal_query_created_curwin(QUERY_REC *query)
 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);
-       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)
@@ -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)
 {
+        TEXT_DEST_REC dest;
+
        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);
 }
@@ -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;
@@ -151,6 +160,7 @@ static void cmd_window_server(const char *data)
 {
        SERVER_REC *server;
         QUERY_REC *query;
+        TEXT_DEST_REC dest;
 
        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 */
+       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);
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   TXT_QUERY_SERVER_CHANGED,
-                   query->name, server->tag);
        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);
 
-       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 (*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) {
@@ -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)
-               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);
@@ -254,13 +267,9 @@ static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
        }
 
        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);
@@ -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_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);
@@ -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("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);
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, "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");
@@ -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')
-               rec = server_setup_find(addr, -1);
+               rec = server_setup_find(addr, -1, NULL);
        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);
 }
 
-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;
-       }
 
-       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;
@@ -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 */
-               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+               printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
                            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);
        }
 }
@@ -276,7 +272,7 @@ static void sig_server_disconnected(SERVER_REC *server)
 {
        g_return_if_fail(server != NULL);
 
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
                    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);
 
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
                    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);
 
-       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)
@@ -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);
+       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_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);
@@ -345,6 +343,7 @@ void fe_server_init(void)
 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);
 
index 0d8aaa933d13db2a87650954270fe097407509b1..382a09f742add475bb5df04d1900d4a2c8f2f2fd 100644 (file)
@@ -50,7 +50,8 @@ static void set_print(SETTINGS_REC *rec)
        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)
@@ -92,7 +93,8 @@ static void cmd_set(char *data)
 
                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;
                }
 
@@ -123,8 +125,10 @@ static void cmd_set(char *data)
        }
        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);
 }
@@ -143,12 +147,13 @@ static void cmd_toggle(const char *data)
 
        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)
-               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));
+               signal_emit("setup changed", 0);
        }
 
         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);
-       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);
-       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)
@@ -241,20 +246,19 @@ static void cmd_unalias(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);
        }
-       g_free(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);
        }
@@ -262,7 +266,7 @@ static void settings_save_fe(const 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);
 }
@@ -270,29 +274,37 @@ static void settings_save_confirm(const char *line, char *fname)
 /* 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;
-       }
 
-       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)
 {
-       if (line[0] == 'Y')
+       if (i_toupper(line[0]) == 'Y')
                 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 &&
-           window->items == NULL && window->level == 0)
+           window->items == NULL && window->bound_items == NULL &&
+           window->level == 0)
                 window_destroy(window);
 }
 
@@ -194,6 +195,21 @@ void window_set_name(WINDOW_REC *window, const char *name)
        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);
@@ -375,7 +391,7 @@ int windows_refnum_last(void)
        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;
 }
@@ -395,6 +411,7 @@ GSList *windows_get_sorted(void)
         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)
 {
@@ -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(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);
@@ -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;
-       char month[100];
        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;
 
@@ -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 */
-       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)
index c9ffe69a39b563632f3261dd2cffa8e38a7d7dc6..320044598372751abfa3ab92868ae117b58c6386 100644 (file)
@@ -1,10 +1,8 @@
 #ifndef __WINDOWS_H
 #define __WINDOWS_H
 
-#include "servers.h"
-
-#define STRUCT_SERVER_REC SERVER_REC
 #include "window-item-def.h"
+#include "command-history.h"
 
 enum {
         DATA_LEVEL_NONE = 0,
@@ -19,7 +17,7 @@ typedef struct {
        unsigned int sticky:1;
 } WINDOW_BIND_REC;
 
-typedef struct {
+struct _WINDOW_REC {
        int refnum;
        char *name;
 
@@ -37,8 +35,8 @@ typedef struct {
        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 */
@@ -50,7 +48,7 @@ typedef struct {
        void *theme; /* THEME_REC */
 
        void *gui_data;
-} WINDOW_REC;
+};
 
 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_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 */
@@ -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_cmp(WINDOW_REC *w1, WINDOW_REC *w2);
 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);
index b933c068a139c5f9a7e9735625bb8532adabdf28..b4d2e7edade7ab69d3685166f994c7d994a3dae1 100644 (file)
 #include "settings.h"
 
 #include "levels.h"
+#include "servers.h"
 
 #include "fe-windows.h"
+#include "window-items.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 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)
@@ -59,11 +61,73 @@ int format_find_tag(const char *module, const char *tag)
        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);
@@ -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;
-       case '%':
-               g_string_append_c(out, '%');
-               break;
        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;
+       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 */
-               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);
@@ -114,8 +184,8 @@ int format_expand_styles(GString *out, char format)
                }
 
                /* 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'));
@@ -124,8 +194,8 @@ int format_expand_styles(GString *out, char format)
                }
 
                /* 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'));
@@ -208,14 +278,21 @@ void format_create_dest(TEXT_DEST_REC *dest,
                        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_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->hilight_priority = 0;
-        dest->hilight_color = NULL;
 }
 
 /* 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++;
-                       if (*str != '%' && format_expand_styles(tmp, *str)) {
+                       if (*str != '%' &&
+                           format_expand_styles(tmp, &str, NULL)) {
                                 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++;
-                       if (*str != '%' && format_expand_styles(tmp, *str)) {
+                       if (*str != '%' &&
+                           format_expand_styles(tmp, &str, NULL)) {
                                 str++;
                                continue;
                        }
@@ -285,7 +364,7 @@ int format_real_length(const char *str, int len)
         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;
@@ -294,11 +373,12 @@ char *format_string_expand(const char *text)
 
        out = g_string_new(NULL);
 
+       if (flags != NULL) *flags = 0;
        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);
@@ -332,7 +412,7 @@ static char *format_get_text_args(TEXT_DEST_REC *dest,
        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);
@@ -342,12 +422,10 @@ static char *format_get_text_args(TEXT_DEST_REC *dest,
                        /* 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
@@ -384,10 +462,8 @@ char *format_get_text_theme(THEME_REC *theme, const char *module,
        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);
@@ -438,8 +514,7 @@ char *format_get_text(const char *module, WINDOW_REC *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);
@@ -487,28 +562,45 @@ char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
 {
        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;
 
+       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);
 }
 
-#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)
 {
+        char *format, str[256];
        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;
 
+       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;
@@ -517,38 +609,47 @@ static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t 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)
 {
-       SERVER_REC *server;
        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;
 
-       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++;
-       }
-       if (count < 2 && lookup_servers != NULL) {
-                count++;
-               if (lookup_servers->next != NULL)
+                       if (servers->next != NULL)
+                               count++;
+               }
+               if (count < 2 && lookup_servers != NULL) {
                        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)
@@ -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),
-                      GINT_TO_POINTER(PRINTFLAG_NEWLINE),
+                      GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE),
                       "", 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 };
-       char *start;
+       const char *start;
        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;
 
-               if (isdigit((int) *str)) {
+               if (i_isdigit(*str)) {
                        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;
-                       flags &= ~PRINTFLAG_INDENT;
+                       flags &= ~GUI_PRINT_FLAG_INDENT;
                        break;
                case 1:
                        /* hilight */
-                       flags |= PRINTFLAG_BOLD;
+                       flags |= GUI_PRINT_FLAG_BOLD;
                        break;
                case 5:
                        /* blink */
-                       flags |= PRINTFLAG_BLINK;
+                       flags |= GUI_PRINT_FLAG_BLINK;
                        break;
                case 7:
                        /* reverse */
-                       flags |= PRINTFLAG_REVERSE;
+                       flags |= GUI_PRINT_FLAG_REVERSE;
                        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;
 
-       if (!isdigit((int) **str) && **str != ',') {
+       if (!i_isdigit(**str) && **str != ',') {
                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 (isdigit((int) **str)) {
+                       if (i_isdigit(**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 (!isdigit((int) **str))
+                       if (!i_isdigit(**str))
                                bg = -1;
                        else {
                                bg = **str-'0';
                                (*str)++;
-                               if (isdigit((int) **str)) {
+                               if (i_isdigit(**str)) {
                                        bg = bg*10 + (**str-'0');
                                        (*str)++;
                                }
@@ -772,7 +873,10 @@ char *strip_codes(const char *input)
                                 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;   
@@ -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);
+               } 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);
-                       flags &= ~PRINTFLAG_INDENT;
+                       flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL);
                }
 
                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)
-                               flags ^= PRINTFLAG_BOLD;
+                               flags ^= GUI_PRINT_FLAG_BOLD;
                        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 */
-                       flags &= ~PRINTFLAG_MIRC_COLOR;
+                       flags &= ~GUI_PRINT_FLAG_MIRC_COLOR;
                        switch (*ptr) {
                        case FORMAT_STYLE_BLINK:
-                               flags ^= PRINTFLAG_BLINK;
+                               flags ^= GUI_PRINT_FLAG_BLINK;
                                break;
                        case FORMAT_STYLE_UNDERLINE:
-                               flags ^= PRINTFLAG_UNDERLINE;
+                               flags ^= GUI_PRINT_FLAG_UNDERLINE;
                                break;
                        case FORMAT_STYLE_BOLD:
-                               flags ^= PRINTFLAG_BOLD;
+                               flags ^= GUI_PRINT_FLAG_BOLD;
                                break;
                        case FORMAT_STYLE_REVERSE:
-                               flags ^= PRINTFLAG_REVERSE;
+                               flags ^= GUI_PRINT_FLAG_REVERSE;
                                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;
+                       }
                        case FORMAT_STYLE_DEFAULTS:
                                fgcolor = bgcolor = -1;
-                               flags &= PRINTFLAG_INDENT;
+                               flags &= GUI_PRINT_FLAG_INDENT;
                                break;
+                       case FORMAT_STYLE_CLRTOEOL:
+                                break;
                        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;
-                                               flags |= PRINTFLAG_BOLD;
+                                               flags |= GUI_PRINT_FLAG_BOLD;
                                        }
                                }
                                ptr++;
-                               if (*ptr != FORMAT_COLOR_NOCHANGE)
+                               if (*ptr != FORMAT_COLOR_NOCHANGE) {
                                        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)
-                               flags ^= PRINTFLAG_BLINK;
+                               flags ^= GUI_PRINT_FLAG_BLINK;
                        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)
-                               flags ^= PRINTFLAG_REVERSE;
+                               flags ^= GUI_PRINT_FLAG_REVERSE;
                        break;
                case 31:
                        /* underline */
                        if (!hide_text_style)
-                               flags ^= PRINTFLAG_UNDERLINE;
+                               flags ^= GUI_PRINT_FLAG_UNDERLINE;
                        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;
                }
 
@@ -920,11 +1049,16 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
 
 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");
-       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)
index 86c53e69f28921ca167dfe920346cb24fdbf2cf1..b98d7be75787e927fa573c58e74df9ae42e4695f 100644 (file)
@@ -4,13 +4,15 @@
 #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
@@ -30,16 +32,32 @@ struct _FORMAT_REC {
        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;
+        const char *server_tag; /* if server is non-NULL, must be server->tag */
        const char *target;
        int level;
 
        int hilight_priority;
        char *hilight_color;
+        int flags;
 } 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) */
@@ -49,7 +67,7 @@ int format_get_length(const char *str);
    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,
@@ -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_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);
 
@@ -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_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);
index 197957f6cf8b3f89acebc22aa8442c8555a5c79a..0bdda55ff2a488c7ee27a1918c1dd0ccf367f3b1 100644 (file)
@@ -37,8 +37,6 @@
 #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;
 
@@ -264,7 +262,7 @@ static char *hilight_get_color(HILIGHT_REC *rec)
        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)
@@ -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;
 
+        g_free_not_null(dest->hilight_color);
         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);
-
-               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 */
-               char *tmp = strip_codes(str);
+               char *tmp = strip_codes(text);
                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 */
-               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 */
-               middle = strip_codes(str+pos);
+               middle = strip_codes(text+pos);
                 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 */
-               pos = strip_real_length(str, next_hilight_end,
+               pos = strip_real_length(text, hilight_end,
                                        &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);
@@ -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);
-               g_string_append(tmp, str+pos);
+               g_string_append(tmp, text+pos);
                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);
@@ -397,7 +381,6 @@ char *hilight_match_nick(SERVER_REC *server, const char *channel,
        color = rec == NULL || !rec->nick ? NULL :
                hilight_get_color(rec);
 
-        next_nick_hilight = rec;
        return color;
 }
 
@@ -416,7 +399,8 @@ static void read_hilight_config(void)
                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)
@@ -567,16 +551,14 @@ static void cmd_hilight(const char *data)
        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);
-               else
-                       g_free_and_null(rec->color);
        }
        if (actcolorarg != NULL) {
+               g_free_and_null(rec->act_color);
                if (*actcolorarg != '\0')
                        rec->act_color = g_strdup(actcolorarg);
-               else
-                       g_free_and_null(rec->act_color);
        }
 
 #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");
 
-       next_nick_hilight = NULL;
-       next_line_hilight = NULL;
-
         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);
@@ -687,7 +665,6 @@ void hilight_text_deinit(void)
        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);
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];
 
-/* 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;
-/* List of all key combo names */
-static GSList *key_combos;
 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
@@ -98,7 +69,6 @@ void keyboard_destroy(KEYBOARD_REC *keyboard)
        signal_emit("keyboard destroyed", 1, keyboard);
 
         g_free_not_null(keyboard->key_state);
-        g_free_not_null(keyboard->key_prev_state);
         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);
 
-       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)
@@ -180,8 +151,10 @@ static void keyconfig_clear(const char *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)
@@ -198,69 +171,170 @@ KEYINFO_REC *key_info_find(const char *id)
        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;
-        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)
-               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;
 
-               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)
@@ -455,7 +529,8 @@ static int key_emit_signal(KEYBOARD_REC *keyboard, KEY_REC *key)
         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)
@@ -463,7 +538,7 @@ int key_states_search(const char *combo, const char *search)
                 search++; combo++;
        }
 
-       return *combo == '\0' || *combo == '-' ? 0 : -1;
+        return 0;
 }
 
 /* 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;
-       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);
 
-       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,
@@ -771,7 +795,8 @@ static void read_keyboard_config(void)
                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();
@@ -785,7 +810,6 @@ void keyboard_init(void)
                                        (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));
 
index 0f37ccfd649173b1e3cd28647f4a3a7312711290..2a5c8f93e63697070a71cc500a29050e9816948a 100644 (file)
@@ -28,21 +28,39 @@ FORMAT_REC fecommon_core_formats[] = {
        { 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 } },
-       { "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 } },
-       { "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 },
-       { "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 },
+       { "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 },
@@ -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 } },
+       { "your_nick", "Your nickname is {nick $0}", 1, { 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 } },
+       { "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 } },
@@ -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 } },
+       { "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 } },
-       { "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 } },
-       { "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 } },
@@ -117,7 +141,8 @@ FORMAT_REC fecommon_core_formats[] = {
        /* ---- */
        { 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 } },
@@ -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_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 } },
@@ -162,14 +187,15 @@ FORMAT_REC fecommon_core_formats[] = {
        /* ---- */
        { 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_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 },
@@ -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 },
+       { "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 },
 
        /* ---- */
@@ -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 } },
-       { "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 } },
@@ -213,16 +243,22 @@ FORMAT_REC fecommon_core_formats[] = {
        { 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 } },
-       { "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 } },
-       { "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 },
+       { "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 }
 };
index e0f31f9aff5348de77c3b4204a5155f7658a7e2b..fcfd87d5c9ca3a4812373ebe175b2ca63b17c322 100644 (file)
@@ -14,13 +14,31 @@ enum {
        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_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,
 
@@ -43,6 +61,7 @@ enum {
        TXT_SETUPSERVER_ADDED,
        TXT_SETUPSERVER_REMOVED,
        TXT_SETUPSERVER_NOT_FOUND,
+       TXT_YOUR_NICK,
 
        TXT_FILL_3,
 
@@ -52,6 +71,7 @@ enum {
        TXT_QUIT,
        TXT_QUIT_ONCE,
        TXT_INVITE,
+       TXT_NOT_INVITED,
        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_NAMES_PREFIX,
+       TXT_NAMES_NICK_OP,
+       TXT_NAMES_NICK_HALFOP,
+       TXT_NAMES_NICK_VOICE,
        TXT_NAMES_NICK,
        TXT_ENDOFNAMES,
        TXT_CHANLIST_HEADER,
@@ -91,7 +115,8 @@ enum {
 
        TXT_FILL_5,
 
-       TXT_QUERY_STARTED,
+       TXT_QUERY_START,
+       TXT_QUERY_STOP,
        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_NOT_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_ILLEGAL_PROTO,
        TXT_NOT_GOOD_IDEA,
 
        TXT_FILL_11,
@@ -161,7 +188,10 @@ enum {
        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_REMOVED,
        TXT_FORMAT_TITLE,
        TXT_FORMAT_SUBTITLE,
        TXT_FORMAT_ITEM,
@@ -188,7 +218,13 @@ enum {
        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[];
index 38de158d3eb8ecf0b74158341d65fddcab167e6c..1900adec34916a6e532a985d5e5b64f705cc7249 100644 (file)
 
 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_finished;
 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 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];
@@ -54,8 +52,7 @@ static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest,
        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],
@@ -77,6 +74,16 @@ static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest,
        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)
@@ -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);
-       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,
@@ -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);
-       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,
@@ -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));
 
-       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);
@@ -150,22 +158,22 @@ void printformat_module_gui(const char *module, int formatnum, ...)
 
 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);
 
-       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);
 
-       /* 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(stripped);
 }
 
 /* append string to `out', expand newlines. */
@@ -237,7 +245,7 @@ static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str,
                        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);
                        }
@@ -250,7 +258,7 @@ static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str,
        return ret;
 }
 
-static char *printtext_expand_formats(const char *str)
+static char *printtext_expand_formats(const char *str, int *flags)
 {
        GString *out;
        char *ret;
@@ -265,7 +273,7 @@ static char *printtext_expand_formats(const char *str)
                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);
                }
@@ -276,7 +284,7 @@ static char *printtext_expand_formats(const char *str)
        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;
 
@@ -291,6 +299,15 @@ void printtext_dest(TEXT_DEST_REC *dest, const char *text, va_list va)
        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, ...)
 {
@@ -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);
-       printtext_dest(&dest, text, va);
+       printtext_dest_args(&dest, text, 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;
        }
 
-        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);
 }
@@ -338,7 +377,7 @@ void printtext_window(WINDOW_REC *window, int level, const char *text, ...)
                           window != NULL ? window : active_win);
 
        va_start(va, text);
-       printtext_dest(&dest, text, va);
+       printtext_dest_args(&dest, text, va);
        va_end(va);
 }
 
@@ -351,14 +390,14 @@ void printtext_gui(const char *text)
 
         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)
 {
-       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)) &&
@@ -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);
+       g_return_if_fail(dest->window != NULL);
        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 */
-       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);
 
-       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,
@@ -433,16 +469,13 @@ static void read_settings(void)
 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_finished = signal_get_uniq_id("print text finished");
        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);
-       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);
 }
@@ -450,7 +483,6 @@ void printtext_init(void)
 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);
 }
index 14f46564489659b85f0799cd9e574895cd01082a..463f3572810357cc2756d06752093f19a84e526f 100644 (file)
@@ -2,17 +2,22 @@
 #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_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_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_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_dest(TEXT_DEST_REC *dest, const char *text, ...);
 
 /* 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)
+#  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)
@@ -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__)
+#  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
@@ -75,6 +84,20 @@ void printformat_window(WINDOW_REC *window, int level, int formatnum, ...)
        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
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);
-       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;
 }
@@ -128,9 +129,9 @@ static const char *fgcolorformats = "nkrgybmpcwKRGYBMPCW";
 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) \
-        ((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)
@@ -143,7 +144,7 @@ static void theme_format_append_variable(GString *str, const char **format)
        (*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)++;
 
@@ -211,7 +212,9 @@ static void theme_format_append_next(THEME_REC *theme, GString *str,
                        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;
        }
 
@@ -232,6 +235,54 @@ static void theme_format_append_next(THEME_REC *theme, GString *str,
         (*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,
@@ -258,17 +309,11 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
           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;
 
@@ -287,7 +332,7 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
                                        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;
@@ -300,7 +345,8 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
                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;
@@ -701,11 +747,11 @@ THEME_REC *theme_load(const char *setname)
        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);
-               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);
@@ -795,9 +841,11 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data)
        }
 
        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) {
@@ -952,8 +1000,13 @@ static void cmd_format(const char *data)
         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,
-                        CONFIG_REC *config)
+                        THEME_SAVE_REC *data)
 {
        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;
 
-       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);
-       for (n = 0; formats[n].def != NULL; n++) {
+       for (n = 1; formats[n].def != NULL; n++) {
                 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]);
-                }
+               } 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 */
-                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;
+       THEME_SAVE_REC data;
        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;
 
@@ -1017,16 +1078,27 @@ static void theme_save(THEME_REC *theme)
        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;
+        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;
 
-               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)
@@ -1102,9 +1174,9 @@ static void change_theme(const char *name, int 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,
@@ -1115,9 +1187,13 @@ static void change_theme(const char *name, int verbose)
 static void read_settings(void)
 {
        const char *theme;
+        int len;
 
        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);
 }
 
@@ -1131,11 +1207,9 @@ static void themes_read(void)
        /* 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->default_color = 0;
-               current_theme->default_real_color = 7;
+               current_theme->default_color = -1;
                 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");
+       command_set_options("save", "formats");
 }
 
 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
-                             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 */
@@ -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 */
-#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
index 2713cc7392dba4b30ef48bf5f42df42dd4849a65..d573fc1742b41c3546ea2620d9b67af51fa280ab 100644 (file)
 */
 
 #include "module.h"
+#include "module-formats.h"
 #include "signals.h"
 #include "line-split.h"
 #include "misc.h"
+#include "levels.h"
 #include "settings.h"
 
+#include "printtext.h"
+
 unsigned char translation_in[256], translation_out[256];
+static char *current_translation;
 
 void translation_reset(void)
 {
@@ -45,7 +50,7 @@ void translate_output(char *text)
 }
 
 #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)
 {
@@ -81,7 +86,12 @@ int translation_read(const char *file)
        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) {
@@ -95,20 +105,40 @@ int translation_read(const char *file)
        line_split_free(buffer);
 
        close(f);
-       if (pos != 512)
+       if (pos != 512) {
                translation_reset();
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_TRANSLATION_FILE_ERROR, file);
+       }
        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();
 
+        current_translation = NULL;
        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"
 
-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] */
@@ -77,7 +194,7 @@ static void cmd_window_close(const char *data)
        }
 
        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;
@@ -224,7 +341,7 @@ static void cmd_window_server(const char *data)
                            "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;
@@ -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,
-                                  TXT_UNSET_SERVER_STICKY, server->tag);
+                                  TXT_UNSET_SERVER_STICKY);
        }
 
        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)
 {
-        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 */
-                window = window_find_refnum(atoi(data));
+                window = window_find_refnum(atoi(target));
         } 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_item_set_active(window, item);
+               window_item_set_active(window, item);
+
+       cmd_params_free(free_arg);
 }
 
 /* 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)
 {
-        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
-   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;
-       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);
-               if (window == NULL) break;
+               if (window == NULL) {
+                        new_refnum++;
+                       break;
+               }
 
                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 */
-static void windows_move_right(WINDOW_REC *move_window)
+static void window_refnums_move_right(WINDOW_REC *move_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 */
-       refnum = 1;
-       while (window_find_refnum(refnum) != NULL) refnum++;
-
+       refnum = new_refnum;
+       while (move_window->refnum == refnum ||
+              window_find_refnum(refnum) != NULL) refnum++;
        refnum--;
-       while (refnum > 0) {
+
+       while (refnum >= new_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--;
        }
+
+       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;
 
-       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;
        }
 
-       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;
 
-       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;
        }
 
-        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;
@@ -463,23 +613,49 @@ static void cmd_window_list(void)
         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;
+       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,
-                                  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 {
-               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)
@@ -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;
-       GSList *tmp;
+        GSList *list;
 
         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);
+                list = g_slist_remove(list, list->data);
        }
+
         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 history", NULL, (SIGNAL_FUNC) cmd_window_history);
        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);
@@ -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 theme", "delete");
 }
 
 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 history", (SIGNAL_FUNC) cmd_window_history);
        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);
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(item->window == NULL);
 
         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);
 
-       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);
        }
@@ -66,7 +68,7 @@ void window_item_remove(WI_ITEM_REC *item)
 
        window = window_item_window(item);
 
-       if (g_slist_find(window->items, item) == NULL)
+       if (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);
-
-       signal_emit("window item destroy", 2, window, item);
+        item->destroy(item);
 }
 
 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;
+        WINDOW_BIND_REC *bind;
        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;
@@ -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? */
-               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:
@@ -290,6 +295,10 @@ void window_item_create(WI_ITEM_REC *item, int automatic)
 
        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 */
@@ -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", "autocreate_split_windows", FALSE);
+       settings_add_bool("lookandfeel", "autofocus_new_items", TRUE);
 
        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"
 
-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;
@@ -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 */
+               signal_add("query created",
+                          (SIGNAL_FUNC) signal_query_created_curwin);
+
+                restore_win = window;
                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;
 
-       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) {
-                       signal_emit("window restore item", 3,
+                       signal_emit("layout restore item", 3,
                                    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;
@@ -85,13 +107,18 @@ void windows_layout_restore(void)
        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;
 
-               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_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));
@@ -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));
-               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)
@@ -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->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) {
@@ -161,7 +190,7 @@ static void window_save(WINDOW_REC *window, CONFIG_NODE *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)
@@ -172,7 +201,7 @@ void windows_layout_save(void)
        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);
@@ -181,16 +210,20 @@ void windows_layout_save(void)
 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)
 {
-       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)
 {
-       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
 
-ADD_INCLUDES = \
-       $(GLIB_CFLAGS) \
+INCLUDES = \
        -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/ \
+       $(GLIB_CFLAGS) \
        $(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@ \
-       $(PROG_LIBS) \
-        $(CURSES_LIBS) \
+       @PERL_LINK_FLAGS@ \
+       @PROG_LIBS@     \
        -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 \
@@ -33,13 +54,19 @@ silc_SOURCES = \
        lastlog.c \
         mainwindows.c \
         mainwindow-activity.c \
-        mainwindows-save.c \
-        screen.c \
+        mainwindows-layout.c \
         statusbar.c \
+        statusbar-config.c \
         statusbar-items.c \
+        term.c \
+       term-dummy.c \
+       $(use_tparm_sources) \
+       $(use_term_sources) \
         textbuffer.c \
         textbuffer-commands.c \
+        textbuffer-reformat.c \
         textbuffer-view.c \
+       utf8.c \
         silc.c \
        module-formats.c
 
@@ -50,8 +77,17 @@ noinst_HEADERS = \
         gui-windows.h \
         mainwindows.h \
         statusbar.h \
-        screen.h \
+        statusbar-config.h \
+       term.h \
+       terminfo-core.h \
         textbuffer.h \
         textbuffer-view.h \
+        textbuffer-reformat.h \
+       utf8.h \
        module.h \
        module-formats.h
+
+EXTRA_DIST = \
+       $(tparm_sources) \
+       $(terminfo_sources) \
+       $(curses_sources)
index 7544539a304c4059ef586f1d263bfe2e7aee4764..f70bf0dfe04e22279198230f460aed0f90edbdc8 100644 (file)
 */
 
 #include "module.h"
+#include "utf8.h"
 #include "formats.h"
 
+#include "gui-entry.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 {
-               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_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_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
-       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++;
-#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;
-       
-       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 (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--;
        }
+
+        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--;
        }
+
+        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)
-               gui_entry_move_words_left(-count);
+               gui_entry_move_words_left(entry, -count, to_space);
        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
 
-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
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)
 {
-       return gui_entry_get_text();
+       return gui_entry_get_text(active_entry);
 }
 
 /* value of cutbuffer */
index 5faae0fa3b53b2d025a726dae523ff5124081aaa..3bd1de3696557b17dd0f6d0784a767a0edbab2bb 100644 (file)
 #include "formats.h"
 #include "printtext.h"
 
-#include "screen.h"
+#include "term.h"
+#include "gui-printtext.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_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 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;
@@ -47,6 +102,18 @@ void gui_printtext(int xpos, int ypos, const char *str)
        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;
@@ -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) {
-                       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);
@@ -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, ... */
-               *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)
 {
-       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++] = 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++] = 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++] = 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++] = 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;
        }
 
-       *line = textbuffer_insert(buffer, *line, data, pos, NULL);
+        if (pos > 0)
+               *line = textbuffer_insert(buffer, *line, data, pos, NULL);
 
        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)
@@ -152,22 +230,28 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
                               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;
-       int fg, bg, flags;
+       int fg, bg, flags, attr;
 
        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);
 
-               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;
        }
@@ -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);
 
-       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);
-       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;
 
-        last_color = 0;
+        last_fg = last_bg = -1;
        last_flags = 0;
 
        view = WINDOW_GUI(window)->view;
@@ -202,74 +296,36 @@ static void sig_printtext_finished(WINDOW_REC *window)
        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");
-        scrollback_save_formats = settings_get_bool("scrollback_save_formats");
 }
 
 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_bool("history", "scrollback_save_formats", FALSE);
 
        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("beep", (SIGNAL_FUNC) beep);
 
        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("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("beep", (SIGNAL_FUNC) beep);
 }
index 3b2098b7805a25dcecf315f7d32d0a30a6f479a7..47cd3c2322bfc0248747169d3810d8f831885a25 100644 (file)
@@ -2,12 +2,21 @@
 #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);
 
+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_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str);
 
 #endif
index bc3429d8b04d8ee0f16338146e86a2f484cdd36c..8fe1741089524393a68f6032d7220fd4f7ad5094 100644 (file)
 #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 "screen.h"
+#include "term.h"
 #include "gui-entry.h"
 #include "gui-windows.h"
 
@@ -51,6 +52,25 @@ char *cutbuffer;
 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;
@@ -63,8 +83,7 @@ static void handle_key_redirect(int key)
        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)
@@ -72,7 +91,7 @@ static void handle_entry_redirect(const char *line)
        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;
@@ -83,8 +102,7 @@ static void handle_entry_redirect(const char *line)
                     active_win->active);
        }
 
-       gui_entry_remove_perm_prompt();
-       window_update_prompt();
+       gui_entry_set_prompt(active_entry, "");
 }
 
 static int get_scroll_count(void)
@@ -99,8 +117,9 @@ static int get_scroll_count(void)
        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;
 }
 
@@ -141,36 +160,44 @@ void handle_key(int key)
 
        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)
 {
-       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;
 
+       /* 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);
 
-       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)
-                       add_history = FALSE;
+                        g_free_and_null(add_history);
                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);
 }
 
@@ -182,83 +209,93 @@ static void key_backward_history(void)
 {
        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;
 
-       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)
 {
-        gui_entry_set_pos(0);
+        gui_entry_set_pos(active_entry, 0);
 }
 
 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)
 {
-       gui_entry_move_pos(-1);
+       gui_entry_move_pos(active_entry, -1);
 }
 
 static void key_forward_character(void)
 {
-       gui_entry_move_pos(1);
+       gui_entry_move_pos(active_entry, 1);
 }
 
 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)
 {
-       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);
-       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;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
        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;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
        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)
-               gui_entry_insert_text(cutbuffer);
+               gui_entry_insert_text(active_entry, cutbuffer);
 }
 
 static void key_transpose_characters(void)
@@ -266,61 +303,72 @@ static void key_transpose_characters(void)
        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')
-               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)
 {
-       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)
 {
-       gui_entry_erase(1);
+       gui_entry_erase(active_entry, 1);
 }
 
 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)
 {
-       gui_entry_erase_next_word();
+       gui_entry_erase_next_word(active_entry, FALSE);
 }
 
 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);
 }
 
-static void key_word_completion(void)
+static void key_completion(int erase)
 {
        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) {
-               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);
        }
 }
 
+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;
 
-       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) {
-               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);
        }
 }
@@ -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);
-       gui_entry_insert_text(str);
+       gui_entry_insert_text(active_entry, str);
         g_free(str);
 }
 
+static void key_sig_stop(void)
+{
+        term_stop();
+}
+
 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,
@@ -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)
-               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)
@@ -499,17 +566,18 @@ void gui_readline_init(void)
        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();
 
+       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, "^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);
@@ -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);
 
-        /* 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_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);
@@ -554,13 +629,13 @@ void gui_readline_init(void)
        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_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_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);
@@ -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);
-       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);
@@ -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);
 
+        /* autoreplaces */
        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);
@@ -604,6 +682,9 @@ void gui_readline_init(void)
                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);
@@ -613,7 +694,7 @@ void gui_readline_init(void)
 void gui_readline_deinit(void)
 {
        g_free_not_null(cutbuffer);
-       g_source_remove(readtag);
+        input_listen_deinit();
 
         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("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);
 
@@ -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_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);
@@ -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("stop_irc", (SIGNAL_FUNC) key_sig_stop);
         keyboard_destroy(keyboard);
 
         key_configure_thaw();
index 6464921f59e56b636b3c79e06e030ba9925683de..9ce3a742412b036390ce64ced9c70e5a4762b7d2 100644 (file)
@@ -3,6 +3,9 @@
 
 extern char *cutbuffer;
 
+void input_listen_init(int handle);
+void input_listen_deinit(void);
+
 void readline(void);
 time_t get_idle_time(void);
 
index 5d7ad0d734b150b306f74cdf072b46b17580da19..cd834ee48c791f1c387710e67098d08ee70bb802 100644 (file)
 #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;
 
-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;
-        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,
+                                          settings_get_bool("scroll"),
+                                          settings_get_bool("term_utf8"));
+       textbuffer_view_set_default_indent(gui->view,
                                           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;
 }
 
@@ -61,33 +65,37 @@ static void sig_window_create_override(gpointer 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;
+        int empty_window, new_parent;
 
        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. */
-               parent = WINDOW_GUI(active_win)->parent;
+               parent = WINDOW_MAIN(active_win);
        }
        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);
+
+       /* 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);
 }
 
@@ -101,21 +109,29 @@ static void gui_window_destroyed(WINDOW_REC *window)
        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;
 
-       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;
 
+       if (window->width == width && window->height == height)
+                return;
+
        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);
@@ -138,68 +154,66 @@ void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line)
        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;
 
-       oldparent = WINDOW_GUI(window)->parent;
+       oldparent = WINDOW_MAIN(window);
        if (oldparent == parent)
                return;
 
+        gui_window_set_unsticky(window);
        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)
@@ -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;
 
-               if (rec->sticky_windows == NULL)
+               if (!rec->sticky_windows)
                         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;
-       WINDOW_REC *old_window;
+        WINDOW_REC *old_window;
 
        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;
-       } 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 */
-                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();
@@ -248,54 +262,20 @@ static void signal_window_changed(WINDOW_REC *window)
        }
 
        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)
 {
-       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)
@@ -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_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();
@@ -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 item remove", (SIGNAL_FUNC) sig_check_window_update);
        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);
 
@@ -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 item remove", (SIGNAL_FUNC) sig_check_window_update);
        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))
+#define WINDOW_MAIN(a) (WINDOW_GUI(a)->parent)
 
 #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;
 
+       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;
@@ -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 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
index 75e68da3188fc83aa9a0bec11f7da87c915062d7..61fb5fc1d2a4617af6a30977f1789368c8356599 100644 (file)
 #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)
 {
-        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;
-       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);
+                line = next;
        }
         textbuffer_view_redraw(view);
-        screen_refresh_thaw();
+        term_refresh_thaw();
 }
 
 /* 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;
-       int level, len;
+       int level, before, after, len;
 
         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;
 
-       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,
-                                   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);
@@ -147,15 +163,20 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        if (count <= 0)
                tmp = list;
        else {
-               int pos = len-count;
-
+               int pos = len-count-start;
                if (pos < 0) pos = 0;
-               pos += start;
 
                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,
@@ -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;
 
+               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")) {
@@ -207,9 +242,10 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        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;
@@ -219,13 +255,14 @@ static void cmd_lastlog(const char *data)
 
        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;
 
-       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 = "";
        }
@@ -258,7 +295,7 @@ void lastlog_init(void)
 {
        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)
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 "screen.h"
-#include "statusbar.h"
+#include "term.h"
 #include "gui-windows.h"
 
 #define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1)
 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)
 {
@@ -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;
 
-               space = rec->height;
+               space = MAIN_WINDOW_TEXT_HEIGHT(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;
 }
 
-#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)
 {
-       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;
-#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;
 
-               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)
@@ -107,11 +174,10 @@ void mainwindows_recreate(void)
        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,
-                                          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);
-       rec->width = screen_width;
-       rec->statusbar_lines = 1;
+       rec->dirty = TRUE;
+       rec->width = term_width;
 
        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 {
-               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 */
 
-               space = (parent->height-parent->statusbar_lines)/2;
+               space = parent->height / 2;
                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;
-               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);
        }
 
-#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);
@@ -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->last_line = last_line-rec->statusbar_lines;
+               rec->last_line = last_line;
                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;
 
-               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);
        }
 }
@@ -234,20 +299,18 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window)
 {
        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);
 
+        term_window_destroy(window->screen_win);
+
        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();
-               statusbar_redraw(NULL);
        }
+
        g_free(window);
 
        if (active_mainwin == window) active_mainwin = NULL;
@@ -257,13 +320,12 @@ void mainwindows_redraw(void)
 {
         GSList *tmp;
 
-       screen_refresh_freeze();
+        irssi_set_dirty();
        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)
@@ -289,62 +351,46 @@ GSList *mainwindows_get_sorted(int reverse)
        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)
 {
+        MAIN_WINDOW_REC *rec;
        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 */
-       sorted = mainwindows_get_sorted(TRUE);
        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) {
+                       mainwindow_resize(rec, xdiff, 0);
+
                        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);
        }
+
+       if (xdiff != 0) {
+               while (tmp != NULL) {
+                       mainwindow_resize(tmp->data, xdiff, 0);
+                       tmp = tmp->next;
+               }
+       }
+
        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;
 
-               space = rec->height-WINDOW_MIN_SIZE;
+               space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE;
                if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) {
+                       mainwindow_resize(rec, xdiff, 0);
                        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;
 
-       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)
@@ -428,104 +482,197 @@ void mainwindows_resize(int width, int height)
         else if (xdiff != 0)
                mainwindows_resize_horiz(xdiff);
 
+        signal_emit("terminal resized", 0);
+
        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;
 
-       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);
-               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;
 }
 
+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)
 {
+        irssi_set_dirty();
+
        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;
 
-       /* shrink lower window */
        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;
-       } 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;
+               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;
 }
 
-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;
 
-       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;
-       } 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;
+               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;
 }
 
-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
-               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>] */
@@ -535,9 +682,9 @@ static void cmd_window_grow(const char *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);
        }
@@ -546,13 +693,10 @@ static void cmd_window_grow(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);
-       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);
        }
@@ -567,7 +711,8 @@ static void cmd_window_size(const char *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);
@@ -587,33 +732,32 @@ static void cmd_window_balance(void)
        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);
-        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;
-               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--;
                }
-               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();
-       statusbar_redraw(NULL);
 }
 
 /* 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_GUI(window)->parent->sticky_windows != NULL) {
+       if (WINDOW_MAIN(window)->sticky_windows) {
                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) {
-               active_mainwin = WINDOW_GUI(active_win)->parent;
+               active_mainwin = WINDOW_MAIN(active_win);
                 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_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();
-       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);
 
+       if (settings_get_bool("autostick_split_windows"))
+                gui_window_set_sticky(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);
+       if (rec == NULL)
+               rec = mainwindows_find_upper(term_height);
        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);
+       if (rec == NULL)
+               rec = mainwindows_find_lower(-1);
        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)
 {
-        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)
 {
-        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)
 {
-       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;
                }
-                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 */
-               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 {
-                       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 {
-                /* 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);
        }
 }
 
+/* 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)
 {
-       screen_width = COLS;
-       screen_height = LINES;
+       old_screen_width = term_width;
+       old_screen_height = term_height;
 
        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);
@@ -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 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)
@@ -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 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"
-#include "screen.h"
+#include "term.h"
 
 #define WINDOW_MIN_SIZE 2
 
+#define MAIN_WINDOW_TEXT_HEIGHT(window) \
+        ((window)->height-(window)->statusbar_lines)
+
 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;
+extern int screen_reserved_top, screen_reserved_bottom;
 
 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 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);
 
-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
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 } },
+       { "lastlog_count", "{hilight Lastlog}: $0 lines", 1, { 1 } },
        { "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 },
@@ -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_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 }
 };
index fe33c8e269e34b6021ab8f7088a49a1048f90cd5..2bbf4007260498402e565e5960ed2fa20ae7d133 100644 (file)
@@ -4,8 +4,10 @@ enum {
        TXT_MODULE_NAME,
 
         TXT_LASTLOG_TOO_LONG,
+        TXT_LASTLOG_COUNT,
        TXT_LASTLOG_START,
        TXT_LASTLOG_END,
+       TXT_LASTLOG_SEPARATOR,
 
         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_WINDOW_UNSET_STICKY
+       TXT_WINDOW_UNSET_STICKY,
+        TXT_WINDOW_INFO_STICKY,
+        TXT_WINDOW_SCROLL,
+        TXT_WINDOW_SCROLL_UNKNOWN
 };
 
 extern FORMAT_REC gui_text_formats[];
index ba5888adebc69132cd4f8d06d8842dbbf24f9bad..c66435453654b6ca3de44506af85712a6ea1a68e 100644 (file)
@@ -4,3 +4,4 @@
 
 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 "modules-load.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 "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 "textbuffer-reformat.h"
 
 #include <signal.h>
 
@@ -64,8 +67,13 @@ void lastlog_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;
@@ -89,25 +97,51 @@ static void sig_exit(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 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();
@@ -116,36 +150,44 @@ static void textui_init(void)
        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;
 
-       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();
-
-       fe_common_core_finish_init();
+       module_register("core", "fe-text");
 
 #ifdef HAVE_STATIC_PERL
        perl_core_init();
-        fe_perl_init();
+       fe_perl_init();
 #endif
+
+       dirty_check();
+
+       fe_common_core_finish_init();
        signal_emit("irssi init finished", 0);
 
 #if 0
@@ -154,42 +196,45 @@ static void textui_finish_init(void)
                                 "%s", firsttimer_text);
        }
 #endif
-
-       screen_refresh_thaw();
 }
 
 static void textui_deinit(void)
 {
        signal(SIGINT, SIG_DFL);
 
-        screen_refresh_freeze();
+        term_refresh_freeze();
        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
-        fe_perl_deinit();
         perl_core_deinit();
+        fe_perl_deinit();
 #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();
@@ -205,7 +250,7 @@ static void check_oldcrap(void)
         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);
@@ -221,7 +266,7 @@ static void check_oldcrap(void)
                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");
@@ -229,7 +274,7 @@ static void check_oldcrap(void)
 
        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);
 }
@@ -237,16 +282,13 @@ static void check_oldcrap(void)
 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();
        }
-        g_free(path);
 }
 
 #ifdef WIN32
@@ -266,7 +308,17 @@ static void winsock_init(void)
 
 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();
+
 #ifdef WIN32
         winsock_init();
 #endif
@@ -279,29 +331,33 @@ int main(int argc, char **argv)
        textdomain(PACKAGE);
 #endif
 
-       quitting = FALSE;
-
        textui_init();
+       args_register(options);
        args_execute(argc, argv);
+
+#if 0
        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);
 
+       /* 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);
-                screen_check_resizes();
+                dirty_check();
        }
 
        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;
 }
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 "misc.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 "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 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 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;
-        char *ret;
+        char *ret, *name, *format, *value;
         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;
 
@@ -214,26 +89,41 @@ static char *get_activity_list(int normal, int hilight)
                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:
+                       name = "{sb_act_text %d}";
                        break;
                case DATA_LEVEL_MSG:
-                        g_string_append(str, "%W");
+                       name = "{sb_act_msg %d}";
                        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;
                }
-               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;
@@ -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. */
-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;
        }
 
-       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(detlist);
 }
 
 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);
-               statusbar_item_redraw(activity_item);
+               statusbar_items_redraw("act");
                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);
-                       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. */
-                       statusbar_item_redraw(activity_item);
+                       statusbar_items_redraw("act");
                }
                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);
 
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
 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);
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
 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;
        }
 
-       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;
        }
 
-       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;
        }
 
-       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)
 {
-       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)
 {
-       GSList *tmp;
-
        settings_add_int("misc", "lag_min_show", 100);
-       settings_add_bool("lookandfeel", "topicbar", TRUE);
        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);
 
-       /* 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();
-       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)
 {
-       /* 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);
-
-       /* 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 "expandos.h"
+#include "special-vars.h"
 
 #include "themes.h"
 
 #include "statusbar.h"
+#include "statusbar-config.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);
 
-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)
 {
-       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;
@@ -71,6 +168,9 @@ static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
         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;
@@ -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 */
-                        size -= rec->size+1; /* +1 == the marginal */
+                        size -= rec->size;
                        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;
-       width = -1; /* -1 because of the marginals */
+       width = 0;
        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) {
-                        /* +1 == marginal between items */
-                       width += rec->max_size+1;
+                       width += rec->max_size;
 
                        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);
 }
 
-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;
-       GSList *tmp;
+       GSList *tmp, *right_items;
        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;
 
-               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;
 
-               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;
 }
 
-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;
        }
 
-       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 */
-       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;
 
-       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;
 
-               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,
-                                    int priority, int right_justify,
-                                    STATUSBAR_FUNC func)
+                                    SBAR_ITEM_CONFIG_REC *config)
 {
        SBAR_ITEM_REC *rec;
+        GSList *items;
 
        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->bar = bar;
        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;
 }
 
-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);
 
-       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("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)
 {
-       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("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"
 
-#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);
 
+/* 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 {
-       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;
+} 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;
 
+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;
-       STATUSBAR_FUNC func;
+       SBAR_ITEM_CONFIG_REC *config;
+        STATUSBAR_FUNC func;
 
        /* what item wants */
-        int priority;
        int min_size, max_size;
-       unsigned int right_justify:1;
 
        /* 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,
-                                    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 */
-void statusbar_redraw(STATUSBAR_REC *bar);
+void statusbar_redraw(STATUSBAR_REC *bar, int force);
 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);
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-formats.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 "textbuffer-reformat.h"
 
 /* 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);
 }
 
+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)
 {
@@ -74,13 +104,13 @@ static void scrollback_goto_line(int linenum)
        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)
 {
-        GList *tmp;
+        LINE_REC *line;
        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 */
-        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;
                }
        }
@@ -188,7 +216,7 @@ static void cmd_scrollback_home(const char *data)
 
        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 */
@@ -200,28 +228,28 @@ static void cmd_scrollback_end(const char *data)
        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)
 {
-#if 0
        GUI_WINDOW_REC *gui;
-       GList *tmp, *next;
+       LINE_REC *line, *next;
 
        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);
-       screen_refresh_thaw();
-#endif
+       term_refresh_thaw();
 }
 
 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);
+       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);
@@ -285,6 +314,7 @@ void textbuffer_commands_init(void)
 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);
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 "screen.h"
+#include "utf8.h"
 
 typedef struct {
        char *name;
@@ -102,21 +102,64 @@ static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *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)
 {
+        INDENT_FUNC indent_func;
        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);
 
-       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;
 
+        indent_func = view->default_indent_func;
         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) {
-                               char *tmp;
+                               unsigned char *tmp;
 
                                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;
-                               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) &&
-                   !view->longword_noindent) {
+                   view->longword_noindent) {
                         /* 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) {
@@ -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++;
-                       } else if (!view->longword_noindent) {
+                       } else if (view->longword_noindent) {
                                /* 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->indent_func = indent_func;
                        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;
                }
 
+               if (view->utf8)
+                       get_utf8_char(&ptr);
+
                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;
 }
 
+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)
 {
+        INDENT_FUNC indent_func;
        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;
 
-       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) {
+                       if (need_clrtoeol && xpos < term_width) {
+                               term_set_color(view->window, ATTR_RESET);
+                               term_clrtoeol(view->window);
+                       }
+
                        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) {
-                               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;
                        }
 
-                       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++;
                }
@@ -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 & 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;
-                       } 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;
                }
 
-               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++;
+               xpos++;
+       }
+
+       if (need_clrtoeol && xpos < term_width) {
+               term_set_color(view->window, ATTR_RESET);
+               term_clrtoeol(view->window);
        }
 
         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)
 {
-       GList *tmp;
+        LINE_REC *line;
         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;
-       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);
-               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;
@@ -347,7 +452,7 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
 
                 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;
@@ -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 */
-       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)
 {
-       GList *tmp;
+        LINE_REC *line;
 
        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,
-                                            int default_indent,
-                                            int longword_noindent)
+                                            int scroll, int utf8)
 {
        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->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);
@@ -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,
-                                       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;
-       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;
 }
 
-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;
 
-       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;
        }
 
-        /* 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)
 {
-       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) {
-                linecount = view_get_linecount(view, line->data);
+                linecount = view_get_linecount(view, line);
                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;
        }
 
-        view_draw(view, line, subline, maxline, lines);
+        view_draw(view, line, subline, maxline, lines, TRUE);
 }
 
 /* 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;
 
@@ -520,7 +694,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
        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)) {
@@ -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;
-               linecount = view_get_linecount(view, (*lines)->data);
+               linecount = view_get_linecount(view, *lines);
 
                 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 {
-                       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)
-                                        view_draw_top(view, -realcount);
+                                        view_draw_top(view, -realcount, TRUE);
                                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->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.. */
-       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 &&
@@ -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 */
-               linecount = view_get_linecount(view, view->startline->data);
+               linecount = view_get_linecount(view, view->startline);
                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->more_text = FALSE;
        }
 
-        textbuffer_view_redraw(view);
+       view->dirty = TRUE;
 }
 
 /* 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 =
-               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->more_text = FALSE;
 
         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);
+        if (view->bottom) view->more_text = FALSE;
 
         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)
 {
-       GList *tmp;
-
         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 {
-               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);
+        if (view->bottom) view->more_text = FALSE;
 
        textbuffer_view_redraw(view);
 }
@@ -724,42 +891,20 @@ LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
         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;
 
+        if (!view->bottom)
+               view->more_text = TRUE;
+
        if (view->bottom_startline == NULL) {
                view->startline = view->bottom_startline =
-                       view->buffer->lines;
+                       view->buffer->first_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;
@@ -780,11 +925,13 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        }
 
        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;
+               } else {
+                       view->bottom = view_is_bottom(view);
                }
 
                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;
                        }
-                       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)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* 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) {
-               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) {
@@ -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,
-                                GList *lines, int subline,
+                                LINE_REC *line, int 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;
                }
-                lines = lines->next;
+                line = line->next;
        }
 
        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)
 {
-       int realcount, scroll;
+       int realcount;
 
        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;
 
-               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);
        }
 
-       if (line == view->buffer->lines->data) {
+       if (view->buffer->first_line == 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;
                }
 
-               if (view->startline->data == line) {
+               if (view->startline == line) {
                         /* 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;
                }
-       } 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);
+        if (view->bottom) view->more_text = FALSE;
         if (view->window != NULL)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* 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);
 }
 
+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)
 {
-       GSList *tmp;
-
        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);
-
-       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 */
@@ -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) {
-                line = g_list_last(view->bottom_startline)->data;
+                line = textbuffer_line_last(view->buffer);
                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. */
-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;
-               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) {
-                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);
        }
+
+        g_slist_free(caches);
        return 1;
 }
 
index 21ed28cfb4b80c39f460b442f1f18d1554565ead..b529ebedd099436225c64e8c10b768b1da5c5872 100644 (file)
@@ -2,11 +2,18 @@
 #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 {
-       unsigned char *start;
+       const unsigned char *start;
        int indent;
+        INDENT_FUNC indent_func;
        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;
 
-typedef struct {
+struct _TEXT_BUFFER_VIEW_REC {
        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 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 */
 
-       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 */
-       GList *bottom_startline;
+       LINE_REC *bottom_startline;
        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;
+        /* 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;
-} TEXT_BUFFER_VIEW_REC;
+};
 
 /* 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,
-                                       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);
@@ -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) \
-        ((view)->buffer->lines)
+        ((view)->buffer->first_line)
 
 /* 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. */
-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);
 
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;
-       char *buf, *ptr, **pptr;
+       unsigned char *buf, *ptr, **pptr;
 
        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;
-               memcpy(buf, pptr, sizeof(char *));
+               memcpy(buf, pptr, sizeof(unsigned char *));
        } 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,
-                             const char *data, int len)
+                             const unsigned char *data, int len)
 {
         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;
-               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;
@@ -187,14 +188,22 @@ static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
 {
        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 {
-               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;
@@ -224,11 +233,34 @@ void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list)
        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;
        }
 }
 
+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)
@@ -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);
 
+       if (len == 0)
+                return 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);
 
-       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) {
-               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);
 }
@@ -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;
+        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);
-        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->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)
 {
-        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);
 
+        last_fg = last_bg = -1;
        for (ptr = line->text;;) {
                if (*ptr != 0) {
-                       g_string_append_c(str, *ptr);
+                       g_string_append_c(str, (char) *ptr);
                         ptr++;
                        continue;
                }
 
                ptr++;
-                cmd = (unsigned char) *ptr;
+                cmd = *ptr;
                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.. */
-                       memcpy(&tmp, ptr, sizeof(char *));
+                       memcpy(&tmp, ptr, sizeof(unsigned char *));
                        ptr = tmp;
                         continue;
                }
@@ -334,40 +421,39 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 
                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;
+               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_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_FUNC:
+                        ptr += sizeof(void *);
+                       break;
                }
        }
 }
 
 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
-       GList *line, *tmp;
+        LINE_REC *line, *pre_line;
        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);
@@ -383,216 +469,65 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
 #endif
        }
 
-       matches = NULL;
+       matches = NULL; match_after = 0;
         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 */
-                        textbuffer_line_ref(rec);
-                       matches = g_list_append(matches, rec);
+                        textbuffer_line_ref(line);
+                       matches = g_list_append(matches, line);
                        continue;
                }
 
-                textbuffer_line2text(rec, FALSE, str);
+                textbuffer_line2text(line, FALSE, str);
 
-                if (
+               line_matched =
 #ifdef HAVE_REGEX_H
-                   regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+                       regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
 #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
+        g_string_free(str, TRUE);
+       return matches;
+}
 
 void textbuffer_init(void)
 {
index 21f70e260de022c4ad49631e6b921ee0035a2598..adea3604c3a84613c71181235758e6fe1c40c9b2 100644 (file)
@@ -1,20 +1,21 @@
 #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_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.. */
-       LINE_CMD_COLOR8,        /* change to dark grey, normally 8 = bold black */
        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_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
@@ -27,13 +28,23 @@ typedef struct {
        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. */
+       struct _LINE_REC *prev, *next;
+
        unsigned char *text;
         unsigned char refcount;
         LINE_INFO_REC info;
@@ -47,7 +58,7 @@ typedef struct {
 
 typedef struct {
        GSList *text_chunks;
-       GList *lines;
+        LINE_REC *first_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);
 
+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. */
@@ -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,
+                           int before, int after,
                            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;
 
-        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' */
@@ -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;
 
-       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
@@ -308,3 +308,23 @@ CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index)
 
        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 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);
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') {
-               h = (h << 4) + toupper(*s);
+               h = (h << 4) + i_toupper(*s);
                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;
-#undef g_strdup_printf /* This is free'd by GLib itself */
                                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;
                }
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') {
-               if (!isalnum((int) *text) && *text != '_')
+               if (!i_isalnum(*text) && *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) 
-           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);
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 (!*line || !isspace(*line)) return;
-    while (*line && isspace(*line)) line++;
+    if (!*line || !i_isspace(*line)) return;
+    while (*line && i_isspace(*line)) line++;
     entryType = line;
 
-    while (!*line || !isspace(*line)) line++;
+    while (!*line || !i_isspace(*line)) line++;
     *line++ = '\0';
-    while (*line && isspace(*line)) line++;
+    while (*line && i_isspace(*line)) line++;
     if (!*line) return;
     opt = line;
 
-    while (!*line || !isspace(*line)) line++;
+    while (!*line || !i_isspace(*line)) line++;
     *line++ = '\0';
-    while (*line && isspace(*line)) line++;
+    while (*line && i_isspace(*line)) line++;
     if (!*line) return;
 
     if (opt[0] == '-' && opt[1] == '-')
@@ -92,7 +92,7 @@ int poptReadConfigFile(poptContext con, char * fn) {
          case '\n':
            *dst = '\0';
            dst = buf;
-           while (*dst && isspace(*dst)) dst++;
+           while (*dst && i_isspace(*dst)) 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;
-       while (ch > help && !isspace(*ch)) ch--;
+       while (ch > help && !i_isspace(*ch)) ch--;
        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;
-       while (isspace(*help) && *help) help++;
+       while (i_isspace(*help) && *help) 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;
-       } else if (isspace(*src)) {
+       } else if (isspace((int) (unsigned char) *src)) {
            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;
 }
 
-/* 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);
 }
 
-/* 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;
@@ -369,6 +338,39 @@ void silc_core_init_finish(void)
   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)
index cb41470edfca631dffacfee201f73dd5c8f692f6..86e3082b1dbc3be880a78c4101e7c49770e08361 100644 (file)
@@ -164,7 +164,7 @@ static int isnickflag_func(char flag)
   return flag == '@' || flag == '+';
 }
 
-static int ischannel_func(const char *data)
+static int ischannel_func(SERVER_REC *server, const char *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;
 
+  server_connect_ref(SERVER_CONNECT(conn));
+
   if (!server_start_connect((SERVER_REC *) server)) {
-    server_connect_free(SERVER_CONNECT(conn));
+    server_connect_unref(SERVER_CONNECT(conn));
     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)
-  win32-support = true
+  win32-support=true
   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)
 #
-epoc-support = false
-AM_CONDITIONAL(SILC_EPOC, test x$epoc-support = xtrue)
+AM_CONDITIONAL(SILC_EPOC, test xfalse = xtrue)
 
 #
 # 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
-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."