+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
--- /dev/null
+Timo Sirainen, tss@iki.fi
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.
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
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.
# 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
#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
# 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
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;;
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
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
+++ /dev/null
-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"; };
-};
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
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)
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],
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],
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
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])
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"
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])
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)
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,"
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
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 **
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
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
dnl * check that perl's ldflags actually work
AC_CACHE_VAL(irssi_cv_lib_perl_works, [
echo "main(){perl_alloc(); return 0;}" > conftest.c
- $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> /dev/null > /dev/null
+ $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> perl.error.tmp > /dev/null
if test -s conftest; then
irssi_cv_lib_perl_works=yes
else
])
if test "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)
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)
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
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 **
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 ****************************************
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
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
AC_SUBST(COMMON_NOUI_LIBS)
AC_SUBST(COMMON_LIBS)
-dnl **
-dnl ** memory debugging
-dnl **
-
-if test "x$want_memdebug" = "xyes"; then
- AC_DEFINE(MEM_DEBUG)
-fi
-AM_CONDITIONAL(BUILD_MEMDEBUG, test "x$want_memdebug" = "xyes")
-
dnl **
dnl ** tr-Chinese Big5 support
dnl **
dnl **
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)
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
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"
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!"
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"
+
--- /dev/null
+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
+])
+
## 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";
};
#
--- /dev/null
+Makefile
+Makefile.in
+startup-HOWTO.txt
--- /dev/null
+
+ 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>
+
--- /dev/null
+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)
--- /dev/null
+
+ 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
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):
--- /dev/null
+
+@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
+
--- /dev/null
+
+@SYNTAX:perlflush@
+
+Stops and removes all Perl scripts which have been run.
+Also undefines all the commands defined by Perl scripts.
+
+See also: RUN
+
--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
--- /dev/null
+ 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"
--- /dev/null
+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.
--- /dev/null
+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"
--- /dev/null
+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 */
+
<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>
<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 <number> - Jump to any window with specified number
Ctrl-P, Ctrl-N - Jump to previous / next window
</pre>
rxvt*modifier: alt
</pre>
+<p>You could do this by changing the X key mappings:</p>
+
+<pre>
+ xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+</pre>
+
<p>And how exactly do you set these X resources? For Debian, there's
/etc/X11/Xresources/xterm file where you can put them and it's read
automatically when X starts. ~/.Xresources and ~/.Xdefaults files might also
-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&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>
<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>
"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 <Proxy host address>
+ /SET proxy_port <Proxy port>
+</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 <password>
+ /SET irssiproxy_ports <ircnet>=<port> ... (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 <password>
+</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
<dt>/SET show_nickmode ON</dt>
<dd>Show the nick's mode before nick in channels, ie. ops have
- <@nick>, voices <+nick> and others < nick></dd>
+ <@nick>, voices <+nick> and others < nick></dd>
+
+<dt>/SET show_nickmode_empty ON</dt>
+ <dd>If the nick doesn't have a mode, use one space. ie. ON:
+ < nick>, OFF: <nick></dd>
<dt>/SET show_quit_once OFF</dt>
<dd>Show quit message only once in some of the channel windows the
<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>
--- /dev/null
+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.
COMMON_LIBS="@COMMON_LIBS@"
CHAT_MODULES="@CHAT_MODULES@"
-silc_MODULES="@silc_MODULES@"
+irc_MODULES="@irc_MODULES@"
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
--- /dev/null
+Makefile
+Makefile.in
--- /dev/null
+scriptdir = $(datadir)/irssi/scripts
+
+script_DATA = \
+ autoop.pl \
+ autorejoin.pl \
+ clones.pl \
+ hello.pl \
+ privmsg.pl \
+ realname.pl \
+ quitmsg.pl
+
+EXTRA_DIST = $(script_DATA)
--- /dev/null
+# /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');
--- /dev/null
+# 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');
--- /dev/null
+# /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');
--- /dev/null
+# "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');
--- /dev/null
+# /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");
--- /dev/null
+# 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");
--- /dev/null
+# 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');
--- /dev/null
+# /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');
--- /dev/null
+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"; };
+};
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)
#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)
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;
typedef struct _SERVER_SETUP_REC SERVER_SETUP_REC;
typedef struct _CHANNEL_SETUP_REC CHANNEL_SETUP_REC;
+typedef struct _WINDOW_REC WINDOW_REC;
+
#endif
--- /dev/null
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
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 \
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 \
rawlog.c \
servers.c \
servers-reconnect.c \
- servers-redirect.c \
servers-setup.c \
+ session.c \
settings.c \
signals.c \
special-vars.c \
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 \
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
g_array_free(iopt_tables, TRUE);
iopt_tables = NULL;
+
+ poptFreeContext(con);
}
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
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) {
/* 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);
}
}
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;
g_free_not_null(channel->key);
g_free(channel->mode);
g_free(channel->name);
+
+ channel->type = 0;
g_free(channel);
}
(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) {
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);
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;
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);
#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;
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;
}
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;
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,
if (server != NULL) {
oldconn = server->connrec;
+ server_connect_ref(oldconn);
reconnect_save_status(conn, server);
} else {
/* maybe we can reconnect some server from
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);
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);
}
}
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);
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);
/* 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);
}
}
settings_add_str("misc", "quit_message", "leaving");
command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
+ command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect);
command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit);
command_bind("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);
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);
}
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 */
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)
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);
int id;
unsigned int not_initialized:1;
+ unsigned int case_insensitive:1;
char *name;
char *fullname;
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);
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);
#include "window-item-def.h"
#include "servers.h"
-#include "servers-redirect.h"
#include "channels.h"
#include "lib-config/iconfig.h"
for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
COMMAND_MODULE_REC *rec = tmp->data;
- if (g_slist_find(rec->signals, func) != NULL)
+ if (g_slist_find(rec->signals, (void *) func) != NULL)
return rec;
}
return 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;
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;
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);
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);
}
g_return_if_fail(data != NULL);
+ while (*data == ' ') data++;
+
if (*data == '\0') {
/* no subcommand given - list the subcommands */
signal_emit("list subcommands", 2, cmd);
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) {
}
(*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 = "-";
/* 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;
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 */
}
option = NULL;
- while (isspace(**data)) (*data)++;
+ while (i_isspace(**data)) (*data)++;
}
return 0;
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;
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;
}
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);
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 *);
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;
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) {
}
}
+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)
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;
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);
typedef struct {
char *name;
char *options;
+ int protocol; /* chat protocol required for this command */
GSList *signals;
} COMMAND_MODULE_REC;
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 */
};
/* 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);
#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
*/
#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();
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();
servers_init();
write_buffer_init();
log_init();
+ log_away_init();
rawlog_init();
channels_init();
nicklist_init();
chat_commands_init();
- settings_check();
+
+ settings_add_str("misc", "ignore_signals", "");
+ settings_add_bool("misc", "override_coredump_limit", TRUE);
+
+#ifdef HAVE_SYS_RESOURCE_H
+ getrlimit(RLIMIT_CORE, &orig_core_rlimit);
+#endif
+ read_settings();
+ signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_add("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished);
+
+ settings_check();
+
+ module_register("core", "core");
}
void core_deinit(void)
{
+ signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_remove("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished);
+
chat_commands_deinit();
nicklist_deinit();
channels_deinit();
rawlog_deinit();
+ log_away_deinit();
log_deinit();
write_buffer_deinit();
servers_deinit();
chatnets_deinit();
chat_protocols_deinit();
+ session_deinit();
nickmatch_cache_deinit();
commands_deinit();
settings_deinit();
pidwait_deinit();
#endif
modules_deinit();
+
+ g_free(irssi_dir);
+ g_free(irssi_config_file);
}
#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);
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, ...)
rec = g_hash_table_lookup(expandos, key);
else {
/* single character expando */
- rec = char_expandos[(int) *key];
+ rec = CHAR_EXPANDO(*key);
}
if (rec != NULL)
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;
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 */
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,
}
}
-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)
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 */
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)
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);
"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);
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);
{
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);
/* 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,
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);
g_return_val_if_fail(server != NULL, 0);
if (nick == NULL) nick = "";
- chanrec = (channel != NULL && server != NULL &&
- server->ischannel(channel)) ?
- channel_find(server, channel) : NULL;
+ chanrec = server == NULL || channel == NULL ? NULL :
+ channel_find(server, channel);
if (chanrec != NULL && nick != NULL &&
(nickrec = nicklist_find(chanrec, nick)) != NULL) {
/* nick found - check only ignores in nickmatch cache */
if (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)
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)
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);
"CLIENTNOTICES",
"CLIENTCRAP",
"CLIENTERRORS",
- "HILIGHT",
+ "HILIGHTS",
"NOHILIGHT",
NULL
--- /dev/null
+/*
+ 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);
+}
#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;
static void log_rotate_check(LOG_REC *log)
{
- char *new_fname;
+ char *new_fname, *dir;
g_return_if_fail(log != NULL);
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);
void log_write_rec(LOG_REC *log, const char *str, int level)
{
+ char *colorstr;
struct tm *tm;
time_t now;
int hour, day;
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
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,
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);
if (logs == NULL)
return;
- servertag = server == NULL ? NULL : server->tag;
fallbacks = NULL; found = FALSE;
for (tmp = logs; tmp != NULL; tmp = tmp->next) {
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);
}
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)
char *item;
int type;
- for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ tmp = config_node_first(node->value);
+ for (; tmp != NULL; tmp = config_node_next(tmp)) {
node = tmp->data;
if (node->type != NODE_TYPE_BLOCK)
node = 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)
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);
"--- 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)
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);
}
#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;
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 */
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);
+++ /dev/null
-/*
- 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;
-}
+++ /dev/null
-#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
return FALSE;
for (;;) {
- while (isspace((gint) *list)) list++;
+ while (i_isspace(*list)) list++;
if (*list == '\0') break;
ptr = strchr(list, ' ');
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++;
#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)
{
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])))
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;
}
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;
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++;
return FALSE;
while (*str != '\0' && *str != end_char) {
- if (!isdigit(*str)) return FALSE;
+ if (!i_isdigit(*str)) return FALSE;
str++;
}
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);
+ }
+}
/* 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
--- /dev/null
+/*
+ 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
--- /dev/null
+#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
/*
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
#include "modules.h"
#include "signals.h"
-#include "commands.h"
-#include "settings.h"
-
GSList *modules;
static GHashTable *uniqids, *uniqstrids;
}
}
-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)
#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))
disconnects = g_slist_remove(disconnects, rec);
g_source_remove(rec->tag);
+ net_disconnect(rec->handle);
g_free(rec);
}
g_free(rec);
}
-/* Transmit all data from buffer - return TRUE if successful */
+/* Transmit all data from buffer - return TRUE if the whole buffer was sent */
static int buffer_send(NET_SENDBUF_REC *rec)
{
int ret;
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)
{
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);
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 */
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;
}
/* 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;
{
#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
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) {
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.
int is_ipv4_address(const char *host)
{
while (*host != '\0') {
- if (*host != '.' && !isdigit(*host))
+ if (*host != '.' && !i_isdigit(*host))
return 0;
host++;
}
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 */
#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)
{
/* 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;
{
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);
/* 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);
}
/* 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
/* 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++;
}
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;
/* 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
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) {
g_free_not_null(query->server_tag);
g_free_not_null(query->address);
g_free(query->name);
+
+ query->type = 0;
g_free(query);
}
#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;
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");
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);
}
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;
/* 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;
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 */
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 */
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;
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 */
/* 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);
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 */
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);
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)
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);
}
}
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 *
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);
}
#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 && \
}
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 */
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);
if (through) {
/* shouldn't happen unless there's no servers in
this chatnet in setup.. */
- server_connect_free(conn);
+ server_connect_unref(conn);
break;
}
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)
rec = reconnects->data;
list = g_slist_append(list, rec->conn);
- server_reconnect_destroy(rec, FALSE);
+ server_connect_ref(rec->conn);
+ server_reconnect_destroy(rec);
}
conn = list->data;
CHAT_PROTOCOL(conn)->server_connect(conn);
+ server_connect_unref(conn);
list = g_slist_remove(list, conn);
}
}
/* 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;
}
}
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();
}
next = tmp->next;
if (rec->conn->chat_type == proto->id)
- server_reconnect_destroy(rec, TRUE);
+ server_reconnect_destroy(rec);
}
}
#define FAILED_RECONNECT_WAIT (60*30)
typedef struct {
- int tag;
+ int tag;
time_t next_connect;
SERVER_CONNECT_REC *conn;
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);
+++ /dev/null
-/*
- 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);
-}
+++ /dev/null
-#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
conn->proxy = g_strdup(settings_get_str("proxy_address"));
conn->proxy_port = settings_get_int("proxy_port");
conn->proxy_string = g_strdup(settings_get_str("proxy_string"));
+ conn->proxy_string_after = g_strdup(settings_get_str("proxy_string_after"));
conn->proxy_password = g_strdup(settings_get_str("proxy_password"));
}
conn->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,
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)
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);;
}
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;
chat_protocol_get_default();
conn = proto->create_server_connect();
+ server_connect_ref(conn);
+
conn->chat_type = proto->id;
if (chatnet != NULL && *chatnet != '\0')
conn->chatnet = g_strdup(chatnet);
conn->nick = g_strdup(nick);
}
- signal_emit("server setup fill connect", 1, conn);
return conn;
}
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;
for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
SERVER_SETUP_REC *rec = tmp->data;
- if (g_strcasecmp(rec->address, address) == 0) {
+ if (g_strcasecmp(rec->address, address) == 0 &&
+ (chatnet == NULL || rec->chatnet == NULL ||
+ g_strcasecmp(rec->chatnet, chatnet) == 0)) {
server = rec;
if (rec->port == port)
break;
{
SERVER_SETUP_REC *rec;
- rec = server_setup_find(address, port);
+ rec = server_setup_find(address, port, NULL);
return rec == NULL || rec->port != port ? NULL : rec;
}
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) {
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);
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);
}
/* 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);
}
}
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;
/* 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);
#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"
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 */
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++)
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);
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)) {
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,
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') {
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);
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)
servers = g_slist_remove(servers, server);
+ server->disconnected = TRUE;
signal_emit("server disconnected", 1, server);
/* close all channels */
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)
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);
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);
signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
servers_reconnect_init();
- servers_redirect_init();
servers_setup_init();
}
signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
servers_setup_deinit();
- servers_redirect_deinit();
servers_reconnect_deinit();
module_uniq_destroy("SERVER");
#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 {
#include "server-rec.h"
};
+#define SEND_TARGET_CHANNEL 0
+#define SEND_TARGET_NICK 1
+
extern GSList *servers, *lookup_servers;
void servers_init(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);
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
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) */
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 */
}
}
-/* 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;
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)
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;
"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);
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 */
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);
}
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;
}
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);
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));
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;
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 "
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;
#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);
/* 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);
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
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)
{
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec == NULL) {
rec = g_mem_chunk_alloc0(signals_chunk);
- rec->id = signal_id;
+ rec->id = signal_id;
g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), rec);
}
g_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)
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;
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;
}
}
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 */
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++) {
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;
/* 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 */
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)
{
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)
#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;
arg = 0;
max = -1;
- argcount = strarray_length(arglist);
+ argcount = arglist == NULL ? 0 : strarray_length(arglist);
if (**cmd == '*') {
/* get all arguments */
/* get last argument */
arg = max = argcount-1;
} else {
- if (isdigit(**cmd)) {
+ if (i_isdigit(**cmd)) {
/* first argument */
arg = max = (**cmd)-'0';
(*cmd)++;
if (**cmd == '-') {
/* get more than one argument */
(*cmd)++;
- if (!isdigit(**cmd))
+ if (!i_isdigit(**cmd))
max = -1; /* get all the rest */
else {
max = (**cmd)-'0';
}
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++;
{
EXPANDO_FUNC func;
- if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
+ if (isarg(**cmd)) {
/* argument */
*free_ret = TRUE;
if (arg_used != NULL) *arg_used = TRUE;
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);
}
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);
}
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)
/* '!' = 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 == '-')
*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++;
}
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++;
}
{
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);
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 == '$') {
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;
/* 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');
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);
}
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);
}
#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);
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
int data_level;
char *hilight_color;
+void (*destroy)(WI_ITEM_REC *item);
+
#undef STRUCT_SERVER_REC
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") *
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;
}
}
--- /dev/null
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
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 \
fe-windows.c
noinst_HEADERS = \
+ autorun.h \
command-history.h \
chat-completion.h \
completion.h \
/*
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) {
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);
-}
--- /dev/null
+#ifndef __AUTORUN_H
+#define __AUTORUN_H
+
+void autorun_startup(void);
+
+#endif
#include "settings.h"
#include "chatnets.h"
+#include "servers.h"
#include "servers-setup.h"
#include "channels.h"
#include "channels-setup.h"
}
}
+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)
{
/* remove non alnum chars from nick */
in = rec->nick; out = str;
while (*in != '\0') {
- if (isalnum(*in))
+ if (i_isalnum(*in))
*out++ = *in;
in++;
}
}
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;
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;
} else if (channel != NULL) {
/* nick completion .. we could also be completing a nick
after /MSG from nicks in channel */
- complete_window_nicks(list, window, word, linestart);
+ complete_window_nicks(list, window, word, linestart);
}
if (*list != NULL) signal_stop();
if (*list != NULL) signal_stop();
}
+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;
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++) {
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;
}
}
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();
}
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)
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);
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);
#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;
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)) {
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);
}
#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
((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);
}
/* 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);
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;
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;
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;
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));
return list;
}
-GList *filename_complete(const char *path)
+GList *filename_complete(const char *path, const char *default_path)
{
GList *list;
DIR *dirp;
/* 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);
}
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;
}
/* 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)
} else {
checkcmd = g_strndup(line, (int) (ptr-line));
- while (isspace(*ptr)) ptr++;
+ while (i_isspace(*ptr)) ptr++;
cmdargs = ptr;
}
*args = (char *) cmdargs;
} while (ptr != NULL);
+ if (cmd != NULL)
+ g_strdown(cmd);
return cmd;
}
}
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;
/* 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);
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)
{
if (*line != '\0') return;
- *list = filename_complete(word);
+ *list = filename_complete(word, NULL);
if (*list != NULL) {
*want_space = FALSE;
signal_stop();
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);
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);
/* 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);
#include "chat-protocols.h"
#include "chatnets.h"
+#include "servers.h"
#include "channels.h"
#include "channels-setup.h"
#include "nicklist.h"
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;
{
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>]
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);
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));
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"),
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);
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);
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)
g_slist_free(nicklist);
g_string_free(str, TRUE);
g_free_not_null(columns);
+ g_free_not_null(prefix_format);
g_free(linebuf);
}
{
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;
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) {
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;
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;
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);
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");
}
{
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);
#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);
#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"
static char *cmdline_nick;
static char *cmdline_hostname;
-void autorun_init(void);
-void autorun_deinit(void);
-
void fe_core_log_init(void);
void fe_core_log_deinit(void);
void fe_core_commands_init(void);
void fe_core_commands_deinit(void);
+static void print_version(void)
+{
+ printf(PACKAGE" " IRSSI_VERSION" (%d %04d)\n",
+ IRSSI_VERSION_DATE, IRSSI_VERSION_TIME);
+ exit(0);
+}
+
static void sig_connected(SERVER_REC *server)
{
MODULE_DATA_SET(server, g_new0(MODULE_SERVER_REC, 1));
static void sig_disconnected(SERVER_REC *server)
{
g_free(MODULE_DATA(server));
+ MODULE_DATA_UNSET(server);
}
static void sig_channel_created(CHANNEL_REC *channel)
static void sig_channel_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 }
};
args_register(options);
settings_add_bool("lookandfeel", "timestamps", TRUE);
- settings_add_bool("lookandfeel", "msgs_timestamps", FALSE);
+ settings_add_str("lookandfeel", "timestamp_level", "ALL");
settings_add_int("lookandfeel", "timestamp_timeout", 0);
settings_add_bool("lookandfeel", "bell_beeps", FALSE);
settings_add_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);
themes_init();
theme_register(fecommon_core_formats);
- autorun_init();
command_history_init();
completion_init();
keyboard_init();
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();
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();
G_LOG_LEVEL_WARNING),
(GLogFunc) glog_func, NULL);
+ if (setup_changed)
+ signal_emit("setup changed", 0);
+
+ autorun_startup();
autoconnect_servers();
}
#include "line-split.h"
#include "settings.h"
#include "irssi-version.h"
+#include "servers.h"
#include "fe-windows.h"
#include "printtext.h"
TXT_NOT_JOINED,
TXT_CHAN_NOT_FOUND,
TXT_CHAN_NOT_SYNCED,
+ TXT_ILLEGAL_PROTO,
TXT_NOT_GOOD_IDEA
};
/* 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);
}
}
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();
{
const char *cmdchar;
+ if (*data == '\0') {
+ /* empty line, forget it. */
+ signal_stop();
+ return;
+ }
+
/* save current command line */
current_cmdline = 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);
}
}
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);
}
}
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);
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);
/*
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
*/
#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;
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;
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;
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
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);
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);
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);
}
}
}
}
-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;
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);
}
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);
}
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 */
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);
/* 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);
}
/* Window name */
static char *expando_winname(SERVER_REC *server, void *item, int *free_ret)
{
+ if (active_win == NULL)
+ return "";
+
return active_win->name;
}
"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);
}
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,
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++) {
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;
}
}
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;
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
{
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) {
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)
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);
#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"
/* close autologs after 5 minutes of inactivity */
#define AUTOLOG_INACTIVITY_CLOSE (60*5)
-#define LOG_DIR_CREATE_MODE 0770
-
static int autolog_level;
static int autoremove_tag;
static const char *autolog_path;
static int 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;
}
/* 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;
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");
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) {
active_win->name != NULL ? active_win->name : "Window",
active_win->name != NULL ? "" : window);
log = log_create_rec(fname, MSGLEVEL_ALL);
+ log->colorizer = log_colorizer_strip;
log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
log_update(log);
g_free(fname);
}
log = log_create_rec(data, MSGLEVEL_ALL);
+ log->colorizer = log_colorizer_strip;
log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
log_update(log);
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);
}
}
}
}
-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);
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];
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;
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),
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);
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;
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)
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;
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)
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);
#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;
/* 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;
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,
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);
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);
}
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) {
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,
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);
#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)
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)
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
#include "settings.h"
#include "chat-protocols.h"
+#include "servers.h"
#include "queries.h"
#include "fe-windows.h"
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)
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)
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);
}
}
}
-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;
{
SERVER_REC *server;
QUERY_REC *query;
+ TEXT_DEST_REC dest;
g_return_if_fail(data != NULL);
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();
}
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) {
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);
}
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);
signal_add("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
signal_add("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
signal_add("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
- signal_add_last("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy);
signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
signal_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);
if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
+ if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE;
+ if (g_hash_table_lookup(optlist, "noproxy")) rec->no_proxy = TRUE;
if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
value = g_hash_table_lookup(optlist, "host");
if (*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));
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;
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);
}
}
{
g_return_if_fail(server != NULL);
- printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_CONNECTION_LOST, server->connrec->address);
}
{
g_return_if_fail(server != NULL);
- printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_SERVER_QUIT, server->connrec->address, msg);
}
{
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)
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);
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);
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)
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;
}
}
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);
}
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);
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)
/* 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);
}
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);
}
/* 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();
}
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);
}
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);
return max;
}
-static int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
+int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
{
return w1->refnum < w2->refnum ? -1 : 1;
}
return 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)
{
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);
}
}
+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;
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)
#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,
unsigned int sticky:1;
} WINDOW_BIND_REC;
-typedef struct {
+struct _WINDOW_REC {
int refnum;
char *name;
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 */
void *theme; /* THEME_REC */
void *gui_data;
-} WINDOW_REC;
+};
extern GSList *windows;
extern WINDOW_REC *active_win;
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 */
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);
#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"
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)
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);
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');
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);
}
/* 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'));
}
/* 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'));
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) */
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;
}
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;
}
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;
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);
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);
/* 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
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);
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);
{
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;
}
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)
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 != '[')
for (;; str++) {
if (*str == '\0') return start;
- if (isdigit((int) *str)) {
+ if (i_isdigit(*str)) {
num = num*10 + (*str-'0');
continue;
}
/* 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)
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 {
if (**str != ',') {
fg = **str-'0';
(*str)++;
- if (isdigit((int) **str)) {
+ if (i_isdigit(**str)) {
fg = fg*10 + (**str-'0');
(*str)++;
}
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)++;
}
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;
/* 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')
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;
}
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)
#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
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) */
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,
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);
#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);
#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;
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)
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 {
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);
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);
color = rec == NULL || !rec->nick ? NULL :
hilight_get_color(rec);
- next_nick_hilight = rec;
return color;
}
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)
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
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);
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);
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
signal_emit("keyboard destroyed", 1, keyboard);
g_free_not_null(keyboard->key_state);
- g_free_not_null(keyboard->key_prev_state);
g_free(keyboard);
}
/* 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)
/* 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)
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, ©)) {
+ /* 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)
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)
search++; combo++;
}
- return *combo == '\0' || *combo == '-' ? 0 : -1;
+ return 0;
}
/* Returns TRUE if key press was consumed. Control characters should be sent
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,
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();
(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));
{ 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 },
{ "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 },
{ "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 } },
{ "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 } },
/* ---- */
{ 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 } },
{ "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 } },
/* ---- */
{ 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 },
{ "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 },
/* ---- */
{ "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 } },
{ 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 }
};
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,
TXT_SETUPSERVER_ADDED,
TXT_SETUPSERVER_REMOVED,
TXT_SETUPSERVER_NOT_FOUND,
+ TXT_YOUR_NICK,
TXT_FILL_3,
TXT_QUIT,
TXT_QUIT_ONCE,
TXT_INVITE,
+ TXT_NOT_INVITED,
TXT_NEW_TOPIC,
TXT_TOPIC_UNSET,
TXT_YOUR_NICK_CHANGED,
TXT_NOT_IN_CHANNELS,
TXT_CURRENT_CHANNEL,
TXT_NAMES,
+ TXT_NAMES_PREFIX,
+ TXT_NAMES_NICK_OP,
+ TXT_NAMES_NICK_HALFOP,
+ TXT_NAMES_NICK_VOICE,
TXT_NAMES_NICK,
TXT_ENDOFNAMES,
TXT_CHANLIST_HEADER,
TXT_FILL_5,
- TXT_QUERY_STARTED,
+ TXT_QUERY_START,
+ TXT_QUERY_STOP,
TXT_NO_QUERY,
TXT_QUERY_SERVER_CHANGED,
TXT_QUERY_MOVE_NOTIFY,
TXT_MODULE_LINE,
TXT_MODULE_FOOTER,
TXT_MODULE_ALREADY_LOADED,
+ TXT_MODULE_NOT_LOADED,
TXT_MODULE_LOAD_ERROR,
TXT_MODULE_INVALID,
TXT_MODULE_LOADED,
TXT_NOT_JOINED,
TXT_CHAN_NOT_FOUND,
TXT_CHAN_NOT_SYNCED,
+ TXT_ILLEGAL_PROTO,
TXT_NOT_GOOD_IDEA,
TXT_FILL_11,
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,
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[];
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];
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],
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)
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,
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,
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);
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. */
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);
}
return ret;
}
-static char *printtext_expand_formats(const char *str)
+static char *printtext_expand_formats(const char *str, int *flags)
{
GString *out;
char *ret;
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);
}
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;
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, ...)
{
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);
}
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);
}
window != NULL ? window : active_win);
va_start(va, text);
- printtext_dest(&dest, text, va);
+ printtext_dest_args(&dest, text, va);
va_end(va);
}
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)) &&
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);
/* 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,
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);
}
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);
}
#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 */
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)
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
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
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;
}
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)
(*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)++;
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;
}
(*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,
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;
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;
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;
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);
}
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) {
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;
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;
}
}
- 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;
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)
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,
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);
}
/* 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);
}
signal_add("setup reread", (SIGNAL_FUNC) themes_read);
command_set_options("format", "delete reset");
+ command_set_options("save", "formats");
}
void themes_deinit(void)
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 */
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
*/
#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)
{
}
#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)
{
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) {
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);
#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] */
}
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;
"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;
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 &&
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> */
/* 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;
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)
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;
}
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);
command_set_options("window number", "sticky");
command_set_options("window server", "sticky unsticky");
+ command_set_options("window theme", "delete");
}
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);
{
g_return_if_fail(window != NULL);
g_return_if_fail(item != NULL);
+ g_return_if_fail(item->window == NULL);
item->window = window;
window->items = g_slist_append(window->items, item);
signal_emit("window item new", 2, window, item);
- if (!automatic || g_slist_length(window->items) == 1) {
+ if (g_slist_length(window->items) == 1 ||
+ (!automatic && settings_get_bool("autofocus_new_items"))) {
window->active = NULL;
window_item_set_active(window, item);
}
window = window_item_window(item);
- if (g_slist_find(window->items, item) == NULL)
+ if (window == NULL)
return;
item->window = NULL;
window = window_item_window(item);
window_item_remove(item);
-
- signal_emit("window item destroy", 2, window, item);
+ item->destroy(item);
}
void window_item_change_server(WI_ITEM_REC *item, void *server)
void window_item_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;
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:
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 */
{
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);
}
#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;
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);
}
}
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;
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));
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)
if (window->name != NULL)
iconfig_node_set_str(node, "name", window->name);
+
+ if (window->history_name != NULL)
+ iconfig_node_set_str(node, "history_name", window->history_name);
+
if (window->servertag != NULL)
iconfig_node_set_str(node, "servertag", window->servertag);
if (window->level != 0) {
if (window->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)
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);
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);
}
--- /dev/null
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+irssi
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 \
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
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)
*/
#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(entry, pos, 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);
}
#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
/* 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 */
#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;
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;
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);
}
}
-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)
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;
}
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;
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);
}
#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
#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"
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;
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)
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;
active_win->active);
}
- gui_entry_remove_perm_prompt();
- window_update_prompt();
+ gui_entry_set_prompt(active_entry, "");
}
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;
}
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);
}
{
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)
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]);
}
}
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);
}
}
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,
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)
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);
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);
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);
/* 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);
/* 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);
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);
void gui_readline_deinit(void)
{
g_free_not_null(cutbuffer);
- g_source_remove(readtag);
+ input_listen_deinit();
key_configure_freeze();
key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character);
key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word);
key_unbind("forward_word", (SIGNAL_FUNC) key_forward_word);
+ key_unbind("backward_to_space", (SIGNAL_FUNC) key_backward_to_space);
+ key_unbind("forward_to_space", (SIGNAL_FUNC) key_forward_to_space);
key_unbind("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line);
key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line);
key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character);
key_unbind("delete_next_word", (SIGNAL_FUNC) key_delete_next_word);
key_unbind("delete_previous_word", (SIGNAL_FUNC) key_delete_previous_word);
+ key_unbind("delete_to_next_space", (SIGNAL_FUNC) key_delete_to_next_space);
key_unbind("delete_to_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space);
key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line);
key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
key_unbind("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();
extern char *cutbuffer;
+void input_listen_init(int handle);
+void input_listen_deinit(void);
+
void readline(void);
time_t get_idle_time(void);
#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;
}
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);
}
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);
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)
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;
}
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();
}
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)
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();
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);
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);
}
#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))
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;
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
#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.
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 */
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);
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,
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")) {
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;
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 = "";
}
{
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)
--- /dev/null
+/*
+ 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);
+}
#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)
{
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;
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)
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);
}
}
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);
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);
}
}
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);
}
}
{
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;
{
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)
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);
mainwindow_resize(rec, xdiff, -space);
}
+
+ if (xdiff != 0) {
+ while (tmp != NULL) {
+ mainwindow_resize(tmp->data, xdiff, 0);
+ tmp = tmp->next;
+ }
+ }
+
g_slist_free(sorted);
}
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;
{
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)
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>] */
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);
}
/* 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);
}
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);
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>] */
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);
}
}
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);
}
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);
}
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);
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)
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);
}
#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);
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
{ 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 },
{ "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 }
};
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,
TXT_CANT_SHOW_STICKY_WINDOWS,
TXT_WINDOW_NOT_STICKY,
TXT_WINDOW_SET_STICKY,
- TXT_WINDOW_UNSET_STICKY
+ TXT_WINDOW_UNSET_STICKY,
+ TXT_WINDOW_INFO_STICKY,
+ TXT_WINDOW_SCROLL,
+ TXT_WINDOW_SCROLL_UNKNOWN
};
extern FORMAT_REC gui_text_formats[];
extern int quitting;
void irssi_redraw(void);
+void irssi_set_dirty(void);
#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>
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;
/* 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();
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
"%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();
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);
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");
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);
}
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
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
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;
}
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
#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;
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;
/* 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)
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;
}
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;
}
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)
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);
}
#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;
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;
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 */
}
}
-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;
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)
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();
}
#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);
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
*/
#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)
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)
{
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;
}
/* 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;
}
}
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 */
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)
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);
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);
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
#include "module.h"
#include "textbuffer-view.h"
-#include "screen.h"
+#include "utf8.h"
typedef struct {
char *name;
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;;) {
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) {
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;
sub->start = ptr;
sub->indent = xpos;
+ sub->indent_func = indent_func;
sub->color = color;
lines = g_slist_append(lines, sub);
continue;
}
+ if (view->utf8)
+ get_utf8_char(&ptr);
+
xpos++;
if (*ptr++ == ' ') {
last_space = xpos-1;
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 {
}
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++;
}
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;
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) {
}
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;
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;
}
/* 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;
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);
/* 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);
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;
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)) {
/* 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;
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);
}
}
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 &&
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;
}
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. */
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);
}
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);
}
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;
}
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) {
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
(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) {
/* 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);
/* 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. */
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 */
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);
}
}
/* 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;
}
}
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);
}
}
(GHRFunc) line_cache_check_remove,
&now);
}
+
+ g_slist_free(caches);
return 1;
}
#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
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
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);
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);
/* 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);
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;
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);
}
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;
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;
{
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;
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)
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);
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);
}
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) {
if (cmd == LINE_CMD_CONTINUE) {
/* line continues in another address.. */
- memcpy(&tmp, ptr, sizeof(char *));
+ memcpy(&tmp, ptr, sizeof(unsigned char *));
ptr = tmp;
continue;
}
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);
#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)
{
#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
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;
typedef struct {
GSList *text_chunks;
- GList *lines;
+ LINE_REC *first_line;
int lines_count;
LINE_REC *cur_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. */
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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+
+#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;
+}
--- /dev/null
+#ifndef __UTF8_H
+#define __UTF8_H
+
+void get_utf8_char(const unsigned char **ptr);
+
+#endif
--- /dev/null
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
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' */
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
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);
+}
/* 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);
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;
} 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;
}
g_return_val_if_fail(text != NULL, FALSE);
while (*text != '\0') {
- if (!isalnum((int) *text) && *text != '_')
+ if (!i_isalnum(*text) && *text != '_')
return TRUE;
text++;
}
if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
&& (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
- con->finalArgv[con->finalArgvCount++] = g_strdup(con->os->nextArg);
+ con->finalArgv[con->finalArgvCount++] =
+ strcpy(malloc(strlen(con->os->nextArg)+1), con->os->nextArg);
}
if (dup) g_free(dup);
if (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] == '-')
case '\n':
*dst = '\0';
dst = buf;
- while (*dst && isspace(*dst)) dst++;
+ while (*dst && i_isspace(*dst)) dst++;
if (*dst && *dst != '#') {
configLine(con, dst);
}
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);
}
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) {
--- /dev/null
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+perl-signals-list.h
+irssi-core.pl.h
--- /dev/null
+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)
--- /dev/null
+Makefile
+Makefile.PL
+Irssi.bs
+*.c
+*.o
+pm_to_blib
+blib
--- /dev/null
+#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);
--- /dev/null
+#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);
+
--- /dev/null
+#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
--- /dev/null
+#
+# 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;
+
--- /dev/null
+#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);
--- /dev/null
+#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
--- /dev/null
+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');
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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);
+
--- /dev/null
+#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);
--- /dev/null
+#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;
--- /dev/null
+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\");
+
--- /dev/null
+#!/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";
--- /dev/null
+# 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);
+}
--- /dev/null
+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"
+;
--- /dev/null
+# 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=''
--- /dev/null
+# 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=''
--- /dev/null
+#include "module.h"
+
+#undef MODULE_NAME
+#define MODULE_NAME "fe-common/perl"
--- /dev/null
+/*
+ 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 }
+};
--- /dev/null
+#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[];
--- /dev/null
+#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
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+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 }
+};
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+#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
--- /dev/null
+Makefile
+Makefile.PL
+TextUI.bs
+*.c
+*.o
+pm_to_blib
+blib
--- /dev/null
+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');
--- /dev/null
+#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);
--- /dev/null
+#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);
+
--- /dev/null
+#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
--- /dev/null
+#
+# 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;
+
--- /dev/null
+#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()
--- /dev/null
+#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;
--- /dev/null
+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\");
+
--- /dev/null
+Makefile
+Makefile.PL
+UI.bs
+*.c
+*.o
+pm_to_blib
+blib
--- /dev/null
+#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);
--- /dev/null
+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');
--- /dev/null
+#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);
--- /dev/null
+#
+# 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;
--- /dev/null
+#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);
--- /dev/null
+#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);
--- /dev/null
+#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;
--- /dev/null
+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\");
+
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;
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)
return flag == '@' || flag == '+';
}
-static int ischannel_func(const char *data)
+static int ischannel_func(SERVER_REC *server, const char *data)
{
return *data == '#';
}
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;
}
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" ])
#
# 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
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."