From: Pekka Riikonen Date: Sun, 10 Feb 2002 13:58:05 +0000 (+0000) Subject: Merged 0.7.99 irssi. X-Git-Tag: silc.client.0.8.1~94 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=d067e054d2b76c94dd92a9aefbfc2185e52d0473 Merged 0.7.99 irssi. --- diff --git a/CHANGES b/CHANGES index aac22727..4ba77bb0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +Sun Feb 10 15:48:38 EET 2002 Pekka Riikonen + + * Merged new irssi from irssi.org's CVS, the version 0.7.99. + Sat Feb 9 14:54:33 EET 2002 Pekka Riikonen * Allow zero length channel messages inside the Channel Message diff --git a/apps/irssi/AUTHORS b/apps/irssi/AUTHORS new file mode 100644 index 00000000..b82b08a3 --- /dev/null +++ b/apps/irssi/AUTHORS @@ -0,0 +1 @@ +Timo Sirainen, tss@iki.fi diff --git a/apps/irssi/COPYING b/apps/irssi/COPYING index d60c31a9..60549be5 100644 --- a/apps/irssi/COPYING +++ b/apps/irssi/COPYING @@ -2,7 +2,7 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - Copyright (C) + Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -313,7 +313,7 @@ Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) year name of author + Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/apps/irssi/Makefile.am b/apps/irssi/Makefile.am index a8a1eff4..5d3888cf 100644 --- a/apps/irssi/Makefile.am +++ b/apps/irssi/Makefile.am @@ -1,29 +1,43 @@ # create default-config.h config.h: default-config.h default-theme.h -default-config.h: $(srcdir)/config - $(srcdir)/file2header.sh $(srcdir)/config default_config > default-config.h +default-config.h: $(srcdir)/silc.conf + $(srcdir)/file2header.sh $(srcdir)/silc.conf default_config > default-config.h default-theme.h: $(srcdir)/default.theme $(srcdir)/file2header.sh $(srcdir)/default.theme default_theme > default-theme.h -SUBDIRS = src docs +if BUILD_PLUGINS +PLUGINS=plugins +endif +if BUILD_SERVERTEST +SERVERTEST=servertest +endif + +SUBDIRS = src docs scripts include $(top_srcdir)/Makefile.defines.in -#confdir = $(sysconfdir)/irssi +#confdir = $(sysconfdir) confdir = $(silc_etcdir) -conf_DATA = config default.theme +conf_DATA = silc.conf + +themedir = $(datadir)/irssi/themes +theme_DATA = default.theme -noinst_HEADERS = irssi-version.h +noinst_HEADERS = irssi-version.h.in EXTRA_DIST = \ autogen.sh \ + curses.m4 \ README \ + README.cygwin \ file2header.sh \ irssi.spec \ irssi.spec.in \ $(conf_DATA) \ + $(theme_DATA) \ irssi-config.in \ + irssi-icon.png \ Makefile.defines.in \ Makefile.defines_int.in diff --git a/apps/irssi/acconfig.h b/apps/irssi/acconfig.h index 75350596..33f02000 100644 --- a/apps/irssi/acconfig.h +++ b/apps/irssi/acconfig.h @@ -4,7 +4,6 @@ #undef PLUGINSDIR /* misc.. */ -#undef MEM_DEBUG #undef HAVE_IPV6 #undef HAVE_POPT_H #undef HAVE_SOCKS_H @@ -23,12 +22,13 @@ #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 diff --git a/apps/irssi/autogen.sh b/apps/irssi/autogen.sh index d968658d..5ad69a27 100755 --- a/apps/irssi/autogen.sh +++ b/apps/irssi/autogen.sh @@ -15,10 +15,6 @@ fi # get versions version_date=`date +%Y%m%d` -echo "/* automatically created by autogen.sh */" > irssi-version.h.in -echo "#define IRSSI_VERSION \"@VERSION@\"" >> irssi-version.h.in -echo "#define IRSSI_VERSION_DATE \"$version_date\"" >> irssi-version.h.in - # create help files echo "Creating help files..." perl syntax.pl @@ -80,6 +76,13 @@ if test "$DIE" -eq 1; then exit 1 fi +#if test -z "$*"; then +# echo "**Warning**: I am going to run \`configure' with no arguments." +# echo "If you wish to pass any to it, please specify them on the" +# echo \`$0\'" command line." +# echo +#fi + case $CC in xlc ) am_opt=--include-deps;; @@ -88,9 +91,9 @@ esac rm -f aclocal.m4 if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then echo "Running libtoolize..." - libtoolize --copy --force + libtoolize --force --copy fi -aclocalinclude="$ACLOCAL_FLAGS" +aclocalinclude="$ACLOCAL_FLAGS -I ." echo "Running aclocal $aclocalinclude ..." aclocal $aclocalinclude if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then @@ -99,5 +102,15 @@ if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then fi echo "Running autoconf ..." autoconf -echo "Running automake $am_opt ..." -automake --add-missing --foreign $am_opt +echo "Running automake --gnu $am_opt ..." +automake --add-missing --gnu $am_opt + +#conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c + +#if test x$NOCONFIGURE = x; then +# echo Running $srcdir/configure $conf_flags "$@" ... +# $srcdir/configure $conf_flags "$@" \ +# && echo Now type \`make\' to compile $PKG_NAME || exit 1 +#else +# echo Skipping configure process. +#fi diff --git a/apps/irssi/config b/apps/irssi/config deleted file mode 100644 index 8bec49bf..00000000 --- a/apps/irssi/config +++ /dev/null @@ -1,65 +0,0 @@ -servers = ( - { address = "silc.silcnet.org"; chatnet = SILCNet; port = 706; } - { address = "silc.ytti.fi"; chatnet = SILCNet; port = 706; } - { address = "silc.peelo.com"; chatnet = SILCNet; port = 706; } - { address = "silc.silcnet.org"; chatnet = SILCNet; port = 707; } -); - -chatnets = { - SILCNet = { type = "SILC"; }; -}; - -channels = ( - { name = "#silc"; chatnet = silcnet; autojoin = No; } -); - -aliases = { - JOIN = "join -window"; - QUERY = "query -window"; - LEAVE = "part"; - BYE = "quit"; - EXIT = "quit"; - SIGNOFF = "quit"; - DESCRIBE = "action"; - DATE = "time"; - HOST = "userhost"; - LAST = "lastlog"; - SAY = "msg *"; - WHO = "users *"; - WI = "whois"; - WII = "whois $0 $0"; - WW = "whowas"; - W = "who"; - N = "names"; - M = "msg"; - T = "topic"; - C = "clear"; - CL = "clear"; - K = "kick"; - KB = "kickban"; - KN = "knockout"; - BANS = "ban"; - B = "ban"; - MUB = "unban *"; - UB = "unban"; - IG = "ignore"; - UNIG = "unignore"; - SB = "scrollback"; - WC = "window close"; - WN = "window new hide"; - GOTO = "sb goto"; - CHAT = "dcc chat"; - ADMIN = "info"; -}; - -settings = { - "fe-common/core" = { - autocreate_own_query = "no"; - use_status_window = "no"; - autoclose_windows = "no"; - use_msgs_window = "no"; - autocreate_windows = "no"; - autocreate_query_level = "none"; - }; - "fe-text" = { topicbar = "no"; mail_counter = "yes"; indent = "8"; }; -}; diff --git a/apps/irssi/configure.in b/apps/irssi/configure.in index dfc21b47..dfa63c84 100644 --- a/apps/irssi/configure.in +++ b/apps/irssi/configure.in @@ -1,7 +1,13 @@ AC_INIT(src) +# we don't want VERSION in our config.h +if test -n "`grep '^#undef VERSION' config.h.in`"; then + grep -v '^#undef VERSION' config.h.in > config.h.in.temp + mv -f config.h.in.temp config.h.in +fi + AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(Irssi-SILC, 0.7.98.3) +AM_INIT_AUTOMAKE(Irssi-SILC, 0.7.99) AM_MAINTAINER_MODE @@ -9,25 +15,17 @@ AC_ISC_POSIX AC_PROG_CC AC_PROG_CPP AC_STDC_HEADERS -AC_ARG_PROGRAM -AC_PROG_RANLIB +AM_PROG_LIBTOOL + +AC_PATH_PROG(sedpath, sed) +AC_PATH_PROG(perlpath, perl) -dnl * ahem.. :) we don't want static libraries for modules -if test "x$lt_target" = "x"; then - if test "$target" = "NONE"; then - lt_target="$host" - else - lt_target="$target" - fi -fi dnl * --disable-static isn't a good idea, complain if it's used if test "x$enable_static" = "xno"; then AC_ERROR([Don't give --disable-static option to configure]) fi -AC_CONFIG_AUX_DIR(.) - -AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h libintl.h) +AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h sys/resource.h) # check posix headers.. AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h) @@ -97,6 +95,18 @@ AC_ARG_WITH(proxy, fi, want_irssiproxy=no) +AC_ARG_WITH(terminfo, +[ --with-terminfo Use terminfo directly instead of curses], + if test x$withval = xyes; then + want_terminfo=yes + else + if test "x$withval" = xno; then + want_terminfo=no + else + want_terminfo=yes + fi + fi, + want_terminfo=yes) AC_ARG_WITH(modules, [ --with-modules Specify what modules to build in binary], @@ -104,39 +114,73 @@ AC_ARG_WITH(modules, build_modules="$withval" fi) -if test "x$prefix" = "xNONE"; then - PERL_LIB_DIR="" -else - PERL_LIB_DIR="$prefix" +if test "x$prefix" != "xNONE"; then + prefix=`eval echo $prefix` + PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=$prefix" + perl_library_dir="PERL_USE_LIB" + perl_set_use_lib=yes + + perl_prefix_note=yes fi -AC_ARG_ENABLE(perl-path, -[ --enable-perl-path=dir Specify where to install the Perl libraries for irssi], - if test x$enableval = xyes; then - want_perl=yes +AC_ARG_WITH(perl-staticlib, +[ --with-perl-staticlib Specify that we want to link perl libraries + statically in irssi, default is no], + if test x$withval = xyes; then + want_staticperllib=yes else - if test "x$enableval" = xno; then - want_perl=no + if test "x$withval" = xno; then + want_staticperllib=no else - want_perl=yes - PERL_LIB_DIR="$enableval" - perl_lib_dir_given=yes + want_staticperllib=yes fi fi, - want_perl=no) + want_staticperllib=no) -AC_ARG_ENABLE(perl, -[ --enable-perl[=yes|no|static] Build with Perl support - also specifies - if it should be built into main irssi binary - (static) or as module (default)], - if test x$enableval = xyes; then + +AC_ARG_WITH(perl-lib, +[ --with-perl-lib=[site|vendor|DIR] Specify where to install the + Perl libraries for irssi, default is site], + if test "x$withval" = xyes; then + want_perl=yes + elif test "x$withval" = xno; then + want_perl=no + elif test "x$withval" = xsite; then want_perl=yes - elif test x$enableval = xstatic; then + perl_prefix_note=no + PERL_MM_PARAMS="" + elif test "x$withval" = xvendor; then + want_perl=yes + perl_prefix_note=no + if test -z "`$perlpath -v|grep '5\.0'`"; then + PERL_MM_PARAMS="INSTALLDIRS=vendor" + else + PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=`perl -e 'use Config; print $Config{prefix}'`" + fi + perl_library_dir="(vendor default - `$perlpath -e 'use Config; print $Config{archlib}'`)" + else + want_perl=yes + perl_prefix_note=no + PERL_MM_PARAMS="INSTALLDIRS=perl LIB=$withval" + perl_library_dir="PERL_USE_LIB" + perl_set_use_lib=yes + fi, + want_perl=yes) + +AC_ARG_WITH(perl, +[ --with-perl[=yes|no|module] Build with Perl support - also specifies + if it should be built into main irssi binary + (static, default) or as module], + if test x$withval = xyes; then want_perl=static + elif test x$withval = xstatic; then + want_perl=static + elif test x$withval = xmodule; then + want_perl=module else want_perl=no fi, - want_perl=no) + want_perl=static) AC_ARG_WITH(tests, [ --with-tests Run all the tests], @@ -146,26 +190,6 @@ AC_ARG_WITH(tests, TEST_DIR=) AC_SUBST(TEST_DIR) -AC_ARG_ENABLE(curses-windows, -[ --enable-curses-windows Use curses windows], - if test x$enableval != xno; then - AC_DEFINE(USE_CURSES_WINDOWS) - fi, - AC_DEFINE(USE_CURSES_WINDOWS)) - -AC_ARG_ENABLE(memdebug, -[ --enable-memdebug Enable memory debugging], - if test x$enableval = xyes; then - want_memdebug=yes - else - if test "x$enableval" = xno; then - want_memdebug=no - else - want_memdebug=yes - fi - fi, - want_memdebug=no) - AC_ARG_ENABLE(ipv6, [ --enable-ipv6 Enable IPv6 support], if test x$enableval = xyes; then @@ -183,30 +207,35 @@ dnl ** dnl ** just some generic stuff... dnl ** +dnl * OS specific options +case "$host_os" in + hpux*) + CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED" + ;; + *) + ;; +esac + + AC_CHECK_FUNCS(mkfifo fcntl) -AC_CHECK_LIB(socket, socket, [ - PROG_LIBS="$PROG_LIBS -lsocket" +AC_CHECK_FUNC(socket, [], [ + AC_CHECK_LIB(socket, socket, [ + LIBS="$LIBS -lsocket" + ]) ]) -AC_CHECK_LIB(nsl, inet_addr, [ - PROG_LIBS="$PROG_LIBS -lnsl" -], -lsocket) +AC_CHECK_FUNC(inet_addr, [], [ + AC_CHECK_LIB(nsl, inet_addr, [ + LIBS="$LIBS -lnsl" + ], -lsocket) +]) dnl * gcc specific options if test "x$ac_cv_prog_gcc" = "xyes"; then CFLAGS="$CFLAGS -Wall" fi -dnl * OS specific options -case "$host_os" in - hpux*) - CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED" - ;; - *) - ;; -esac - dnl * socklen_t - AC_CHECK_TYPE() would be _really_ useful if it only would dnl * accept header files where to find the typedef.. AC_MSG_CHECKING([for socklen_t]) @@ -229,7 +258,7 @@ dnl ** if test "x$want_socks" = "xyes"; then AC_CHECK_LIB(socks, connect, [ - PROG_LIBS="$PROG_LIBS -lsocks" + LIBS="$LIBS -lsocks" AC_CHECK_HEADER(socks.h, [ AC_DEFINE(HAVE_SOCKS_H) CFLAGS="$CFLAGS -DSOCKS" @@ -245,13 +274,11 @@ dnl ** dnl ** fe-text checks dnl ** -AC_PATH_PROG(sedpath, sed) - AC_DEFUN(AC_CHECK_GLIBDIR,[ AC_MSG_CHECKING([whether GLib is unpacked to irssi dir]) GLIB_DIR=`for d in *; do test -f $d/glib.h && echo $d; done` - if test "x$GLIB_DIR" != "x"; then + if test -n "$GLIB_DIR"; then dnl * glib in irssi directory, use it AC_MSG_RESULT([yes, using it]) @@ -294,9 +321,9 @@ AC_DEFUN(AC_CHECK_GLIBDIR,[ AC_CHECK_GLIBDIR -if test "x$GLIB_DIR" = "x"; then +if test -z "$GLIB_DIR"; then AM_PATH_GLIB(1.2.0,,, gmodule) - if test "x$GLIB_LIBS" = "x"; then + if test -z "$GLIB_LIBS"; then echo "*** trying without -lgmodule" glib_config_args= AM_PATH_GLIB(1.2.0) @@ -304,7 +331,7 @@ if test "x$GLIB_DIR" = "x"; then AC_DEFINE(HAVE_GMODULE) fi - if test "x$GLIB_LIBS" = "x"; then + if test -z "$GLIB_LIBS"; then echo echo "*** If you don't have GLIB, you can get it from ftp://ftp.gtk.org" echo "*** If you can't install GLIB anywhere or if you don't want to," @@ -319,14 +346,16 @@ if test "x$GLIB_DIR" = "x"; then glib_file=glib-1.2.10.tar.gz dlcmd= - if test "x`ncftpget 2>/dev/null|grep -i ncftp`" != "x"; then + if test -n "`ncftpget 2>/dev/null|grep -i ncftp`"; then dlcmd="ncftpget ftp://ftp.gtk.org/pub/gtk/v1.2/$glib_file" fi - if test "x`wget 2>/dev/null|grep -i wget`" != "x"; then + if test -n "`wget 2>/dev/null|grep -i wget`"; then dlcmd="wget http://irssi.org/files/$glib_file" fi - if test "x$dlcmd" != "x"; then + if test -n "$dlcmd"; then echo "*** I can download GLib for you now. If you don't want to, press CTRL-C now." + echo + echo "*** Press ENTER to continue" read answer eval $dlcmd if `gunzip $glib_file`; then @@ -338,344 +367,109 @@ if test "x$GLIB_DIR" = "x"; then fi fi - if test "x$GLIB_LIBS" = "x"; then + if test -z "$GLIB_LIBS"; then AC_ERROR([GLIB is required to build irssi.]) fi fi fi -PROG_LIBS="$PROG_LIBS $GLIB_LIBS" +LIBS="$LIBS $GLIB_LIBS" dnl ** -dnl ** curses checks +dnl ** check if we can link dynamic libraries to modules +dnl ** also checks if libraries are built to .libs dir dnl ** -dnl Curses detection: Munged from Midnight Commander's configure.in -dnl -dnl What it does: -dnl ============= -dnl -dnl - Determine which version of curses is installed on your system -dnl and set the -I/-L/-l compiler entries and add a few preprocessor -dnl symbols -dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that -dnl @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in -dnl Makefile.in's -dnl - Modify the following configure variables (these are the only -dnl curses.m4 variables you can access from within configure.in) -dnl CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if -dnl an ncurses.h that's been renamed to curses.h -dnl is found. -dnl CURSES_LIBS - sets -L and -l's appropriately -dnl CFLAGS - if --with-sco, add -D_SVID3 -dnl has_curses - exports result of tests to rest of configure -dnl -dnl Usage: -dnl ====== -dnl 1) Add lines indicated below to acconfig.h -dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in -dnl 3) Instead of #include 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 -dnl #else -dnl #include -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 < +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 -#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 < +main() { +GModule *m; int (*modfunc)(void); +m = g_module_open(".libs/$libfile", 0); +if (!m) g_print("error loading: %s", g_module_error()); +else if (!g_module_symbol(m, "modfunc", (gpointer *) &modfunc)) + g_print("modfunc() symbol not found from module"); +else if (modfunc() == 1) g_print("ok"); else g_print("wrong result?! 1 vs %d", modfunc()); +return 0; } +EOF + $CC $CFLAGS $LDFLAGS conftest.c -o conftest $GLIB_CFLAGS $GLIB_LIBS 2> /dev/null > /dev/null + if test ! -s conftest; then + AC_MSG_RESULT([no, error compiling test program]) + else + status="`./conftest`" + if test "x$status" = "xok"; then + DYNLIB_MODULES=yes + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no, error running: $status]) fi + fi +fi +rm -rf conftest conftest.* libconftest.* .libs - dnl use whatever curses there happens to be - if $search_ncurses - then - if test -f /usr/include/curses.h - then - CURSES_LIBS="-lcurses" - AC_DEFINE(HAS_CURSES) - has_curses=true - search_ncurses=false - screen_manager="curses" - fi - fi -]) +dnl ** +dnl ** curses checks +dnl ** if test "x$want_textui" = "xyes"; then AC_CHECK_CURSES - if test "x$has_ncurses" != "x"; then - AC_CHECK_LIB(ncurses, use_default_colors, [ - AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS) - ],, $CURSES_LIBS) - AC_CHECK_LIB(ncurses, idcok, [ - AC_DEFINE(HAVE_CURSES_IDCOK) - ],, $CURSES_LIBS) - AC_CHECK_LIB(ncurses, resizeterm, [ - AC_DEFINE(HAVE_CURSES_RESIZETERM) - ],, $CURSES_LIBS) - AC_CHECK_LIB(ncurses, wresize, [ - AC_DEFINE(HAVE_CURSES_WRESIZE) - ],, $CURSES_LIBS) - elif test "x$has_curses" = "xtrue"; then - AC_CHECK_LIB(curses, idcok, [ - AC_DEFINE(HAVE_CURSES_IDCOK) - ],, $CURSES_LIBS) - AC_CHECK_LIB(curses, resizeterm, [ - AC_DEFINE(HAVE_CURSES_RESIZETERM) - ],, $CURSES_LIBS) - AC_CHECK_LIB(curses, wresize, [ - AC_DEFINE(HAVE_CURSES_WRESIZE) - ],, $CURSES_LIBS) + LIBS="$LIBS $CURSES_LIBS" + if test "x$has_curses" = "xtrue"; then + AC_CHECK_FUNC(use_default_colors, AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS)) + AC_CHECK_FUNC(idcok, AC_DEFINE(HAVE_CURSES_IDCOK)) + AC_CHECK_FUNC(resizeterm, AC_DEFINE(HAVE_CURSES_RESIZETERM)) + AC_CHECK_FUNC(wresize, AC_DEFINE(HAVE_CURSES_WRESIZE)) + if test "x$want_terminfo" = "xyes"; then + AC_CHECK_LIB(curses, setupterm,, [ + want_termcap=yes + ]) + fi else - want_textui=no - curses_error=yes + AC_CHECK_LIB(tinfo, setupterm, [ + LIBS="$LIBS -ltinfo" + want_terminfo=yes + ], AC_CHECK_LIB(termlib, tgetent, [ + LIBS="$LIBS -ltermlib" + want_termcap=yes + ], AC_CHECK_LIB(termcap, tgetent, [ + LIBS="$LIBS -ltermcap" + want_termcap=yes + ], [ + AC_MSG_WARN(Terminfo/termcap not found) + want_textui=no + curses_error=yes + ]))) fi -else - has_curses=false + if test "x$want_termcap" = "xyes"; then + AC_CHECK_FUNC(tparm,, need_tparm=yes) + else + AC_DEFINE(HAVE_TERMINFO) + fi fi dnl ** @@ -683,17 +477,18 @@ dnl ** perl checks dnl ** if test "$want_perl" != "no"; then - AC_PATH_PROG(perlpath, perl) AC_MSG_CHECKING(for working Perl support) - if test "x$perlpath" = "x"; then + if test -z "$perlpath"; then perl_check_error="perl binary not found" else PERL_CFLAGS=`$perlpath -MExtUtils::Embed -e ccopts 2>/dev/null` fi - if test "x$PERL_CFLAGS" = "x"; then - perl_check_error="Error getting perl CFLAGS" + if test -z "$PERL_CFLAGS"; then + if test -n "$perl_check_error"; then + perl_check_error="Error getting perl CFLAGS" + fi AC_MSG_RESULT([not found, building without Perl]) want_perl=no else @@ -718,10 +513,10 @@ if test "$want_perl" != "no"; then fi dnl * don't check libperl.a if dynaloader.a wasn't found.. - if test "x$DYNALOADER_A" != "x"; then + if test -n "$DYNALOADER_A"; then dnl * find either libperl.a or libperl.so LIBPERL_A=`echo "$PERL_LDFLAGS -L/usr/lib"|$perlpath -e 'foreach (split(/ /, )) { if (/^-L(.*)/) { my $dir=$1; if (\`ls $dir/libperl.so* 2>/dev/null\`) { print "-lperl"; last; }; if (-e "$dir/libperl.a") { print "$dir/libperl.a"; last } } };'` - if test "x$LIBPERL_A" = "x"; then + if test -z "$LIBPERL_A"; then perl_mod_error="Didn't find location of -lperl" DYNALOADER_A= elif test "$LIBPERL_A" = "-lperl"; then @@ -754,7 +549,7 @@ if test "$want_perl" != "no"; then dnl * check that perl's ldflags actually work AC_CACHE_VAL(irssi_cv_lib_perl_works, [ echo "main(){perl_alloc(); return 0;}" > conftest.c - $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> /dev/null > /dev/null + $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> perl.error.tmp > /dev/null if test -s conftest; then irssi_cv_lib_perl_works=yes else @@ -763,22 +558,23 @@ if test "$want_perl" != "no"; then ]) if test "x$irssi_cv_lib_perl_works" = "xno"; then - perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS" + perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS: `cat perl.error.tmp`" AC_MSG_RESULT([error linking with perl libraries, building without Perl]) want_perl=no fi + rm -f perl.error.tmp fi if test "x$want_perl" != "xno"; then if test "x$want_perl" = "xstatic"; then AC_MSG_RESULT(ok) - elif test "x$DYNALOADER_A" = "x"; then + elif test -z "$DYNALOADER_A"; then AC_MSG_RESULT([error parsing ldopts, building Perl into irssi binary instead of as module]) want_perl=static else AC_MSG_RESULT(ok) PERL_LDFLAGS=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*[[^ ]]*DynaLoader\.a/\1libperl_dynaloader.la/'` - if test "x$LIBPERL_A" != "x"; then + if test -n "$LIBPERL_A"; then PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lperl /libperl_orig.la /' -e 's/-lperl$/libperl_orig.la$/'` fi AC_SUBST(LIBPERL_A) @@ -788,8 +584,8 @@ if test "$want_perl" != "no"; then if test "x$want_perl" = "xstatic"; then dnl * building with static perl support dnl * all PERL_LDFLAGS linking is done in fe-text - PERL_LDFLAGS="../perl/libperl_static.la $PERL_LDFLAGS" - PERL_LINK_LIBS="$PERL_LDFLAGS" + PERL_LINK_FLAGS="$PERL_LDFLAGS" + PERL_LINK_LIBS="../perl/libperl_core_static.la" PERL_FE_LINK_LIBS="../perl/libfe_perl_static.la" PERL_LDFLAGS= AC_DEFINE(HAVE_STATIC_PERL) @@ -797,31 +593,51 @@ if test "$want_perl" != "no"; then dnl * build only static library of perl module perl_module_lib= perl_module_fe_lib= - perl_static_lib=libperl_static.la + perl_static_lib=libperl_core_static.la perl_static_fe_lib=libfe_perl_static.la PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool' else - dnl * build dynamic library of perl module, - dnl * use libtool-shared to prevent creating of - dnl * libperl.a + dnl * build dynamic library of perl module perl_module_lib=libperl_core.la perl_module_fe_lib=libfe_perl.la perl_static_lib= perl_static_fe_lib= - PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool-shared' + PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool' + fi + + if test "x$want_staticperllib" = "xyes"; then + PERL_MM_PARAMS="$PERL_MM_PARAMS LINKTYPE=static" + PERL_LINK_LIBS="$PERL_LINK_LIBS ../perl/common/blib/arch/auto/Irssi/Irssi.a ../perl/irc/blib/arch/auto/Irssi/Irc/Irc.a ../perl/ui/blib/arch/auto/Irssi/UI/UI.a ../perl/textui/blib/arch/auto/Irssi/TextUI/TextUI.a" + PERL_STATIC_LIBS=1 + else + PERL_STATIC_LIBS=0 fi + + # figure out the correct @INC path - we'll need to do this + # through MakeMaker since it's difficult to get it right + # otherwise. + if test "x$perl_set_use_lib" = "xyes"; then + perl -e 'use ExtUtils::MakeMaker; WriteMakefile("NAME" => "test", "MAKEFILE" => "Makefile.test");' $PERL_MM_PARAMS >/dev/null + PERL_USE_LIB=`perl -e 'open(F, "Makefile.test"); while () { chomp; if (/^(\w+) = (.*$)/) { $keys{$1} = $2; } }; $key = $keys{INSTALLARCHLIB}; while ($key =~ /\\$\((\w+)\)/) { $value = $keys{$1}; $key =~ s/\\$\($1\)/$value/; }; print $key;'` + rm -f Makefile.test + fi + AC_SUBST(perl_module_lib) AC_SUBST(perl_static_lib) AC_SUBST(perl_module_fe_lib) AC_SUBST(perl_static_fe_lib) AC_SUBST(PERL_LIBTOOL) + AC_SUBST(PERL_LINK_FLAGS) AC_SUBST(PERL_LINK_LIBS) - AC_SUBST(PERL_FE_LINK_LIBS) + AC_SUBST(PERL_FE_LINK_LIBS) AC_SUBST(PERL_LDFLAGS) AC_SUBST(PERL_CFLAGS) - AC_SUBST(PERL_LIB_DIR) + + AC_SUBST(PERL_USE_LIB) + AC_SUBST(PERL_MM_PARAMS) + AC_SUBST(PERL_STATIC_LIBS) fi fi @@ -830,10 +646,15 @@ AM_CONDITIONAL(BUILD_TEXTUI, test "$want_textui" = "yes") AM_CONDITIONAL(BUILD_IRSSIBOT, test "$want_irssibot" = "yes") AM_CONDITIONAL(BUILD_IRSSIPROXY, test "$want_irssiproxy" = "yes") AM_CONDITIONAL(BUILD_PLUGINS, test "$want_plugins" = "yes") -AM_CONDITIONAL(BUILD_SERVERTEST, test "x$TEST_DIR" != "x") +AM_CONDITIONAL(BUILD_SERVERTEST, test -n "$TEST_DIR") AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no") AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static") +AM_CONDITIONAL(NEED_TPARM, test "$need_tparm" = "yes") +AM_CONDITIONAL(USE_CURSES, test "$want_terminfo" != "yes" -a "$want_termcap" != "yes") +# move LIBS to PROG_LIBS so they're not tried to be used when linking eg. perl libraries +PROG_LIBS=$LIBS +LIBS= AC_SUBST(PROG_LIBS) dnl ** @@ -844,10 +665,9 @@ dnl ** (these could be made configurable) CHAT_MODULES="silc" silc_MODULES="" -if test "x$build_modules" != "x"; then +if test -n "$build_modules"; then silc_MODULES="$silc_MODULES $build_modules" fi -#PROG_LIBS="$PROG_LIBS -lsilc" dnl **************************************** @@ -868,11 +688,11 @@ for c in $CHAT_MODULES; do FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/libfe_common_$c.a " fi for s in `eval echo \\$${c}_MODULES`; do - CHAT_LIBS="$CHAT_LIBS ../$c/$s/.libs/lib${c}_$s.a" + CHAT_LIBS="$CHAT_LIBS ../$c/$s/lib${c}_$s.a" module_inits="$module_inits ${c}_${s}_init();" module_deinits="${c}_${s}_deinit(); $module_deinits" if test -f $srcdir/src/fe-common/$c/$s/module.h; then - FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/$s/.libs/libfe_${c}_$s.a " + FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/$s/libfe_${c}_$s.a " fe_module_inits="$fe_module_inits fe_${c}_${s}_init();" fe_module_deinits="fe_${c}_${s}_deinit(); $fe_module_deinits" fi @@ -880,19 +700,18 @@ for c in $CHAT_MODULES; do file="$srcdir/src/$c/$c.c" echo "/* this file is automatically generated by configure - don't change */" > $file - echo "void ${c}_core_init(void); void ${c}_core_init_finish(void); void ${c}_core_deinit(void);" >> $file - if test "x$module_inits" != "x"; then + echo "void ${c}_core_init(void); void ${c}_core_deinit(void);" >> $file + if test -n "$module_inits"; then echo "$module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file echo "$module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file fi echo "void ${c}_init(void) { ${c}_core_init(); $module_inits }" >> $file - echo "void ${c}_init_finish(void) { ${c}_core_init_finish(); $module_inits }" >> $file echo "void ${c}_deinit(void) { $module_deinits ${c}_core_deinit(); }" >> $file if test -f $srcdir/src/fe-common/$c/module.h; then file="$srcdir/src/fe-common/$c/${c}-modules.c" echo "/* this file is automatically generated by configure - don't change */" > $file - if test "x$fe_module_inits" != "x"; then + if test -n "$fe_module_inits"; then echo "$fe_module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file echo "$fe_module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file fi @@ -909,15 +728,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS" AC_SUBST(COMMON_NOUI_LIBS) AC_SUBST(COMMON_LIBS) -dnl ** -dnl ** memory debugging -dnl ** - -if test "x$want_memdebug" = "xyes"; then - AC_DEFINE(MEM_DEBUG) -fi -AM_CONDITIONAL(BUILD_MEMDEBUG, test "x$want_memdebug" = "xyes") - dnl ** dnl ** tr-Chinese Big5 support dnl ** @@ -931,9 +741,35 @@ dnl ** IPv6 support dnl ** if test "x$want_ipv6" = "xyes"; then - AC_DEFINE(HAVE_IPV6) + AC_MSG_CHECKING([for IPv6]) + AC_CACHE_VAL(irssi_cv_type_in6_addr, + [AC_TRY_COMPILE([ + #include + #include + #include + #include + #include ], + [struct in6_addr i;], + irssi_cv_type_in6_addr=yes, + irssi_cv_type_in6_addr=no, + )]) + if test $irssi_cv_type_in6_addr = yes; then + AC_DEFINE(HAVE_IPV6) + fi + AC_MSG_RESULT($irssi_cv_type_in6_addr) fi +dnl ** +dnl ** IRSSI_VERSION_DATE and IRSSI_VERSION_TIME +dnl ** +#VERSION_DATE=`head -1 $srcdir/ChangeLog|sed 's/^\(....\)-\(..\)-\(..\).*/\1\2\3/'` +#VERSION_TIME=`head -1 $srcdir/ChangeLog|sed -e 's/^[[^ ]]* \(..\):\(..\).*/\1\2/' -e 's/^0*//'` +#AC_SUBST(VERSION_DATE) +#AC_SUBST(VERSION_TIME) + +# +# Glue into SILC build system +# INCLUDE_DEFINES_INT="include \$(top_srcdir)/Makefile.defines_int" AC_SUBST(INCLUDE_DEFINES_INT) @@ -949,20 +785,30 @@ src/lib-config/Makefile src/lib-popt/Makefile src/silc/Makefile src/silc/core/Makefile +src/perl/Makefile +src/perl/common/Makefile.PL +src/perl/ui/Makefile.PL +src/perl/textui/Makefile.PL +scripts/Makefile docs/Makefile docs/help/Makefile docs/help/in/Makefile +irssi-version.h stamp.h irssi.spec -irssi-version.h irssi-config) dnl ** for building from objdir -if test "x$want_perl" != "xno"; then - old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir +old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir +if test "x$old_dir" != "x$whole_dir"; then + $LN_S $srcdir/irssi-version.h irssi-version.h - if test "x$old_dir" != "x$whole_dir"; then - for file in $whole_dir/src/perl/*.[[ch]] $whole_dir/src/perl/libperl_orig.la $whole_dir/src/perl/libperl_dynaloader.la $whole_dir/src/perl/common/typemap $whole_dir/src/perl/common/module.h $whole_dir/src/perl/common/*.xs $whole_dir/src/perl/irc/typemap $whole_dir/src/perl/irc/module.h $whole_dir/src/perl/irc/*.xs; do + if test "x$want_perl" != "xno"; then + subdirfiles="" + for i in $whole_dir/src/perl/common $whole_dir/src/perl/irc $whole_dir/src/perl/ui $whole_dir/src/perl/textui; do + subdirfiles=`echo $subdirfiles $i/typemap $i/module.h $i/*.pm $i/*.xs` + done + for file in $whole_dir/src/perl/*.[[ch]] $whole_dir/src/perl/libperl_orig.la $whole_dir/src/perl/libperl_dynaloader.la $subdirfiles; do link=`echo $file|$sedpath "s?$whole_dir/??"` rm -f $link $LN_S $file $link @@ -973,7 +819,16 @@ fi echo if test "x$curses_error" != "xyes"; then - echo "Building text frontend ..... : $want_textui" + if test "x$want_textui" = "xno"; then + text=no + elif test "x$want_termcap" = "xyes"; then + text="yes, using termcap" + elif test "x$want_terminfo" = "xyes"; then + text="yes, using terminfo" + else + text="yes, using curses" + fi + echo "Building text frontend ..... : $text" else echo "Building text frontend ..... : NO!!" echo " - Because curses was not found, specify the path to it with" @@ -986,10 +841,10 @@ echo "Building with IPv6 support . : $want_ipv6" if test "x$want_perl" = "xstatic"; then echo "Building with Perl support . : static (in irssi binary)" -elif test "x$want_perl" = "xyes"; then +elif test "x$want_perl" = "xmodule"; then echo "Building with Perl support . : module" else - if test "x$perl_check_error" = "x"; then + if test -z "$perl_check_error"; then echo "Building with Perl support . : no" else echo "Building with Perl support . : NO!" @@ -1003,18 +858,20 @@ if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then echo " $perl_mod_error" fi -if test "x$want_perl" = "xyes"; then - if test "x$PERL_LIB_DIR" = "x"; then - echo "Perl library directory ..... : (default - usually /usr/local/lib/perl_site)" - else - echo "Perl library directory ..... : $PERL_LIB_DIR" - if test "x$perl_lib_dir_given" != "xyes"; then - echo " - NOTE: This was automatically set to the same directory you gave with" - echo " --prefix. If you want the perl libraries to install to their 'correct'" - echo " path, you'll need to give --enable-perl-path= (nothing after '=') option" - echo " to configure. Anyway, installing perl to this directory should work" - echo " just as well.." - fi +if test "x$want_perl" != "xno"; then + if test "$perl_library_dir" = "PERL_USE_LIB"; then + perl_library_dir=$PERL_USE_LIB + fi + if test -z "$perl_library_dir"; then + perl_library_dir="(site default - `$perlpath -e 'use Config; print $Config{sitearch}'`)" + fi + echo "Perl library directory ..... : $perl_library_dir" + if test "x$perl_prefix_note" = "xyes"; then + echo " - NOTE: This was automatically set to the same directory you gave with" + echo " --prefix. If you want the perl libraries to install to their 'correct'" + echo " path, you'll need to give --with-perl-lib=site option to configure." + echo " Anyway, installing perl to this directory should work just as well." fi fi echo "Install prefix ............. : $prefix" + diff --git a/apps/irssi/curses.m4 b/apps/irssi/curses.m4 new file mode 100644 index 00000000..83a08126 --- /dev/null +++ b/apps/irssi/curses.m4 @@ -0,0 +1,296 @@ +dnl Curses detection: Munged from Midnight Commander's configure.in +dnl +dnl What it does: +dnl ============= +dnl +dnl - Determine which version of curses is installed on your system +dnl and set the -I/-L/-l compiler entries and add a few preprocessor +dnl symbols +dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that +dnl @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in +dnl Makefile.in's +dnl - Modify the following configure variables (these are the only +dnl curses.m4 variables you can access from within configure.in) +dnl CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if +dnl an ncurses.h that's been renamed to curses.h +dnl is found. +dnl CURSES_LIBS - sets -L and -l's appropriately +dnl CFLAGS - if --with-sco, add -D_SVID3 +dnl has_curses - exports result of tests to rest of configure +dnl +dnl Usage: +dnl ====== +dnl 1) Add lines indicated below to acconfig.h +dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in +dnl 3) Instead of #include 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 +dnl #else +dnl #include +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 +#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 +]) + diff --git a/apps/irssi/default.theme b/apps/irssi/default.theme index 4e1ec830..427f7f59 100644 --- a/apps/irssi/default.theme +++ b/apps/irssi/default.theme @@ -231,24 +231,45 @@ abstracts = { ## statusbar ## - # background of statusbar - sb_background = "%4"; - - # default statusbar item style - sb = "%c[%n$0-%c]%n"; - - sbmode = "(%c+%n$0-)"; + # default background for all statusbars. You can also give + # the default foreground color for statusbar items. + sb_background = "%4%w"; + + # default backround for "default" statusbar group + #sb_default_bg = "%4"; + # background for prompt / input line + sb_prompt_bg = "%0"; + # background for info statusbar + sb_info_bg = "%8"; + # background for topicbar (same default) + #sb_topic_bg = "%4"; + + # text at the beginning of statusbars. sb-item already puts + # space there,so we don't use anything by default. + sbstart = ""; + # text at the end of statusbars. Use space so that it's never + # used for anything. + sbend = " "; + + prompt = "[$*] "; + + sb = " %c[%n$*%c]%n"; + sbmode = "(%c+%n$*)"; sbaway = " (%GzZzZ%n)"; - sbservertag = "%c:%n$0 (change with ^X)"; - sbmore = "%_-- more --%_"; - sblag = "{sb Lag: $0-}"; - sbmail = "{sb Mail: $0-}"; - - # activity. Det is used for hilights when display doesn't support colors - sbact = "{sb {sbact_act $0}{sbact_det $1}}"; - sbact_act = "Act: $0-"; - sbact_det = " Det: $0-"; - + sbservertag = ":$0 (change with ^X)"; + + # activity in statusbar + + # ',' separator + sb_act_sep = "%c$*"; + # normal text + sb_act_text = "%c$*"; + # public message + sb_act_msg = "%W$*"; + # hilight + sb_act_hilight = "%M$*"; + # hilight with specified color, $0 = color, $1 = text + sb_act_hilight_color = "$0$1-%n"; }; # diff --git a/apps/irssi/docs/.cvsignore b/apps/irssi/docs/.cvsignore new file mode 100644 index 00000000..c5b9e552 --- /dev/null +++ b/apps/irssi/docs/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +startup-HOWTO.txt diff --git a/apps/irssi/docs/botnet.txt b/apps/irssi/docs/botnet.txt new file mode 100644 index 00000000..8b5f9ba7 --- /dev/null +++ b/apps/irssi/docs/botnet.txt @@ -0,0 +1,316 @@ + + Irssi's botnet description + + Copyright (c) 1999-2000 Timo Sirainen + + + 0. History + + draft v0.1 : 21.8.1999 + + Just a first draft of my botnet design I did on a boring friday + work afternoon :) I'll try to implement this to irssi some day, it + feels pretty interesting now so it might be pretty soon even. Any + comments are welcome :) + + draft v0.2 : 21.11.1999 + + Exactly three months since the first draft :) Now I actually have + some code done, just committed pretty simple botnet to irssi CVS. + Made several changes to this document.. Still missing much details + but the basic idea should be clear. + + draft v0.3 : 21.05.2000 + + Strange, again the same day. I really didn't plan this :) + Reformatted the text, added lots of text, implemented more of the + stuff. + + + 1. General + + 1.1 Description + + A small description of what botnet would do: A group of bots + efficiently working together to perform their tasks. Like when + someone's trying to take over your channel, bots will quickly + decide who deops/kicks whom instead of multiple bots deopping or + trying to kick the same people. + + Irssi's botnet is pretty much based on trust. Some malicious bot + can quite well mess up the whole botnet. Connecting the bots to + each other via ssh would be a good idea. + + 1.2 Configuration + + example config file: + + mybotnet = + { + priority=5; + nick=mybot; + uplinks = ( + { host = "main.botnet.org"; password = "mypass"; }, + { host = "alter.botnet.org"; password = "pass2"; } + ); + downlinks = ( + { password = "thepass"; valid_addrs = ( "192.168.0.*" ); }, + { password = "blah"; valid_addrs = ( "*.botnet.org" ); }, + { password = "localpass"; valid_addrs = ( "127.*" ); } + ); + } + + When connecting to botnet, the bot first tries to connect to the + first bot in uplinks list, then the second, etc. Setting port to -1 + will prevent connecting to the bot, 0 uses the default. + + 1.3 Botnet master + + To avoid total chaos inside botnet, the bots shouldn't do (almost) + anything without a command from botnet's master. The master should + know everything, and give commands to clients that can perform the + task best. + + Master is the bot with the highest priority. If there's multiple + with the same priority, the one that already was the master will + stay master. When joining two botnets to one, the uplink's master + stays. If link to master breaks, the closest bot to it will choose + a new one. + + The priorities should be given so that the bots that have the + fastest connections and are in the middle of the botnet have the + highest priorities. + + 1.4 Command format + + Commands that are sent inside botnet are in the following format: + + 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 + HOST : (if error, disconnect) + + CLIENT: 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 + + 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 + + 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 + + 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 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 + + 4.2 IRC commands + + Master asks a bot to send some command to IRC server by saying: + + CMD + + can't really be anything, since the bot should also be + able to reply to it. The is for identifying the command/reply + pair. Master should keep the command in memory until it receives + the reply: + + CMDREPLY + + The command could get a reply of multiple lines, so + specifies if the reply is the last line (1 or 0). + + If the command failed for some reason, the bot will reply with + + CMDFAIL + + and master should send the command to some other bot. + + 4.3 Channels + + When joined/left channels, the bot says: + + CHANJOIN + CHANPART + + After BOTJOIN, master tries to op the bot. When bot receives +o, + it says: + + CHANOP + + 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 + + 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 + (or) + CHANCANTJOIN + + 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 NAMES + CMD WHO + CMD MODE + CMD MODE b + CMD MODE e (if IRC network supports this) + CMD MODE I (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 + diff --git a/apps/irssi/docs/crash.txt b/apps/irssi/docs/crash.txt new file mode 100644 index 00000000..d3100f57 --- /dev/null +++ b/apps/irssi/docs/crash.txt @@ -0,0 +1,59 @@ +How to submit a good bug report? + +First you should give the following information: + - irssi version, if CVS (or devel. tarball) then which day? + - operating system / distribution and it's version + - when did it crash? did you do something? can you reproduce the crash? + +Getting backtrace of the crash also helps a lot, especially if irssi crashes +randomly. If after crash you see text: + + "segmentation fault (core dumped)" + +It writes a file named "core" or "irssi.core" depending on your OS to +directory where you started irssi. If it doesn't print the "(core dumped)" +or you can't find the core file, you'll have to raise the limit for max. +core file size before running irssi. To do this, say: + + ulimit -c unlimited + +So, if you have the core file and GNU debugger (gdb), you can get the +backtrace with: + + gdb irssi core + bt + +Paste all the lines starting from line having #0 at the beginning. + +Here's an example session: + +[cras@hurina] ~/cvs/m/irssi/src/fe-text$ gdb ./irssi core + +GNU gdb 5.0 +Copyright 2000 Free Software Foundation, Inc. +GDB is free software, covered by the GNU General Public License, and you are +welcome to change it and/or distribute copies of it under certain conditions. +Type "show copying" to see the conditions. +There is absolutely no warranty for GDB. Type "show warranty" for details. +This GDB was configured as "i686-pc-linux-gnu"... + +Core was generated by ./irssi'. +Program terminated with signal 11, Segmentation fault. +#0 0x805e949 in view_scroll (view=0x816cfb5, lines=0x816cfd9, + subline=0x816cfdd, scrollcount=-11, draw_nonclean=1) + at textbuffer-view.c:528 +528 realcount += view->bottom_subline; + +(gdb) bt + +#0 0x805e949 in view_scroll (view=0x816cfb5, lines=0x816cfd9, + subline=0x816cfdd, scrollcount=-11, draw_nonclean=1) + at textbuffer-view.c:528 +#1 0x805ecb4 in textbuffer_view_scroll (view=0x816cfb5, lines=-11) + at textbuffer-view.c:669 +#2 0x8058387 in gui_window_scroll (window=0x816cead, lines=-11) + at gui-windows.c:128 +#3 0x8056b64 in window_prev_page () at gui-readline.c:109 +#4 0x8057047 in key_scroll_backward () at gui-readline.c:334 +... +(gdb) diff --git a/apps/irssi/docs/design.txt b/apps/irssi/docs/design.txt new file mode 100644 index 00000000..5c8ddcf1 --- /dev/null +++ b/apps/irssi/docs/design.txt @@ -0,0 +1,156 @@ + + Irssi's hierarchy is something like this: + + + sub1 sub2 + \ / + xxx IRC COMMON ICQ yyy + |____|___________|____|____| + | + GUI (gtk/gnome, qt/kde, text, none) + | + sub1 sub2 | + \ / | + xxx IRC | COMMON ICQ yyy + |____|_____|_____|____|____| + | + COMMON UI + | + sub1 sub2 | + \ / | + xxx IRC | ICQ yyy + |____|_____|_____|____| + | + CORE + / \ + lib-config lib-popt + + + (IRC, ICQ, xxx and yyy are chat protocols ..) + (sub1 and sub2 are submodules of IRC module, like DCC and flood protect) + + + Chat protocols and frontends are kept in separate modules. Common UI + and GUI modules also have the common parts which don't know anything + about the chat protocols. This should allow implementing modules to + whatever chat protocols and with whatever frontends easily. + + ** Signals + + Communication between different modules are done with "signals". They are + not related to UNIX signals in any way, you could more like think of them + as "events" - which might be a better name for them, but I don't really + want to change it anymore :) + + So, you send signal with signal_emit() and it's sent to all modules that + have grabbed it by calling signal_add() in their init function. For + example: + + signal_emit("mysignal", 1, "hello"); + + Sends a "mysignal" function with one argument "hello" - before that, you + should have grabbed the signal somewhere else with: + + static void sig_mysignal(const char *arg1) + { + /* arg1 contains "hello" */ + } + + signal_add("mysignal", (SIGNAL_FUNC) sig_mysignal); + + There are three different signal_add() functions which you can use to + specify if you want to grab the signal first, "normally" or last. You can + also stop the signal from going any further. + + Emitting signal with it's name creates a small overhead since it has to + look up the signal's numeric ID first, after which it looks up the signal + structure. This is done because if you call a signal _really_ often, + it's faster to find it with it's numeric ID instead of the string. You + can use signal_get_uniq_id() macro to convert the signal name into ID - + you'll have to do this only once! - and use signal_emit_id() to emit the + signal. Don't bother to do this unless your signal is sent (or could be + sent) several times in a second. + + See src/core/signals.h for defination of the signal function, and + signals.txt for a list of signals. + + + ** lib-popt + + CORE depends on this for command line parameter handling. + (distributed with irssi) + + + ** lib-config + + Irssi depends on this for reading and saving configuration. + (created by me for irssi) + + + ** CORE module + + Provides some functionality that all other modules can use: + - signal handling + - keeping list of settings + - keeping list of /commands + - keeping track of loaded modules + - networking functions (with nonblocking connects, IPv6 support) + - handles connecting to servers + - raw logging of server's input/output data + - /EVAL support + - fgets() like function line_split() without any maximum line limits + - command line parameter handling + - miscellaneous useful little functions + - handles logging + + + ** COMMON UI module + + - knows basics about windows and window items (=channels, queries, ..) + - printtext() - parsing texts and feeding it for GUI to print. + - themes + - translation tables + - text hilighting + - command history + - user interface (/commands) for CORE's functionality + + + ** GUI modules + + - all the rest of the functionality needed for a working client. + + + ** IRC module + + * CORE + + - IRC specific /commands + - flood protecting commands sent to server + - creating IRC masks based on nick/address for bans, ignores, etc. + - keeps list of channels, nicks, channel modes, bans, etc. + - keeps list of servers, server settings, irc networks, + server reconnections and irc network splits + - redirection of commands' replies + - lag detection + - ctcp support and flood protection + - Handles ignoring people + + * DCC + + - DCC chat, send and get + + * FLOOD + + - detects private or channel flooding and sends "flood" signal + - automatic ignoring when flooding + + * NOTIFYLIST + + - handles notifylist + + + ** IRC UI module + + - placing channels and queries in windows + - nick completion + - printing infomation of some events diff --git a/apps/irssi/docs/formats.txt b/apps/irssi/docs/formats.txt index 6dabc43c..e98f2719 100644 --- a/apps/irssi/docs/formats.txt +++ b/apps/irssi/docs/formats.txt @@ -29,26 +29,27 @@ foreground (fg) background (bg) ------------------------------------------------------- - 0 white black with 0 as fg - light gray with fg > 0 - 1 ligh gray black + 0 white light gray + blinking fg + 1 black black 2 blue blue 3 green green - 4 light red red + blinking fg - 5 orange orange + 4 light red red + blinking fg + 5 red red 6 magenta (purple) magenta - 7 red red - 8 yellow orange + blinking fg - 9 light green light green + blinking fg - 10 cyan cyan - 11 light cyan cyan + blinking fg - 12 light blue blue + blinking fg - 13 light magenta magenta + blinking fg - 14 gray black + blinking fg - 15 light gray black on black - gray on light gray (with bold) + 7 orange orange + 8 yellow orange + blinking fg + 9 light green green + blinking fg + 10 cyan cyan + 11 light cyan cyan + blinking fg + 12 light blue blue + blinking fg + 13 light magenta magenta + blinking fg + 14 gray black + blinking fg + 15 light gray light gray - These colors may differ depending on your terminal. + These colors may differ depending on your terminal. In particular + the meaning for background may be the same as for the foreground + (bright colors, no blinking), and orange often looks like brown or + dark yellow. How to use these colors ('#' means a number as MIRC color code): diff --git a/apps/irssi/docs/help/in/invitelist.in b/apps/irssi/docs/help/in/invitelist.in new file mode 100644 index 00000000..ed6bc060 --- /dev/null +++ b/apps/irssi/docs/help/in/invitelist.in @@ -0,0 +1,9 @@ + +@SYNTAX:invitelist@ + +Shows the +I modes of the current channel. +I mode +allows free joins of clients with certain userhost mask +even if the channel is invite only. + +See also: INVITE, MODE + diff --git a/apps/irssi/docs/help/in/perlflush.in b/apps/irssi/docs/help/in/perlflush.in new file mode 100644 index 00000000..27fc509f --- /dev/null +++ b/apps/irssi/docs/help/in/perlflush.in @@ -0,0 +1,8 @@ + +@SYNTAX:perlflush@ + +Stops and removes all Perl scripts which have been run. +Also undefines all the commands defined by Perl scripts. + +See also: RUN + diff --git a/apps/irssi/docs/manual.txt b/apps/irssi/docs/manual.txt index 7c25c7bc..2954deff 100644 --- a/apps/irssi/docs/manual.txt +++ b/apps/irssi/docs/manual.txt @@ -146,10 +146,15 @@ --enable-memdebug Enable memory debugging, great for finding memory leaks - --enable-perl=static Build Perl support statically to irssi binary + --with-perl=static Build Perl support statically to irssi binary (default is to build a module) - --enable-perl-path=dir Specify installation dir for Perl libraries - --disable-perl Disable Perl support + --with-perl-lib=[site|vendor|DIR] Specify installation dir for + Perl libraries. Site is the default (usually + /usr/local/lib/perl/...), vendor uses the path + where the base of the perl is installed + (/usr/lib/perl/...), or DIR specifies exactly + where you want to install it. + --without-perl Disable Perl support --with-socks Build with socks library --with-bot Build irssi-bot diff --git a/apps/irssi/docs/perl.txt b/apps/irssi/docs/perl.txt new file mode 100644 index 00000000..b04e0cb9 --- /dev/null +++ b/apps/irssi/docs/perl.txt @@ -0,0 +1,1143 @@ + Running Perl scripts + -------------------- + +First you'll need to have Perl support on. By default irssi compiles +Perl as a module, so /LOAD perl probably helps. If you want to do this +automatically at startup put the "/LOAD perl" to ~/.irssi/startup file. +After that you can run scripts with /RUN script (you don't need to give +the .pl extension). If /RUN complains about "unknown command", you +don't have Perl module loaded, or maybe Perl support wasn't compiled at +all. + +Place new scripts to ~/.irssi/scripts/ or /usr/local/lib/irssi/scripts/ +directory. Scripts in ~/.irssi/scripts/autorun/ directory are +automatically run at startup. + +Using /PERLFLUSH closes and reopens the perl interpreter removing all +Perl scripts from memory. There's currently no way to unload a single +Perl script (/SCRIPT REMOVE will probably work soon). You can however +run same script multiple times, and irssi will remove the old version +from memory before running the new version. + + + Irssi's signals + --------------- + +Irssi is pretty much based on sending and handling different signals. +Like when you receive a message from server, say + + :nick!user@there.org PRIVMSG you :blahblah + +Irssi will first send a signal: + + "server incoming", SERVER_REC, "nick!user@there PRIVMSG ..." + +You probably don't want to use this signal. Default handler for this +signal interprets the header and sends a signal: + + "server event", SERVER_REC, "PRIVMSG ...", "nick", "user@there.org" + +You probably don't want to use this either, since this signal's default +handler parses the event string and sends a signal: + + "event privmsg", SERVER_REC, "you :blahblah", "nick", "user@there.org" + +You can at any point grab the signal, do whatever you want to do with +it and optionally stop it from going any further by calling +Irssi::signal_stop(); + +For example: + + sub event_privmsg { + # $data = "nick/#channel :text" + my ($server, $data, $nick, $address) = @_; + my ($target, $text) = split(/ :/, $data, 2); + + Irssi::signal_stop() if ($text =~ /free.*porn/ || $nick =~ /idiot/); + } + +Irssi::signal_add("event privmsg", "event_privmsg") + +This will hide all public or private messages that match the regexp +"free.*porn" or the sender's nick contain the word "idiot". Yes, you +could use /IGNORE instead for both of these :) + +You can also use signal_add_last() if you wish to let the Irssi's internal +functions be run before yours. + +A list of signals that irssi sends can be found from signals.txt file. + + + Creating/replacing /COMMANDS + ---------------------------- + +You can create your own commands, or replace existing ones with +Irssi::command_bind(). The command handling work internally pretty much +the same as signal handlers, so if you replace existing command and don't +wish to let it run, call Irssi::signal_stop(). + +Here's an example: + + # Usage: /HELLO [] + 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_win - print text to this window + + shell - start the program via /bin/sh + notice - send text with /notice, not /msg if target is set + silent - don't print "process exited with level xx" diff --git a/apps/irssi/docs/proxy.txt b/apps/irssi/docs/proxy.txt new file mode 100644 index 00000000..20e51715 --- /dev/null +++ b/apps/irssi/docs/proxy.txt @@ -0,0 +1,26 @@ +Irssi proxy usage: + +First you'll need to have the proxy module installed, either configure +irssi with --with-proxy and do make install, or manually: + + cd src/irc/proxy + make + mkdir ~/.irssi/modules + cp .libs/libproxy.so ~/.irssi/modules/ + +In irssi, say: + + /LOAD proxy + +You really should set some password for the proxy with: + + /SET irssiproxy_password secret + +Then you'll need to configure the ports/ircnets the proxy listens in, +something like: + + /SET irssiproxy_ports ircnet=2777 efnet=2778 opn=2779 + +There we have 3 different irc networks answering in 3 ports. Note that +you'll have to make the correct /IRCNET ADD and /SERVER ADD commands to +make it work properly. diff --git a/apps/irssi/docs/signals.txt b/apps/irssi/docs/signals.txt new file mode 100644 index 00000000..f32789fc --- /dev/null +++ b/apps/irssi/docs/signals.txt @@ -0,0 +1,322 @@ +List of signals irssi emits - see design.txt for more information about +signals. + +core +---- + +* Requires to work properly: + + "gui exit" + "gui dialog", char *type, char *text + "send command", char *command, SERVER_REC, WI_ITEM_REC + +* Provides signals: + +chat-protocols.c: + "chat protocol created", CHAT_PROTOCOL_REC + "chat protocol updated", CHAT_PROTOCOL_REC + "chat protocol destroyed", CHAT_PROTOCOL_REC + +channels.c: + "channel created", CHANNEL_REC, int automatic + "channel destroyed", CHANNEL_REC + +chatnets.c: + "chatnet created", CHATNET_REC + "chatnet destroyed", CHATNET_REC + +commands.c: + "commandlist new", COMMAND_REC + "commandlist remove", COMMAND_REC + "error command", int err, char *cmd + + "send command", char *args, SERVER_REC, WI_ITEM_REC + "send text", char *line, SERVER_REC, WI_ITEM_REC + "command ", 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 ", 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 ", 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 ", 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 ", 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 ", char *args, DCC_REC + "default dcc ctcp", char *args, DCC_REC + "dcc unknown ctcp", char *args, char *sender, char *sendaddr + + "dcc reply ", char *args, DCC_REC + "default dcc reply", char *args, DCC_REC + "dcc unknown reply", char *args, char *sender, char *sendaddr + + "dcc chat message", DCC_REC, char *msg + + "dcc created", DCC_REC + "dcc destroyed", DCC_REC + "dcc connected", DCC_REC + "dcc rejecting", DCC_REC + "dcc closed", DCC_REC + "dcc request", DCC_REC, char *sendaddr + "dcc request send", DCC_REC + "dcc chat message", DCC_REC, char *msg + "dcc transfer update", DCC_REC + "dcc get receive", DCC_REC + "dcc error connect", DCC_REC + "dcc error file create", DCC_REC, char *filename + "dcc error file open", char *nick, char *filename, int errno + "dcc error get not found", char *nick + "dcc error send exists", char *nick, char *filename + "dcc error unknown type", char *type + "dcc error close not found", char *type, char *nick, char *filename + +autoignore.c: + + "autoignore new", SERVER_REC, AUTOIGNORE_REC + "autoignore remove", SERVER_REC, AUTOIGNORE_REC + +flood.c: + + "flood", SERVER_REC, char *nick, char *host, int level, char *target + +notifylist.c: + + "notifylist new", NOTIFYLIST_REC + "notifylist remove", NOTIFYLIST_REC + "notifylist joined", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg + "notifylist away changed", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg + "notifylist unidle", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg + "notifylist left", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg + +proxy/listen.c: + + "proxy client connected", CLIENT_REC + "proxy client disconnected", CLIENT_REC + +FE common +--------- + +* Requires to work properly: + + "gui print text", WINDOW_REC, int fg, int bg, int flags, char *text, int level + +(Can be used to determine when all "gui print text"s are sent (not required)) + "gui print text finished", WINDOW_REC + +* Provides signals: + +completion.c: + "complete word", GList * of char*, WINDOW_REC, char *word, char *linestart, int *want_space + +fe-common-core.c: + "irssi init read settings" + +fe-exec.c: + "exec new", PROCESS_REC + "exec remove", PROCESS_REC, int status + "exec input", PROCESS_REC, char *text + +fe-messages.c: + "message public", SERVER_REC, char *msg, char *nick, char *address, char *target + "message private", SERVER_REC, char *msg, char *nick, char *address + "message own_public", SERVER_REC, char *msg, char *target + "message own_private", SERVER_REC, char *msg, char *target, char *orig_target + "message join", SERVER_REC, char *channel, char *nick, char *address + "message part", SERVER_REC, char *channel, char *nick, char *address, char *reason + "message quit", SERVER_REC, char *nick, char *address, char *reason + "message kick", SERVER_REC, char *channel, char *nick, char *kicker, char *address, char *reason + "message nick", SERVER_REC, char *newnick, char *oldnick, char *address + "message own_nick", SERVER_REC, char *newnick, char *oldnick, char *address + "message invite", SERVER_REC, char *channel, char *nick, char *address + "message topic", SERVER_REC, char *channel, char *topic, char *nick, char *address + +keyboard.c: + "keyinfo created", KEYINFO_REC + "keyinfo destroyed", KEYINFO_REC + +printtext.c: + "print text", TEXT_DEST_REC *dest, char *text, char *stripped + +themes.c: + "theme created", THEME_REC + "theme destroyed", THEME_REC + +window-activity.c: + "window hilight", WINDOW_REC + "window activity", WINDOW_REC, int old_level + "window item hilight", WI_ITEM_REC + "window item activity", WI_ITEM_REC, int old_lvel + +window-items.c: + "window item new", WINDOW_REC, WI_ITEM_REC + "window item remove", WINDOW_REC, WI_ITEM_REC + "window item changed", WINDOW_REC, WI_ITEM_REC + "window item server changed", WINDOW_REC, WI_ITEM_REC + +windows.c: + "window created", WINDOW_REC + "window destroyed", WINDOW_REC + "window changed", WINDOW_REC, WINDOW_REC old + "window changed automatic", WINDOW_REC + "window server changed", WINDOW_REC, SERVER_REC + "window refnum changed", WINDOW_REC, int old + "window name changed", WINDOW_REC + "window history changed", WINDOW_REC, char *oldname + "window level changed", WINDOW_REC + +FE IRC +------ + +fe-irc-messages.c: + "message irc op_public", SERVER_REC, char *msg, char *nick, char *address, char *target + "message irc own_wall", SERVER_REC, char *msg, char *target + "message irc own_action", SERVER_REC, char *msg, char *target + "message irc action", SERVER_REC, char *msg, char *nick, char *address, char *target + "message irc own_notice", SERVER_REC, char *msg, char *target + "message irc notice", SERVER_REC, char *msg, char *nick, char *address, char *target + "message irc own_ctcp", SERVER_REC, char *cmd, char *data, char *target + "message irc ctcp", SERVER_REC, char *msg, char *nick, char *address, char *target + +dcc/fe-dcc-chat-messages.c: + "message dcc own", DCC_REC *dcc, char *msg + "message dcc own_action", DCC_REC *dcc, char *msg + "message dcc own_ctcp", DCC_REC *dcc, char *cmd, char *data + "message dcc", DCC_REC *dcc, char *msg + "message dcc action", DCC_REC *dcc, char *msg + "message dcc ctcp", DCC_REC *dcc, char *cmd, char *data + +Text FE +------- + +gui-printtext.c: + "beep" + +statusbar-items.c: + "mail counter" diff --git a/apps/irssi/docs/special_vars.txt b/apps/irssi/docs/special_vars.txt new file mode 100644 index 00000000..c56c452f --- /dev/null +++ b/apps/irssi/docs/special_vars.txt @@ -0,0 +1,113 @@ +NOTE: This is just a slightly modified file taken from EPIC's help. + +Special Variables and Expandos + +Irssi supports a number of reserved, dynamic variables, sometimes +referred to as expandos. They are special in that the client is +constantly updating their values automatically. There are also +numerous variable modifiers available. + + Modifier Description + $variable A normal variable, expanding to the first match of: + | 1) an internal SET variable + | 2) an environment variable + $[num]variable Expands to the variables value, with 'num' width. If + | the number is negative, the value is right-aligned. + | The value is padded to meet the width with the + | character given after number (default is space). + | The value is truncated to specified width unless + | '!' character precedes the number. If '.' character + | precedes the number the value isn't padded, just + | truncated. + $#variable Expands to the number of words in $variable. If $variable + | is omitted, it assumes $* + $@variable Expands to the number of characters in $variable. if + | $variable is omitted, it assumes $* + $($subvariable) This is somewhat similar to a pointer, in that the + | value of $subvar is taken as the name of the + | variable to expand to. Nesting is allowed. + ${expression} Permits the value to be embedded in another string + | unambiguously. + $!history! Expands to a matching entry in the client's command + | history, wildcards allowed. + +Whenever an alias is called, these expandos are set to the arguments passed +to it. If none of these expandos are used in the alias, or the $() form +shown above, any arguments passed will automatically be appended to the last +command in the alias. + + Expando Description + $* expands to all arguments passed to an alias + $n expands to argument 'n' passed to an alias (counting from zero) + $n-m expands to arguments 'n' through 'm' passed to an alias + $n- expands to all arguments from 'n' on passed to an alias + $-m expands to all arguments up to 'm' passed to an alias + $~ expands to the last argument passed to an alias + +These variables are set and updated dynamically by the client. The case of +$A .. $Z is important. + + Variable Description + $, last person who sent you a MSG + $. last person to whom you sent a MSG + $: last person to join a channel you are on + $; last person to send a public message to a channel you are on + $A text of your AWAY message, if any + $B body of last MSG you sent + $C current channel + $D last person that NOTIFY detected a signon for + $E idle time + $F time client was started, $time() format + $H current server numeric being processed + $I channel you were last INVITEd to + $J client version text string + $K current value of CMDCHARS + $L current contents of the input line + $M modes of current channel, if any + $N current nickname + $O value of STATUS_OPER if you are an irc operator + $P if you are a channel operator in $C, expands to a '@' + $Q nickname of whomever you are QUERYing + $R version of current server + $S current server name + $T target of current input (channel or nick of query) + $U value of cutbuffer + $V client release date (format YYYYMMDD) + $W current working directory + $X your /userhost $N address (user@host) + $Y value of REALNAME + $Z time of day (hh:mm, can be changed with /SET timestamp_format) + $$ a literal '$' + + $versiontim prints time of the irssi version in HHMM format + $sysname system name (eg. Linux) + $sysrelease system release (eg. 2.2.18) + $sysarch system architecture (eg. i686) + $topic channel topic + $usermode user mode + $cumode own channel user mode + $cumode_space like $cumode, but gives space if there's no mode. + $tag server tag + $chatnet chat network of server + $winref window reference number + $winname window name + +For example, assume you have the following alias: + + alias blah msg $D Hi there! + +If /blah is passed any arguments, they will automatically be appended to the +MSG text. For example: + + /blah oops /* command as entered */ + "Hi there! oops" /* text sent to $D */ + +Another useful form is ${}. In general, variables can be embedded inside +strings without problems, assuming the surrounding text could not be +misinterpreted as part of the variable name. This form guarantees that +surrounding text will not affect the expression's return value. + + /eval echo foo$Nfoo /* breaks, looks for $nfoo */ + /eval echo foo${N}foo /* ${N} returns current nickname */ + fooYourNickfoo /* returned by above command */ + diff --git a/apps/irssi/docs/startup-HOWTO.html b/apps/irssi/docs/startup-HOWTO.html index e0af3492..dd8f2148 100644 --- a/apps/irssi/docs/startup-HOWTO.html +++ b/apps/irssi/docs/startup-HOWTO.html @@ -34,7 +34,8 @@
  • How can I save all texts in a window to file?
  • Logging
  • -
  • Irssi's settings
  • +
  • Proxies and IRC bouncers
  • +
  • Irssi's settings
  • 1. For all the lazy people

    @@ -83,19 +84,29 @@ to bot after joined to efnet/#irssi:

          /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
     
    +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-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
     
    @@ -117,10 +128,16 @@ 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 +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.

    @@ -216,7 +233,7 @@ same network if the -auto server fails.

    And finally channels:

    -     /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
     
    @@ -478,13 +495,99 @@ 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
    +     /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
     

    For logging only some specific channels or nicks, see /HELP log

    -

    9. Irssi's settings

    +

    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 @@ -603,7 +706,11 @@ of them you might want to change (the default value is shown):

    /SET show_nickmode ON
    Show the nick's mode before nick in channels, ie. ops have - <@nick>, voices <+nick> and others < nick>
    + <@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 @@ -632,7 +739,7 @@ of them you might want to change (the default value is shown):

    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

    diff --git a/apps/irssi/docs/startup-HOWTO.txt b/apps/irssi/docs/startup-HOWTO.txt new file mode 100644 index 00000000..cf989546 --- /dev/null +++ b/apps/irssi/docs/startup-HOWTO.txt @@ -0,0 +1,598 @@ +Startup HOWTO + + To new Irssi users (not to new IRC users ..) + + Copyright (c) 2000-2001 by Timo Sirainen + + Index with some FAQ questions that are answered in the chapter: + 1. For all the lazy people + 2. Basic user interface usage + 3. Server and channel automation + + how do I automatically connect to servers at startup? + + how do I automatically join to channels at startup? + 4. Setting up windows and automatically restoring them at startup + 5. Status and msgs windows & message levels + + I want /WHOIS to print reply to current window + + I want all messages to go to one window, not create new + windows + 6. How support for multiple servers works in irssi + + I connected to some server that doesn't respond and now irssi + keeps trying to reconnect to it again and again, how can I + stop it?? + + I want to have own status and/or msgs window for each servers + 7. /LASTLOG and jumping around in scrollback + + How can I save all texts in a window to file? + 8. Logging + 9. Proxies and IRC bouncers + 10. Irssi's settings + + 1. For all the lazy people + + These settings should give you pretty good defaults (the ones I use): + + I don't like automatic query windows, I don't like status window, I do + like msgs window where all messages go: + /SET autocreate_own_query OFF + /SET autocreate_query_level DCCMSGS + /SET use_status_window OFF + /SET use_msgs_window ON + + Disable automatic window closing when /PARTing channel or /UNQUERYing + query: + /SET autoclose_windows OFF + /SET reuse_unused_windows ON + + And example how to add servers: + + (openprojects network, identify with nickserv and wait for 2 seconds + before joining channels) + /IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait -opn 2000" opn + + Then add some servers to different networks (ircnet is already set up + for them), irc.kpnqwest.fi is used by default for IRCNet but if it + fails, irc.funet.fi is tried next: + /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667 + /SERVER ADD -ircnet ircnet irc.funet.fi 6667 + /SERVER ADD -auto -ircnet efnet efnet.cs.hut.fi 6667 + + Automatically join to channels after connected to server, send op + request to bot after joined to efnet/#irssi: + /CHANNEL ADD -auto #irssi ircnet + /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" + #irssi efnet + + If you want lines containing your nick to hilight: + /HILIGHT nick + + 2. Basic user interface usage + + Windows can be scrolled up/down with PgUp and PgDown keys. If they + don't work for you, use Meta-p and Meta-n keys. For jumping to + beginning or end of the buffer, use /SB HOME and /SB END commands. + + By default, irssi uses "hidden windows" for everything. Hidden window + is created every time you /JOIN a channel or /QUERY someone. There's + several ways you can change between these windows: + Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10 + Meta-q .. Meta-o - Jump directly between windows 11-19 + /WINDOW - 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 [|] - Make the split window hidden window + /WINDOW SHOW | - Make the hidden window a split window + + /WINDOW SHRINK [] - Shrink the split window + /WINDOW GROW [] - 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 | - 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//.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 + /SET 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 + /SET irssiproxy_ports = ... (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 + + 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 " text" + and other channels as " 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: + + /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. diff --git a/apps/irssi/irssi-config.in b/apps/irssi/irssi-config.in index 0a410425..3076247c 100644 --- a/apps/irssi/irssi-config.in +++ b/apps/irssi/irssi-config.in @@ -3,4 +3,4 @@ PERL_LDFLAGS="@PERL_LDFLAGS@" COMMON_LIBS="@COMMON_LIBS@" CHAT_MODULES="@CHAT_MODULES@" -silc_MODULES="@silc_MODULES@" +irc_MODULES="@irc_MODULES@" diff --git a/apps/irssi/irssi-icon.png b/apps/irssi/irssi-icon.png new file mode 100644 index 00000000..dd5efd20 Binary files /dev/null and b/apps/irssi/irssi-icon.png differ diff --git a/apps/irssi/irssi.spec.in b/apps/irssi/irssi.spec.in index 40f1b21e..c15069cf 100644 --- a/apps/irssi/irssi.spec.in +++ b/apps/irssi/irssi.spec.in @@ -4,213 +4,70 @@ Version: @VERSION@ Release: 1 Vendor: Timo Sirainen 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 . -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 -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 . 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 . -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 -All below listed persons can be reached on @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 - 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 [ON/OFF] - same as /SET 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 ) +* Fri Aug 17 2001 - Joose Vettenranta + Created new spec file from spec file founded in irssi-0.7.98.3 diff --git a/apps/irssi/scripts/.cvsignore b/apps/irssi/scripts/.cvsignore new file mode 100644 index 00000000..282522db --- /dev/null +++ b/apps/irssi/scripts/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/apps/irssi/scripts/Makefile.am b/apps/irssi/scripts/Makefile.am new file mode 100644 index 00000000..7a5a7ab4 --- /dev/null +++ b/apps/irssi/scripts/Makefile.am @@ -0,0 +1,12 @@ +scriptdir = $(datadir)/irssi/scripts + +script_DATA = \ + autoop.pl \ + autorejoin.pl \ + clones.pl \ + hello.pl \ + privmsg.pl \ + realname.pl \ + quitmsg.pl + +EXTRA_DIST = $(script_DATA) diff --git a/apps/irssi/scripts/autoop.pl b/apps/irssi/scripts/autoop.pl new file mode 100644 index 00000000..413f5e17 --- /dev/null +++ b/apps/irssi/scripts/autoop.pl @@ -0,0 +1,80 @@ +# /AUTOOP <*|#channel> [] + +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> []"); + 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 = ""; + delete $opnicks{$channel}; + } else { + $opnicks{$channel} = $masks; + } + if ($channel eq "*") { + Irssi::print("Now auto-opping in all channels: $masks"); + } else { + Irssi::print("$channel: Now auto-opping: $masks"); + } +} + +sub autoop { + my ($channel, $masks, @nicks) = @_; + my ($server, $nickrec); + + $server = $channel->{server}; + foreach $nickrec (@nicks) { + my $nick = $nickrec->{nick}; + my $host = $nickrec->{host}; + + if (!$temp_opped{$nick} && + $server->masks_match($masks, $nick, $host)) { + $channel->command("/op $nick"); + $temp_opped{$nick} = 1; + } + } +} + +sub event_massjoin { + my ($channel, $nicks_list) = @_; + my @nicks = @{$nicks_list}; + + return if (!$channel->{chanop}); + + undef %temp_opped; + + # channel specific + my $masks = $opnicks{$channel->{name}}; + autoop($channel, $masks, @nicks) if ($masks); + + # for all channels + $masks = $opnicks{"*"}; + autoop($channel, $masks, @nicks) if ($masks); +} + +Irssi::command_bind('autoop', 'cmd_autoop'); +Irssi::signal_add_last('massjoin', 'event_massjoin'); diff --git a/apps/irssi/scripts/autorejoin.pl b/apps/irssi/scripts/autorejoin.pl new file mode 100644 index 00000000..bbbb0ed8 --- /dev/null +++ b/apps/irssi/scripts/autorejoin.pl @@ -0,0 +1,26 @@ +# automatically rejoin to channel after kicked + +# NOTE: I personally don't like this feature, in most channels I'm in it +# will just result as ban. You've probably misunderstood the idea of /KICK +# if you kick/get kicked all the time "just for fun" ... + +use Irssi; +use Irssi::Irc; +use strict; + +sub event_rejoin_kick { + my ($server, $data) = @_; + my ($channel, $nick) = split(/ +/, $data); + + return if ($server->{nick} ne $nick); + + # check if channel has password + my $chanrec = $server->channel_find($channel); + my $password = $chanrec->{key} if ($chanrec); + + # We have to use send_raw() because the channel record still + # exists and irssi won't even try to join to it with command() + $server->send_raw("JOIN $channel $password"); +} + +Irssi::signal_add('event kick', 'event_rejoin_kick'); diff --git a/apps/irssi/scripts/clones.pl b/apps/irssi/scripts/clones.pl new file mode 100644 index 00000000..59c22c3f --- /dev/null +++ b/apps/irssi/scripts/clones.pl @@ -0,0 +1,43 @@ +# /CLONES - Display clones in the active channel +# Modified by Roi Dayan. dejavo@punkass.com + +use strict; + +sub cmd_clones { + my ($data, $server, $channel) = @_; + my $min_show_count = ($data =~ /^[0-9]+$/) ? $data : 2; + + if (!$channel || $channel->{type} ne "CHANNEL") { + Irssi::print("No active channel in window"); + return; + } + + my %hostnames = {}; + my %hostnicks = {}; + my @hosttmp = {}; + foreach my $nick ($channel->nicks()) { + my @hosttmp = split(/\@/,$nick->{host}); + $hostnames{$hosttmp[1]}++; + $hostnicks{$hosttmp[1]} = $hostnicks{$hosttmp[1]}.$hostnames{$hosttmp[1]}.". ".$nick->{nick}."!".$nick->{host}."\n"; + $hostnicks{$hosttmp[1]} =~ s/^,//; +# $hostnicks{$hosttmp[1]} =~ s/\n$//; + } + + foreach my $nick (keys %hostnicks) { + $hostnicks{$nick} =~ s/\n$//; + } + + my $count = 0; + foreach my $host (keys %hostnames) { + my $clones = $hostnames{$host}; + if ($clones >= $min_show_count) { + $channel->print("Clones:") if ($count == 0); + $channel->print("$host: $clones $hostnicks{$host}"); + $count++; + } + } + + $channel->print("No clones in channel") if ($count == 0); +} + +Irssi::command_bind('clones', 'cmd_clones'); diff --git a/apps/irssi/scripts/hello.pl b/apps/irssi/scripts/hello.pl new file mode 100644 index 00000000..82adfe22 --- /dev/null +++ b/apps/irssi/scripts/hello.pl @@ -0,0 +1,12 @@ +# "Hello, world!" script :) /hello sends "Hello, world!" to + +use Irssi; +use strict; + +sub cmd_hello { + my ($data, $server, $channel) = @_; + + $server->command("/msg $data Hello, world!"); +} + +Irssi::command_bind('hello', 'cmd_hello'); diff --git a/apps/irssi/scripts/mlock.pl b/apps/irssi/scripts/mlock.pl new file mode 100644 index 00000000..35ad7856 --- /dev/null +++ b/apps/irssi/scripts/mlock.pl @@ -0,0 +1,119 @@ +# /MLOCK +# +# Locks the channel mode to , if someone else tries to change the mode +# Irssi will automatically change it back. +k and +l are a bit special since +# they require the parameter. If you omit the parameter, like setting the +# mode to "+ntlk", Irssi will allow all +k and +l (or -lk) mode changes. + +use Irssi; +use Irssi::Irc; +use strict; + +my %keep_channels; + +sub cmd_mlock { + my ($data, $server) = @_; + my ($channel, $mode) = split(/ /, $data, 2); + + $keep_channels{$channel} = $mode; + mlock_check_mode($server, $channel); +} + +sub mlock_check_mode { + my ($server, $channame) = @_; + + my $channel = $server->channel_find($channame); + return if (!$channel || !$channel->{chanop}); + + my $keep_mode = $keep_channels{$channame}; + return if (!$keep_mode); + + # old channel mode + my ($oldmode, $oldkey, $oldlimit); + $oldmode = $channel->{mode}; + $oldmode =~ s/^([^ ]*).*/\1/; + $oldkey = $channel->{key}; + $oldlimit = $channel->{limit}; + + # get the new channel key/limit + my (@newmodes, $newkey, $limit); + @newmodes = split(/ /, $keep_mode); $keep_mode = $newmodes[0]; + if ($keep_mode =~ /k/) { + if ($keep_mode =~ /k.*l/) { + $newkey = $newmodes[1]; + $limit = $newmodes[2]; + } elsif ($keep_mode =~ /l.*k/) { + $limit = $newmodes[1]; + $newkey = $newmodes[2]; + } else { + $newkey = $newmodes[1]; + } + } elsif ($keep_mode =~ /l/) { + $limit = $newmodes[1]; + } + + # check the differences + my %allmodes; + $keep_mode =~ s/^\+//; + for (my $n = 0; $n < length($keep_mode); $n++) { + my $modechar = substr($keep_mode, $n, 1); + $allmodes{$modechar} = '+'; + } + + for (my $n = 0; $n < length($oldmode); $n++) { + my $modechar = substr($oldmode, $n, 1); + + if ($allmodes{$modechar} eq '+') { + next if (($modechar eq "k" && $newkey ne $oldkey) || + ($modechar eq "l" && $limit != $oldlimit)); + delete $allmodes{$modechar}; + } else { + $allmodes{$modechar} = '-'; + } + } + + # create the mode change string + my ($modecmd, $extracmd); + foreach my $mode (keys %allmodes) { + Irssi::print("key = '$mode':".$allmodes{$mode}); + if ($mode eq "k") { + if ($allmodes{$mode} eq '+') { + next if ($newkey eq ""); + if ($oldkey ne "") { + # we need to get rid of old key too + $modecmd .= "-k"; + $extracmd .= " $oldkey"; + } + $extracmd .= " $newkey"; + } else { + $extracmd .= " $oldkey"; + } + } + if ($mode eq "l" && $allmodes{$mode} eq '+') { + next if ($limit <= 0); + $extracmd .= " $limit"; + } + $modecmd .= $allmodes{$mode}.$mode; + } + + if ($modecmd ne "") { + $channel->{server}->command("/mode $channame $modecmd$extracmd"); + } +} + +sub mlock_mode_changed { + my ($server, $data) = @_; + my ($channel, $mode) = split(/ /, $data, 2); + + mlock_check_mode($server, $channel); +} + +sub mlock_synced { + my $channel = $_[0]; + + mlock_check_mode($channel->{server}, $channel->{name}); +} + +Irssi::command_bind('mlock', 'cmd_mlock'); +Irssi::signal_add_last("event mode", "mlock_mode_changed"); +Irssi::signal_add("channel synced", "mlock_synced"); diff --git a/apps/irssi/scripts/privmsg.pl b/apps/irssi/scripts/privmsg.pl new file mode 100644 index 00000000..b1d119cb --- /dev/null +++ b/apps/irssi/scripts/privmsg.pl @@ -0,0 +1,18 @@ +# listen PRIVMSGs - send a notice to yourself when your nick is meantioned + +use Irssi; +use strict; + +sub event_privmsg { + my ($server, $data, $nick, $address) = @_; + my ($target, $text) = $data =~ /^(\S*)\s:(.*)/; + + return if (!$server->ischannel($target)); + + my $mynick = $server->{nick}; + return if ($text !~ /\b$mynick\b/); + + $server->command("/notice $mynick In channel $target, $nick!$address said: $text"); +} + +Irssi::signal_add("event privmsg", "event_privmsg"); diff --git a/apps/irssi/scripts/quitmsg.pl b/apps/irssi/scripts/quitmsg.pl new file mode 100644 index 00000000..981b15a0 --- /dev/null +++ b/apps/irssi/scripts/quitmsg.pl @@ -0,0 +1,35 @@ +# If quit message isn't given, quit with a random message +# read from ~/.irssi/irssi.quit + +use Irssi; +use Irssi::Irc; +use strict; + +my $quitfile = glob "~/.irssi/irssi.quit"; + +sub cmd_quit { + my ($data, $server, $channel) = @_; + return if ($data ne ""); + + open (f, $quitfile) || return; + my $lines = 0; while() { $lines++; }; + + my $line = int(rand($lines))+1; + + my $quitmsg; + seek(f, 0, 0); $. = 0; + while() { + next if ($. != $line); + + chomp; + $quitmsg = $_; + last; + } + close(f); + + foreach my $server (Irssi::servers) { + $server->command("/disconnect ".$server->{tag}." $quitmsg"); + } +} + +Irssi::command_bind('quit', 'cmd_quit'); diff --git a/apps/irssi/scripts/realname.pl b/apps/irssi/scripts/realname.pl new file mode 100644 index 00000000..ccb1c6b1 --- /dev/null +++ b/apps/irssi/scripts/realname.pl @@ -0,0 +1,34 @@ +# /RN - display real name of nick + +use Irssi; +use Irssi::Irc; +use strict; + +sub cmd_realname { + my ($data, $server, $channel) = @_; + + $server->send_raw("WHOIS :$data"); + + # ignore all whois replies except "No such nick" or the + # first line of the WHOIS reply + $server->redirect_event($data, 2, + "event 318", "event empty", -1, + "event 402", "event 402", -1, + "event 401", "event 401", 1, + "event 311", "redir whois", 1, + "event 301", "event empty", 1, + "event 312", "event empty", 1, + "event 313", "event empty", 1, + "event 317", "event empty", 1, + "event 319", "event empty", 1); +} + +sub event_rn_whois { + my ($num, $nick, $user, $host, $empty, $realname) = split(/ +/, $_[1], 6); + $realname =~ s/^://; + + Irssi::print("%_$nick%_ is $realname"); +} + +Irssi::command_bind('rn', 'cmd_realname'); +Irssi::signal_add('redir whois', 'event_rn_whois'); diff --git a/apps/irssi/silc.conf b/apps/irssi/silc.conf new file mode 100644 index 00000000..e99332a6 --- /dev/null +++ b/apps/irssi/silc.conf @@ -0,0 +1,173 @@ +servers = ( + { address = "silc.silcnet.org"; chatnet = SILCNet; port = 706; }, + { address = "silc.ytti.fi"; chatnet = SILCNet; port = 706; }, + { address = "silc.peelo.com"; chatnet = SILCNet; port = 706; }, + { address = "silc.silcnet.org"; chatnet = SILCNet; port = 707; } +); + +chatnets = { + SILCNet = { type = "SILC"; }; +}; + +channels = ( + { name = "#silc"; chatnet = silcnet; autojoin = No; } +); + +aliases = { + JOIN = "join -window"; + QUERY = "query -window"; + LEAVE = "part"; + BYE = "quit"; + EXIT = "quit"; + SIGNOFF = "quit"; + DESCRIBE = "action"; + DATE = "time"; + HOST = "userhost"; + LAST = "lastlog"; + SAY = "msg *"; + WHO = "users *"; + WI = "whois"; + WII = "whois $0 $0"; + WW = "whowas"; + W = "who"; + N = "names"; + M = "msg"; + T = "topic"; + C = "clear"; + CL = "clear"; + K = "kick"; + KB = "kickban"; + KN = "knockout"; + BANS = "ban"; + B = "ban"; + MUB = "unban *"; + UB = "unban"; + IG = "ignore"; + UNIG = "unignore"; + SB = "scrollback"; + WC = "window close"; + WN = "window new hide"; + GOTO = "sb goto"; + CHAT = "dcc chat"; + ADMIN = "info"; + RUN = "SCRIPT LOAD"; + UPTIME = "eval exec - expr `date +%s` - \\$F | awk '{print \"Irssi uptime: \"int(\\\\\\$1/3600/24)\"d \"int(\\\\\\$1/3600%24)\"h \"int(\\\\\\$1/60%60)\"m \"int(\\\\\\$1%60)\"s\" }'"; + CALC = "exec - if which bc &>/dev/null\\; then echo '$*' | bc | awk '{print \"$*=\"$$1}'\\; else echo bc was not found\\; fi"; +}; + +statusbar = { + # formats: + # when using {templates}, the template is shown only if it's argument isn't + # empty unless no argument is given. for example {sb} is printed always, + # but {sb $T} is printed only if $T isn't empty. + + items = { + # start/end text in statusbars + barstart = "{sbstart}"; + barend = "{sbend}"; + + # treated "normally", you could change the time/user name to whatever + time = "{sb $Z}"; + user = "{sb $cumode$N{sbmode $usermode}{sbaway $A}}"; + topic = " $topic"; + + # treated specially .. window is printed with non-empty windows, + # window_empty is printed with empty windows + window = "{sb $winref:$T{sbmode $M}}"; + window_empty = "{sb $winref{sbservertag $tag}}"; + prompt = "{prompt $[.15]T}"; + prompt_empty = "{prompt $winname}"; + + # all of these treated specially, they're only displayed when needed + lag = "{sb Lag: $0-}"; + act = "{sb Act: $0-}"; + more = "-- more --"; + }; + + # there's two type of statusbars. root statusbars are either at the top + # of the screen or at the bottom of the screen. window statusbars are at + # the top/bottom of each split window in screen. + default = { + # the "default statusbar" to be displayed at the bottom of the window. + # contains all the normal items. + window = { + # window, root + type = "window"; + # top, bottom + placement = "bottom"; + # number + position = "1"; + # active, inactive, always, never (disables the statusbar) + visible = "active"; + + # list of items in statusbar in the display order + items = { + barstart = { priority = "100"; }; + time = { }; + user = { }; + window = { }; + window_empty = { }; + lag = { priority = "-1"; }; + act = { priority = "10"; }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + + # statusbar to use in inactive split windows + window_inact = { + type = "window"; + placement = "bottom"; + position = "1"; + visible = "inactive"; + items = { + barstart = { priority = "100"; }; + window = { }; + window_empty = { }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + + # we treat input line as yet another statusbar :) It's possible to + # add other items before or after the input line item. + prompt = { + type = "root"; + placement = "bottom"; + # we want to be at the bottom always + position = "100"; + visible = "always"; + items = { + prompt = { priority = "-1"; }; + prompt_empty = { priority = "-1"; }; + # treated specially, this is the real input line. + input = { priority = "10"; }; + }; + }; + + # topicbar + topic = { + type = "root"; + placement = "top"; + position = "1"; + visible = "always"; + items = { + barstart = { priority = "100"; }; + topic = { }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + }; +}; + +settings = { + "fe-common/core" = { + autocreate_own_query = "no"; + use_status_window = "no"; + autoclose_windows = "no"; + use_msgs_window = "no"; + autocreate_windows = "no"; + autocreate_query_level = "none"; + }; + "fe-text" = { indent = "8"; }; +}; diff --git a/apps/irssi/src/Makefile.am b/apps/irssi/src/Makefile.am index d8fc1899..a0dc6af1 100644 --- a/apps/irssi/src/Makefile.am +++ b/apps/irssi/src/Makefile.am @@ -2,7 +2,15 @@ if BUILD_TEXTUI TEXTUI=fe-text endif +if BUILD_IRSSIBOT +BOTUI=# +endif + +if HAVE_PERL +PERLDIR=perl +endif + noinst_HEADERS = \ common.h -SUBDIRS = lib-popt lib-config core silc fe-common $(TEXTUI) +SUBDIRS = lib-popt lib-config core silc fe-common $(PERLDIR) $(TEXTUI) $(BOTUI) diff --git a/apps/irssi/src/common.h b/apps/irssi/src/common.h index c79c5d9b..a4240be5 100644 --- a/apps/irssi/src/common.h +++ b/apps/irssi/src/common.h @@ -4,6 +4,9 @@ #define IRSSI_AUTHOR "Timo Sirainen " #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 @@ -43,18 +46,7 @@ # include #endif -#include "core/memdebug.h" - -#define g_free_not_null(a) \ - G_STMT_START { \ - if (a) g_free(a); \ - } G_STMT_END - -#define g_free_and_null(a) \ - G_STMT_START { \ - if (a) { g_free(a); (a) = NULL; } \ - } G_STMT_END - +/* input functions */ #define G_INPUT_READ (1 << 0) #define G_INPUT_WRITE (1 << 1) @@ -65,8 +57,38 @@ int g_input_add(GIOChannel *source, int condition, int g_input_add_full(GIOChannel *source, int priority, int condition, GInputFunction function, void *data); +/* return full path for ~/.irssi */ +const char *get_irssi_dir(void); +/* return full path for ~/.irssi/config */ +const char *get_irssi_config(void); + +/* max. size for %d */ #define MAX_INT_STRLEN ((sizeof(int) * CHAR_BIT + 2) / 3 + 1) +#define g_free_not_null(a) g_free(a) + +#define g_free_and_null(a) \ + G_STMT_START { \ + if (a) { g_free(a); (a) = NULL; } \ + } G_STMT_END + +/* ctype.h isn't safe with chars, use our own instead */ +#define i_toupper(x) toupper((int) (unsigned char) (x)) +#define i_tolower(x) tolower((int) (unsigned char) (x)) +#define i_isalnum(x) isalnum((int) (unsigned char) (x)) +#define i_isalpha(x) isalpha((int) (unsigned char) (x)) +#define i_isascii(x) isascii((int) (unsigned char) (x)) +#define i_isblank(x) isblank((int) (unsigned char) (x)) +#define i_iscntrl(x) iscntrl((int) (unsigned char) (x)) +#define i_isdigit(x) isdigit((int) (unsigned char) (x)) +#define i_isgraph(x) isgraph((int) (unsigned char) (x)) +#define i_islower(x) islower((int) (unsigned char) (x)) +#define i_isprint(x) isprint((int) (unsigned char) (x)) +#define i_ispunct(x) ispunct((int) (unsigned char) (x)) +#define i_isspace(x) isspace((int) (unsigned char) (x)) +#define i_isupper(x) isupper((int) (unsigned char) (x)) +#define i_isxdigit(x) isxdigit((int) (unsigned char) (x)) + typedef struct _IPADDR IPADDR; typedef struct _CONFIG_REC CONFIG_REC; typedef struct _CONFIG_NODE CONFIG_NODE; @@ -86,4 +108,6 @@ typedef struct _SERVER_CONNECT_REC SERVER_CONNECT_REC; typedef struct _SERVER_SETUP_REC SERVER_SETUP_REC; typedef struct _CHANNEL_SETUP_REC CHANNEL_SETUP_REC; +typedef struct _WINDOW_REC WINDOW_REC; + #endif diff --git a/apps/irssi/src/core/.cvsignore b/apps/irssi/src/core/.cvsignore new file mode 100644 index 00000000..8553e9e9 --- /dev/null +++ b/apps/irssi/src/core/.cvsignore @@ -0,0 +1,8 @@ +*.la +*.lo +*.o +.deps +.libs +Makefile +Makefile.in +so_locations diff --git a/apps/irssi/src/core/Makefile.am b/apps/irssi/src/core/Makefile.am index 1443bc91..655d291a 100644 --- a/apps/irssi/src/core/Makefile.am +++ b/apps/irssi/src/core/Makefile.am @@ -3,17 +3,11 @@ noinst_LIBRARIES = libcore.a include $(top_srcdir)/Makefile.defines.in INCLUDES = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core \ $(GLIB_CFLAGS) \ -DSYSCONFDIR=\""$(silc_etcdir)"\" \ - -DMODULEDIR=\""$(silc_modulesdir)"\" \ - -I$(top_srcdir)/src \ - -I$(top_srcdir)/src/core - -if BUILD_MEMDEBUG -memdebug_src=memdebug.c -else -memdebug_src= -endif + -DMODULEDIR=\""$(silc_modulesdir)"\" libcore_a_SOURCES = \ args.c \ @@ -29,10 +23,11 @@ libcore_a_SOURCES = \ levels.c \ line-split.c \ log.c \ + log-away.c \ masks.c \ - $(memdebug_src) \ misc.c \ modules.c \ + modules-load.c \ net-disconnect.c \ net-nonblock.c \ net-sendbuffer.c \ @@ -44,8 +39,8 @@ libcore_a_SOURCES = \ rawlog.c \ servers.c \ servers-reconnect.c \ - servers-redirect.c \ servers-setup.c \ + session.c \ settings.c \ signals.c \ special-vars.c \ @@ -75,10 +70,10 @@ noinst_HEADERS = \ line-split.h \ log.h \ masks.h \ - memdebug.h \ misc.h \ module.h \ modules.h \ + modules-load.h \ net-disconnect.h \ net-nonblock.h \ net-sendbuffer.h \ @@ -91,14 +86,11 @@ noinst_HEADERS = \ rawlog.h \ servers.h \ servers-reconnect.h \ - servers-redirect.h \ servers-setup.h \ + session.h \ settings.h \ signals.h \ special-vars.h \ window-item-def.h \ write-buffer.h \ $(structure_headers) - -EXTRA_DIST = \ - memdebug.c diff --git a/apps/irssi/src/core/args.c b/apps/irssi/src/core/args.c index 093a8d56..ab26ee14 100644 --- a/apps/irssi/src/core/args.c +++ b/apps/irssi/src/core/args.c @@ -61,4 +61,6 @@ void args_execute(int argc, char *argv[]) g_array_free(iopt_tables, TRUE); iopt_tables = NULL; + + poptFreeContext(con); } diff --git a/apps/irssi/src/core/channel-rec.h b/apps/irssi/src/core/channel-rec.h index 2ff6a536..f3ec5f8d 100644 --- a/apps/irssi/src/core/channel-rec.h +++ b/apps/irssi/src/core/channel-rec.h @@ -22,6 +22,7 @@ unsigned int synced:1; /* Channel synced - all queries done */ unsigned int joined:1; /* Have we even received JOIN event for this channel? */ unsigned int left:1; /* You just left the channel */ unsigned int kicked:1; /* You just got kicked */ +unsigned int session_rejoin:1; /* This channel was joined with /UPGRADE */ unsigned int destroying:1; /* Return the information needed to call SERVER_REC->channels_join() for diff --git a/apps/irssi/src/core/channels-setup.c b/apps/irssi/src/core/channels-setup.c index a53bd3f3..ac9b1c23 100644 --- a/apps/irssi/src/core/channels-setup.c +++ b/apps/irssi/src/core/channels-setup.c @@ -118,13 +118,6 @@ static CHANNEL_SETUP_REC *channel_setup_read(CONFIG_NODE *node) channel = config_node_get_str(node, "name", NULL); chatnet = config_node_get_str(node, "chatnet", NULL); - if (chatnet == NULL) /* FIXME: remove this after .98... */ { - chatnet = g_strdup(config_node_get_str(node, "ircnet", NULL)); - if (chatnet != NULL) { - iconfig_node_set_str(node, "chatnet", chatnet); - iconfig_node_set_str(node, "ircnet", NULL); - } - } chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet); if (channel == NULL || chatnetrec == NULL) { @@ -158,7 +151,8 @@ static void channels_read_config(void) /* Read channels */ node = iconfig_node_traverse("channels", FALSE); if (node != NULL) { - for (tmp = node->value; tmp != NULL; tmp = tmp->next) + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) channel_setup_read(tmp->data); } } diff --git a/apps/irssi/src/core/channels.c b/apps/irssi/src/core/channels.c index f3d3a7a4..57288f7e 100644 --- a/apps/irssi/src/core/channels.c +++ b/apps/irssi/src/core/channels.c @@ -48,6 +48,7 @@ void channel_init(CHANNEL_REC *channel, int automatic) MODULE_DATA_INIT(channel); channel->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL"); + channel->destroy = (void (*) (WI_ITEM_REC *)) channel_destroy; channel->mode = g_strdup(""); channel->createtime = time(NULL); channel->get_join_data = get_join_data; @@ -74,6 +75,8 @@ void channel_destroy(CHANNEL_REC *channel) g_free_not_null(channel->key); g_free(channel->mode); g_free(channel->name); + + channel->type = 0; g_free(channel); } @@ -113,17 +116,48 @@ CHANNEL_REC *channel_find(SERVER_REC *server, const char *name) (void *) name); } +static CHANNEL_REC *channel_find_servers(GSList *servers, const char *name) +{ + return gslist_foreach_find(servers, + (FOREACH_FIND_FUNC) channel_find_server, + (void *) name); +} + +static GSList *servers_find_chatnet_except(SERVER_REC *server) +{ + GSList *tmp, *list; + + list = NULL; + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + SERVER_REC *rec = tmp->data; + + if (server != rec && rec->connrec->chatnet != NULL && + strcmp(server->connrec->chatnet, + rec->connrec->chatnet) == 0) { + /* chatnets match */ + list = g_slist_append(list, rec); + } + } + + return list; +} + /* connected to server, autojoin to channels. */ static void event_connected(SERVER_REC *server) { GString *chans; - GSList *tmp; + GSList *tmp, *chatnet_servers; g_return_if_fail(SERVER(server)); - if (server->connrec->reconnection) + if (server->connrec->reconnection || + server->connrec->no_autojoin_channels) return; + /* get list of servers in same chat network */ + chatnet_servers = server->connrec->chatnet == NULL ? NULL: + servers_find_chatnet_except(server); + /* join to the channels marked with autojoin in setup */ chans = g_string_new(NULL); for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) { @@ -134,8 +168,12 @@ static void event_connected(SERVER_REC *server) server->connrec->chatnet)) continue; - g_string_sprintfa(chans, "%s,", rec->name); + /* check that we haven't already joined this channel in + same chat network connection.. */ + if (channel_find_servers(chatnet_servers, rec->name) == NULL) + g_string_sprintfa(chans, "%s,", rec->name); } + g_slist_free(chatnet_servers); if (chans->len > 0) { g_string_truncate(chans, chans->len-1); @@ -165,6 +203,9 @@ void channel_send_autocommands(CHANNEL_REC *channel) g_return_if_fail(IS_CHANNEL(channel)); + if (channel->session_rejoin) + return; + rec = channel_setup_find(channel->name, channel->server->connrec->chatnet); if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd) return; @@ -180,6 +221,9 @@ void channel_send_autocommands(CHANNEL_REC *channel) for (bot = bots; *bot != NULL; bot++) { const char *botnick = *bot; + if (*botnick == '\0') + continue; + nick = nicklist_find_mask(channel, channel->server->isnickflag(*botnick) ? botnick+1 : botnick); diff --git a/apps/irssi/src/core/chat-commands.c b/apps/irssi/src/core/chat-commands.c index 78a05a70..8b1b3738 100644 --- a/apps/irssi/src/core/chat-commands.c +++ b/apps/irssi/src/core/chat-commands.c @@ -32,8 +32,10 @@ #include "channels.h" #include "queries.h" #include "window-item-def.h" +#include "rawlog.h" -static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr) +static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, + char **rawlog_file) { CHAT_PROTOCOL_REC *proto; SERVER_CONNECT_REC *conn; @@ -73,7 +75,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr) if (proto->not_initialized) { /* trying to use protocol that isn't yet initialized */ signal_emit("chat protocol unknown", 1, proto->name); - server_connect_free(conn); + server_connect_unref(conn); cmd_params_free(free_arg); return NULL; } @@ -83,6 +85,14 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr) else if (g_hash_table_lookup(optlist, "4") != NULL) conn->family = AF_INET; + if (g_hash_table_lookup(optlist, "!") != NULL) + conn->no_autojoin_channels = TRUE; + + if (g_hash_table_lookup(optlist, "noproxy") != NULL) + g_free_and_null(conn->proxy); + + *rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog")); + host = g_hash_table_lookup(optlist, "host"); if (host != NULL && *host != '\0') { IPADDR ip4, ip6; @@ -100,10 +110,19 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr) static void cmd_connect(const char *data) { SERVER_CONNECT_REC *conn; + SERVER_REC *server; + char *rawlog_file; + + conn = get_server_connect(data, NULL, &rawlog_file); + if (conn != NULL) { + server = CHAT_PROTOCOL(conn)->server_connect(conn); + server_connect_unref(conn); + + if (server != NULL && rawlog_file != NULL) + rawlog_open(server->rawlog, rawlog_file); - conn = get_server_connect(data, NULL); - if (conn != NULL) - CHAT_PROTOCOL(conn)->server_connect(conn); + g_free(rawlog_file); + } } static RECONNECT_REC *find_reconnect_server(int chat_type, @@ -147,6 +166,7 @@ static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server) if (server != NULL) { oldconn = server->connrec; + server_connect_ref(oldconn); reconnect_save_status(conn, server); } else { /* maybe we can reconnect some server from @@ -156,7 +176,8 @@ static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server) if (recon == NULL) return; oldconn = recon->conn; - server_reconnect_destroy(recon, FALSE); + server_connect_ref(oldconn); + server_reconnect_destroy(recon); conn->away_reason = g_strdup(oldconn->away_reason); conn->channels = g_strdup(oldconn->channels); @@ -167,29 +188,46 @@ static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server) if (conn->chatnet == NULL && oldconn->chatnet != NULL) conn->chatnet = g_strdup(oldconn->chatnet); + server_connect_unref(oldconn); if (server != NULL) { signal_emit("command disconnect", 2, "* Changing server", server); - } else { - server_connect_free(oldconn); } } +static void cmd_server(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + command_runsub("server", data, server, item); +} + +static void sig_default_command_server(const char *data, SERVER_REC *server, + WI_ITEM_REC *item) +{ + signal_emit("command server connect", 3, data, server, item); +} + /* SYNTAX: SERVER [-4 | -6] [-ircnet ] [-host ] [+]
    | [ [ []]] */ -static void cmd_server(const char *data, SERVER_REC *server) +static void cmd_server_connect(const char *data, SERVER_REC *server) { SERVER_CONNECT_REC *conn; + char *rawlog_file; int plus_addr; g_return_if_fail(data != NULL); /* create connection record */ - conn = get_server_connect(data, &plus_addr); + conn = get_server_connect(data, &plus_addr, &rawlog_file); if (conn != NULL) { if (!plus_addr) update_reconnection(conn, server); - CHAT_PROTOCOL(conn)->server_connect(conn); + server = CHAT_PROTOCOL(conn)->server_connect(conn); + server_connect_unref(conn); + + if (server != NULL && rawlog_file != NULL) + rawlog_open(server->rawlog, rawlog_file); + + g_free(rawlog_file); } } @@ -247,36 +285,36 @@ static void cmd_join(const char *data, SERVER_REC *server) void *free_arg; g_return_if_fail(data != NULL); - if (!IS_SERVER(server) || !server->connected) - cmd_return_error(CMDERR_NOT_CONNECTED); if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, "join", &optlist, &channels)) return; + /* - */ + server = 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 = 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 [-] */ +/* SYNTAX: MSG [-] [-channel | -nick] */ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { GHashTable *optlist; char *target, *origtarget, *msg; void *free_arg; - int free_ret; + int free_ret, target_type; g_return_if_fail(data != NULL); @@ -297,13 +335,34 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) NULL, &free_ret, NULL, 0); if (target != NULL && *target == '\0') target = NULL; - } else if (strcmp(target, "*") == 0 && item != NULL) + } + + if (strcmp(target, "*") == 0) { + /* send to active channel/query */ + if (item == NULL) + cmd_param_error(CMDERR_NOT_JOINED); + + target_type = IS_CHANNEL(item) ? + SEND_TARGET_CHANNEL : SEND_TARGET_NICK; target = item->name; + } + else if (g_hash_table_lookup(optlist, "channel") != NULL) + target_type = SEND_TARGET_CHANNEL; + else if (g_hash_table_lookup(optlist, "nick") != NULL) + target_type = SEND_TARGET_NICK; + else { + /* Need to rely on server_ischannel(). If the protocol + doesn't really know if it's channel or nick based on the + name, it should just assume it's nick, because when typing + text to channels it's always sent with /MSG -channel. */ + target_type = server_ischannel(server, target) ? + SEND_TARGET_CHANNEL : SEND_TARGET_NICK; + } if (target != NULL) - server->send_message(server, target, msg); + server->send_message(server, target, msg, target_type); - signal_emit(target != NULL && server->ischannel(target) ? + signal_emit(target != NULL && target_type == SEND_TARGET_CHANNEL ? "message own_public" : "message own_private", 4, server, msg, target, origtarget); @@ -320,33 +379,40 @@ static void cmd_foreach(const char *data, SERVER_REC *server, /* SYNTAX: FOREACH SERVER */ 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 */ 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 */ static void cmd_foreach_query(const char *data) { - GSList *tmp; + GSList *list; - for (tmp = queries; tmp != NULL; tmp = tmp->next) { - QUERY_REC *rec = tmp->data; + list = g_slist_copy(queries); + while (list != NULL) { + QUERY_REC *rec = list->data; signal_emit("send command", 3, data, rec->server, rec); + list = g_slist_remove(list, list->data); } } @@ -355,6 +421,7 @@ void chat_commands_init(void) settings_add_str("misc", "quit_message", "leaving"); command_bind("server", NULL, (SIGNAL_FUNC) cmd_server); + command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect); command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect); command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect); command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit); @@ -365,13 +432,17 @@ void chat_commands_init(void) command_bind("foreach channel", NULL, (SIGNAL_FUNC) cmd_foreach_channel); command_bind("foreach query", NULL, (SIGNAL_FUNC) cmd_foreach_query); - command_set_options("connect", "4 6 +host"); + signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server); + + command_set_options("connect", "4 6 !! +host noproxy -rawlog"); command_set_options("join", "invite"); + command_set_options("msg", "channel nick"); } void chat_commands_deinit(void) { command_unbind("server", (SIGNAL_FUNC) cmd_server); + command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect); command_unbind("connect", (SIGNAL_FUNC) cmd_connect); command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect); command_unbind("quit", (SIGNAL_FUNC) cmd_quit); @@ -381,4 +452,6 @@ void chat_commands_deinit(void) command_unbind("foreach server", (SIGNAL_FUNC) cmd_foreach_server); command_unbind("foreach channel", (SIGNAL_FUNC) cmd_foreach_channel); command_unbind("foreach query", (SIGNAL_FUNC) cmd_foreach_query); + + signal_remove("default command server", (SIGNAL_FUNC) sig_default_command_server); } diff --git a/apps/irssi/src/core/chat-protocols.c b/apps/irssi/src/core/chat-protocols.c index ab7c09d4..2989598c 100644 --- a/apps/irssi/src/core/chat-protocols.c +++ b/apps/irssi/src/core/chat-protocols.c @@ -156,7 +156,13 @@ void chat_protocol_unregister(const char *name) g_return_if_fail(name != NULL); rec = chat_protocol_find(name); - if (rec != NULL) chat_protocol_destroy(rec); + if (rec != NULL) { + chat_protocol_destroy(rec); + + /* there might still be references to this chat protocol - + recreate it as a dummy protocol */ + chat_protocol_get_unknown(name); + } } /* Default chat protocol to use */ @@ -190,6 +196,10 @@ static SERVER_CONNECT_REC *create_server_connect(void) return g_new0(SERVER_CONNECT_REC, 1); } +static void destroy_server_connect(SERVER_CONNECT_REC *conn) +{ +} + /* Return "unknown chat protocol" record. Used when protocol name is specified but it isn't registered yet. */ CHAT_PROTOCOL_REC *chat_protocol_get_unknown(const char *name) @@ -209,6 +219,7 @@ CHAT_PROTOCOL_REC *chat_protocol_get_unknown(const char *name) rec->create_server_setup = create_server_setup; rec->create_channel_setup = create_channel_setup; rec->create_server_connect = create_server_connect; + rec->destroy_server_connect = destroy_server_connect; newrec = chat_protocol_register(rec); g_free(rec); diff --git a/apps/irssi/src/core/chat-protocols.h b/apps/irssi/src/core/chat-protocols.h index cc7eaadc..eeb0075f 100644 --- a/apps/irssi/src/core/chat-protocols.h +++ b/apps/irssi/src/core/chat-protocols.h @@ -5,6 +5,7 @@ typedef struct { int id; unsigned int not_initialized:1; + unsigned int case_insensitive:1; char *name; char *fullname; @@ -14,6 +15,7 @@ typedef struct { SERVER_SETUP_REC *(*create_server_setup) (void); CHANNEL_SETUP_REC *(*create_channel_setup) (void); SERVER_CONNECT_REC *(*create_server_connect) (void); + void (*destroy_server_connect) (SERVER_CONNECT_REC *); SERVER_REC *(*server_connect) (SERVER_CONNECT_REC *); CHANNEL_REC *(*channel_create) (SERVER_REC *, const char *, int); diff --git a/apps/irssi/src/core/chatnets.c b/apps/irssi/src/core/chatnets.c index a2cff529..8739ff7d 100644 --- a/apps/irssi/src/core/chatnets.c +++ b/apps/irssi/src/core/chatnets.c @@ -162,39 +162,30 @@ static void chatnet_read(CONFIG_NODE *node) static void read_chatnets(void) { CONFIG_NODE *node; + GSList *tmp; while (chatnets != NULL) chatnet_destroy(chatnets->data); node = iconfig_node_traverse("chatnets", FALSE); - if (node == NULL) { - /* FIXME: remove after .98 */ - node = iconfig_node_traverse("ircnets", FALSE); - if (node != NULL) { - /* very dirty method - doesn't update hashtables - but this will do temporarily.. */ - g_free(node->key); - node->key = g_strdup("chatnets"); - } + if (node != NULL) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) + chatnet_read(tmp->data); } - - if (node != NULL) - g_slist_foreach(node->value, (GFunc) chatnet_read, NULL); } void chatnets_init(void) { chatnets = NULL; - signal_add("event connected", (SIGNAL_FUNC) sig_connected); + signal_add_first("event connected", (SIGNAL_FUNC) sig_connected); signal_add("setup reread", (SIGNAL_FUNC) read_chatnets); signal_add_first("irssi init read settings", (SIGNAL_FUNC) read_chatnets); } void chatnets_deinit(void) { - while (chatnets != NULL) - chatnet_destroy(chatnets->data); module_uniq_destroy("CHATNET"); signal_remove("event connected", (SIGNAL_FUNC) sig_connected); diff --git a/apps/irssi/src/core/commands.c b/apps/irssi/src/core/commands.c index c4c88908..a96b5fa0 100644 --- a/apps/irssi/src/core/commands.c +++ b/apps/irssi/src/core/commands.c @@ -26,7 +26,6 @@ #include "window-item-def.h" #include "servers.h" -#include "servers-redirect.h" #include "channels.h" #include "lib-config/iconfig.h" @@ -84,7 +83,7 @@ static COMMAND_MODULE_REC *command_module_find_func(COMMAND_REC *rec, for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) { COMMAND_MODULE_REC *rec = tmp->data; - if (g_slist_find(rec->signals, func) != NULL) + if (g_slist_find(rec->signals, (void *) func) != NULL) return rec; } @@ -111,8 +110,8 @@ int command_have_sub(const char *command) return FALSE; } -static COMMAND_MODULE_REC *command_module_get(COMMAND_REC *rec, - const char *module) +static COMMAND_MODULE_REC * +command_module_get(COMMAND_REC *rec, const char *module, int protocol) { COMMAND_MODULE_REC *modrec; @@ -122,14 +121,18 @@ static COMMAND_MODULE_REC *command_module_get(COMMAND_REC *rec, if (modrec == NULL) { modrec = g_new0(COMMAND_MODULE_REC, 1); modrec->name = g_strdup(module); + modrec->protocol = -1; rec->modules = g_slist_append(rec->modules, modrec); } + if (protocol != -1) + modrec->protocol = protocol; + return modrec; } void command_bind_to(const char *module, int pos, const char *cmd, - const char *category, SIGNAL_FUNC func) + int protocol, const char *category, SIGNAL_FUNC func) { COMMAND_REC *rec; COMMAND_MODULE_REC *modrec; @@ -145,9 +148,9 @@ void command_bind_to(const char *module, int pos, const char *cmd, rec->category = category == NULL ? NULL : g_strdup(category); commands = g_slist_append(commands, rec); } - modrec = command_module_get(rec, module); + modrec = command_module_get(rec, module, protocol); - modrec->signals = g_slist_append(modrec->signals, func); + modrec->signals = g_slist_append(modrec->signals, (void *) func); if (func != NULL) { str = g_strconcat("command ", cmd, NULL); @@ -221,7 +224,10 @@ void command_unbind(const char *cmd, SIGNAL_FUNC func) rec = command_find(cmd); if (rec != NULL) { modrec = command_module_find_func(rec, func); - modrec->signals = g_slist_remove(modrec->signals, func); + g_return_if_fail(modrec != NULL); + + modrec->signals = + g_slist_remove(modrec->signals, (void *) func); if (modrec->signals == NULL) command_module_destroy(rec, modrec); } @@ -283,6 +289,8 @@ void command_runsub(const char *cmd, const char *data, g_return_if_fail(data != NULL); + while (*data == ' ') data++; + if (*data == '\0') { /* no subcommand given - list the subcommands */ signal_emit("list subcommands", 2, cmd); @@ -431,7 +439,7 @@ void command_set_options_module(const char *module, rec = command_find(cmd); g_return_if_fail(rec != NULL); - modrec = command_module_get(rec, module); + modrec = command_module_get(rec, module, -1); reload = modrec->options != NULL; if (reload) { @@ -550,15 +558,17 @@ static int get_cmd_options(char **data, int ignore_unknown, } (*data)++; - if (**data == '-' && isspace((*data)[1])) { + if (**data == '-' && i_isspace((*data)[1])) { /* -- option means end of options even if next word starts with - */ (*data)++; - while (isspace(**data)) (*data)++; + while (i_isspace(**data)) (*data)++; break; } - if (!isspace(**data)) + if (**data == '\0') + option = "-"; + else if (!i_isspace(**data)) option = cmd_get_param(data); else { option = "-"; @@ -568,6 +578,18 @@ static int get_cmd_options(char **data, int ignore_unknown, /* check if this option can have argument */ pos = optlist == NULL ? -1 : option_find(optlist, option); + + if (pos == -1 && optlist != NULL && + is_numeric(option, '\0')) { + /* check if we want - option */ + pos = option_find(optlist, "#"); + if (pos != -1) { + g_hash_table_insert(options, "#", + option); + pos = -3; + } + } + if (pos == -1 && !ignore_unknown) { /* unknown option! */ *data = option; @@ -584,21 +606,21 @@ static int get_cmd_options(char **data, int ignore_unknown, option = optlist[pos] + iscmdtype(*optlist[pos]); } - if (options != NULL) + if (options != NULL && pos != -3) g_hash_table_insert(options, option, ""); if (pos < 0 || !iscmdtype(*optlist[pos]) || *optlist[pos] == '!') option = NULL; - while (isspace(**data)) (*data)++; + while (i_isspace(**data)) (*data)++; continue; } if (option == NULL) break; - if (*optlist[pos] == '@' && !isdigit(**data)) + if (*optlist[pos] == '@' && !i_isdigit(**data)) break; /* expected a numeric argument */ /* save the argument */ @@ -609,7 +631,7 @@ static int get_cmd_options(char **data, int ignore_unknown, } option = NULL; - while (isspace(**data)) (*data)++; + while (i_isspace(**data)) (*data)++; } return 0; @@ -620,7 +642,8 @@ typedef struct { GHashTable *options; } CMD_TEMP_REC; -static char *get_optional_channel(WI_ITEM_REC *active_item, char **data) +static char *get_optional_channel(WI_ITEM_REC *active_item, char **data, + int require_name) { CHANNEL_REC *chanrec; char *tmp, *origtmp, *channel, *ret; @@ -633,15 +656,19 @@ static char *get_optional_channel(WI_ITEM_REC *active_item, char **data) origtmp = tmp = g_strdup(*data); channel = cmd_get_param(&tmp); - if (strcmp(channel, "*") == 0 || - !active_item->server->ischannel(channel)) - ret = active_item->name; - else { + if (strcmp(channel, "*") == 0 && !require_name) { + /* "*" means active channel */ + cmd_get_param(data); + ret = active_item->name; + } else if (!server_ischannel(active_item->server, channel)) { + /* we don't have channel parameter - use active channel */ + ret = active_item->name; + } else { /* Find the channel first and use it's name if found. This allows automatic !channel -> !XXXXXchannel replaces. */ channel = cmd_get_param(data); - chanrec = channel_find(active_item->server, channel); + chanrec = channel_find(active_item->server, channel); ret = chanrec == NULL ? channel : chanrec->name; } @@ -656,7 +683,7 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...) GHashTable **opthash; char **str, *arg, *datad; va_list args; - int cnt, error, ignore_unknown; + int cnt, error, ignore_unknown, require_name; g_return_val_if_fail(data != NULL, FALSE); @@ -669,8 +696,8 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...) datad = rec->data; error = FALSE; - item = (count & PARAM_FLAG_OPTCHAN) == 0 ? NULL: - (WI_ITEM_REC *) va_arg(args, WI_ITEM_REC *); + item = (count & PARAM_FLAG_OPTCHAN) == 0 ? NULL: + (WI_ITEM_REC *) va_arg(args, WI_ITEM_REC *); if (count & PARAM_FLAG_OPTIONS) { arg = (char *) va_arg(args, char *); @@ -690,7 +717,8 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...) cnt = PARAM_WITHOUT_FLAGS(count); if (count & PARAM_FLAG_OPTCHAN) { /* optional channel as first parameter */ - arg = get_optional_channel(item, &datad); + require_name = (count & PARAM_FLAG_OPTCHAN_NAME); + arg = get_optional_channel(item, &datad, require_name); str = (char **) va_arg(args, char **); if (str != NULL) *str = arg; @@ -741,7 +769,7 @@ static void command_module_unbind_all(COMMAND_REC *rec, for (tmp = modrec->signals; tmp != NULL; tmp = next) { next = tmp->next; - command_unbind(rec->cmd, tmp->data); + command_unbind(rec->cmd, (SIGNAL_FUNC) tmp->data); } if (g_slist_find(commands, rec) != NULL) { @@ -767,6 +795,28 @@ void commands_remove_module(const char *module) } } +static int cmd_protocol_match(COMMAND_REC *cmd, SERVER_REC *server) +{ + GSList *tmp; + + for (tmp = cmd->modules; tmp != NULL; tmp = tmp->next) { + COMMAND_MODULE_REC *rec = tmp->data; + + if (rec->protocol == -1) { + /* at least one module accepts the command + without specific protocol */ + return 1; + } + + if (server != NULL && rec->protocol == server->chat_type) { + /* matching protocol found */ + return 1; + } + } + + return 0; +} + #define alias_runstack_push(alias) \ alias_runstack = g_slist_append(alias_runstack, alias) @@ -779,6 +829,7 @@ void commands_remove_module(const char *module) static void parse_command(const char *command, int expand_aliases, SERVER_REC *server, void *item) { + COMMAND_REC *rec; const char *alias, *newcmd; char *cmd, *orig, *args, *oldcmd; @@ -808,17 +859,32 @@ static void parse_command(const char *command, int expand_aliases, return; } - cmd = g_strconcat("command ", newcmd, NULL); - if (server != NULL) - server_redirect_default(SERVER(server), cmd); + rec = command_find(newcmd); + if (rec != NULL && !cmd_protocol_match(rec, server)) { + g_free(orig); + signal_emit("error command", 2, + GINT_TO_POINTER(server == NULL ? + CMDERR_NOT_CONNECTED : + CMDERR_ILLEGAL_PROTO)); + return; + } + + cmd = g_strconcat("command ", newcmd, NULL); g_strdown(cmd); + oldcmd = current_command; current_command = cmd+8; - if (!signal_emit(cmd, 3, args, server, item)) { + if (server != NULL) server_ref(server); + if (!signal_emit(cmd, 3, args, server, item)) { signal_emit_id(signal_default_command, 3, command, server, item); } + if (server != NULL) { + if (server->connection_lost) + server_disconnect(server); + server_unref(server); + } current_command = oldcmd; g_free(cmd); diff --git a/apps/irssi/src/core/commands.h b/apps/irssi/src/core/commands.h index 9dee2e80..3e55a2ad 100644 --- a/apps/irssi/src/core/commands.h +++ b/apps/irssi/src/core/commands.h @@ -6,6 +6,7 @@ typedef struct { char *name; char *options; + int protocol; /* chat protocol required for this command */ GSList *signals; } COMMAND_MODULE_REC; @@ -26,10 +27,11 @@ enum { CMDERR_ERRNO, /* get the error from errno */ CMDERR_NOT_ENOUGH_PARAMS, /* not enough parameters given */ - CMDERR_NOT_CONNECTED, /* not connected to IRC server */ + CMDERR_NOT_CONNECTED, /* not connected to server */ CMDERR_NOT_JOINED, /* not joined to any channels in this window */ CMDERR_CHAN_NOT_FOUND, /* channel not found */ CMDERR_CHAN_NOT_SYNCED, /* channel not fully synchronized yet */ + CMDERR_ILLEGAL_PROTO, /* requires different chat protocol than the active server */ CMDERR_NOT_GOOD_IDEA /* not good idea to do, -yes overrides this */ }; @@ -56,10 +58,14 @@ extern char *current_command; /* the command we're right now. */ /* Bind command to specified function. */ void command_bind_to(const char *module, int pos, const char *cmd, - const char *category, SIGNAL_FUNC func); -#define command_bind(a, b, c) command_bind_to(MODULE_NAME, 1, a, b, c) -#define command_bind_first(a, b, c) command_bind_to(MODULE_NAME, 0, a, b, c) -#define command_bind_last(a, b, c) command_bind_to(MODULE_NAME, 2, a, b, c) + int protocol, const char *category, SIGNAL_FUNC func); +#define command_bind(a, b, c) command_bind_to(MODULE_NAME, 1, a, -1, b, c) +#define command_bind_first(a, b, c) command_bind_to(MODULE_NAME, 0, a, -1, b, c) +#define command_bind_last(a, b, c) command_bind_to(MODULE_NAME, 2, a, -1, b, c) + +#define command_bind_proto(a, b, c, d) command_bind_to(MODULE_NAME, 1, a, b, c, d) +#define command_bind_proto_first(a, b, c, d) command_bind_to(MODULE_NAME, 0, a, b, c, d) +#define command_bind_proto_last(a, b, c, d) command_bind_to(MODULE_NAME, 2, a, b, c, d) void command_unbind(const char *cmd, SIGNAL_FUNC func); @@ -129,6 +135,8 @@ int command_have_option(const char *cmd, const char *option); #define PARAM_FLAG_UNKNOWN_OPTIONS 0x00008000 /* optional channel in first argument */ #define PARAM_FLAG_OPTCHAN 0x00010000 +/* optional channel in first argument, but don't treat "*" as current channel */ +#define PARAM_FLAG_OPTCHAN_NAME 0x00030000 char *cmd_get_param(char **data); /* get parameters from command - you should point free_me somewhere and diff --git a/apps/irssi/src/core/core.c b/apps/irssi/src/core/core.c index fd7459db..2321208e 100644 --- a/apps/irssi/src/core/core.c +++ b/apps/irssi/src/core/core.c @@ -19,13 +19,17 @@ */ #include "module.h" +#include +#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" @@ -42,13 +46,159 @@ #include "nicklist.h" #include "nickmatch-cache.h" +#ifdef HAVE_SYS_RESOURCE_H +# include + struct rlimit orig_core_rlimit; +#endif + void chat_commands_init(void); void chat_commands_deinit(void); +void log_away_init(void); +void log_away_deinit(void); + int irssi_gui; +int irssi_init_finished; + +static char *irssi_dir, *irssi_config_file; +static GSList *dialog_type_queue, *dialog_text_queue; + +const char *get_irssi_dir(void) +{ + return irssi_dir; +} + +/* return full path for ~/.irssi/config */ +const char *get_irssi_config(void) +{ + return irssi_config_file; +} + +static void read_settings(void) +{ +#ifndef WIN32 + static int signals[] = { + SIGHUP, SIGINT, SIGQUIT, SIGTERM, + SIGALRM, SIGUSR1, SIGUSR2 + }; + static char *signames[] = { + "hup", "int", "quit", "term", + "alrm", "usr1", "usr2" + }; + + const char *ignores; + struct sigaction act; + int n; + + ignores = settings_get_str("ignore_signals"); + + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + + for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) { + act.sa_handler = find_substr(ignores, signames[n]) ? + SIG_IGN : SIG_DFL; + sigaction(signals[n], &act, NULL); + } + +#ifdef HAVE_SYS_RESOURCE_H + if (!settings_get_bool("override_coredump_limit")) + setrlimit(RLIMIT_CORE, &orig_core_rlimit); + else { + struct rlimit rlimit; + + rlimit.rlim_cur = RLIM_INFINITY; + rlimit.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rlimit) == -1) + settings_set_bool("override_coredump_limit", FALSE); + } +#endif +#endif +} + +static void sig_gui_dialog(const char *type, const char *text) +{ + dialog_type_queue = g_slist_append(dialog_type_queue, g_strdup(type)); + dialog_text_queue = g_slist_append(dialog_text_queue, g_strdup(text)); +} + +static void sig_init_finished(void) +{ + GSList *type, *text; + + signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); + signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished); + + /* send the dialog texts that were in queue before irssi + was initialized */ + type = dialog_type_queue; + text = dialog_text_queue; + for (; text != NULL; text = text->next, type = type->next) { + signal_emit("gui dialog", 2, type->data, text->data); + g_free(type->data); + g_free(text->data); + } + g_slist_free(dialog_type_queue); + g_slist_free(dialog_text_queue); +} + +void core_init_paths(int argc, char *argv[]) +{ + static struct poptOption options[] = { + { "config", 0, POPT_ARG_STRING, NULL, 0, "Configuration file location (~/.irssi/config)", "PATH" }, + { "home", 0, POPT_ARG_STRING, NULL, 0, "Irssi home dir location (~/.irssi)", "PATH" }, + { NULL, '\0', 0, NULL } + }; + char *str; + int n, len; + + for (n = 1; n < argc; n++) { + if (strncmp(argv[n], "--home=", 7) == 0) { + g_free_not_null(irssi_dir); + irssi_dir = convert_home(argv[n]+7); + len = strlen(irssi_dir); + if (irssi_dir[len-1] == G_DIR_SEPARATOR) + irssi_dir[len-1] = '\0'; + } else if (strncmp(argv[n], "--config=", 9) == 0) { + g_free_not_null(irssi_config_file); + irssi_config_file = convert_home(argv[n]+9); + } + } + + if (irssi_dir != NULL && !g_path_is_absolute(irssi_dir)) { + str = irssi_dir; + irssi_dir = g_strdup_printf("%s/%s", g_get_current_dir(), str); + g_free(str); + } + + if (irssi_config_file != NULL && + !g_path_is_absolute(irssi_config_file)) { + str = irssi_config_file; + irssi_config_file = + g_strdup_printf("%s/%s", g_get_current_dir(), str); + g_free(str); + } + + args_register(options); + + if (irssi_dir == NULL) + irssi_dir = g_strdup_printf(IRSSI_DIR_FULL, g_get_home_dir()); + if (irssi_config_file == NULL) + irssi_config_file = g_strdup_printf("%s/config", irssi_dir); + + session_set_binary(argv[0]); +} -void core_init(void) +static void sig_irssi_init_finished(void) { + irssi_init_finished = TRUE; +} + +void core_init(int argc, char *argv[]) +{ + dialog_type_queue = NULL; + dialog_text_queue = NULL; + modules_init(); #ifndef WIN32 pidwait_init(); @@ -57,9 +207,14 @@ void core_init(void) net_disconnect_init(); net_sendbuffer_init(); signals_init(); + + signal_add_first("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); + signal_add_first("irssi init finished", (SIGNAL_FUNC) sig_init_finished); + settings_init(); commands_init(); - nickmatch_cache_init(); + nickmatch_cache_init(); + session_init(); chat_protocols_init(); chatnets_init(); @@ -68,6 +223,7 @@ void core_init(void) servers_init(); write_buffer_init(); log_init(); + log_away_init(); rawlog_init(); channels_init(); @@ -75,11 +231,27 @@ void core_init(void) nicklist_init(); chat_commands_init(); - settings_check(); + + settings_add_str("misc", "ignore_signals", ""); + settings_add_bool("misc", "override_coredump_limit", TRUE); + +#ifdef HAVE_SYS_RESOURCE_H + getrlimit(RLIMIT_CORE, &orig_core_rlimit); +#endif + read_settings(); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + signal_add("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished); + + settings_check(); + + module_register("core", "core"); } void core_deinit(void) { + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + signal_remove("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished); + chat_commands_deinit(); nicklist_deinit(); @@ -87,6 +259,7 @@ void core_deinit(void) channels_deinit(); rawlog_deinit(); + log_away_deinit(); log_deinit(); write_buffer_deinit(); servers_deinit(); @@ -95,6 +268,7 @@ void core_deinit(void) chatnets_deinit(); chat_protocols_deinit(); + session_deinit(); nickmatch_cache_deinit(); commands_deinit(); settings_deinit(); @@ -106,4 +280,7 @@ void core_deinit(void) pidwait_deinit(); #endif modules_deinit(); + + g_free(irssi_dir); + g_free(irssi_config_file); } diff --git a/apps/irssi/src/core/core.h b/apps/irssi/src/core/core.h index 61d6ef70..6b7ec621 100644 --- a/apps/irssi/src/core/core.h +++ b/apps/irssi/src/core/core.h @@ -10,6 +10,9 @@ #define IRSSI_GUI_KDE 5 extern int irssi_gui; +extern int irssi_init_finished; /* TRUE after "irssi init finished" signal is sent */ + +void core_init_paths(int argc, char *argv[]); void core_init(void); void core_deinit(void); diff --git a/apps/irssi/src/core/expandos.c b/apps/irssi/src/core/expandos.c index 21ebae6b..777d50e1 100644 --- a/apps/irssi/src/core/expandos.c +++ b/apps/irssi/src/core/expandos.c @@ -48,16 +48,19 @@ typedef struct { static int timer_tag; -static EXPANDO_REC *char_expandos[127]; +static EXPANDO_REC *char_expandos[255]; static GHashTable *expandos; static time_t client_start_time; static char *last_sent_msg, *last_sent_msg_body; static char *last_privmsg_from, *last_public_from; static char *sysname, *sysrelease, *sysarch; + static const char *timestamp_format; +static int timestamp_seconds; +static time_t last_timestamp; -#define CHAR_EXPANDOS_COUNT \ - ((int) (sizeof(char_expandos) / sizeof(char_expandos[0]))) +#define CHAR_EXPANDO(chr) \ + (char_expandos[(int) (unsigned char) chr]) /* Create expando - overrides any existing ones. */ void expando_create(const char *key, EXPANDO_FUNC func, ...) @@ -73,7 +76,7 @@ void expando_create(const char *key, EXPANDO_FUNC func, ...) rec = g_hash_table_lookup(expandos, key); else { /* single character expando */ - rec = char_expandos[(int) *key]; + rec = CHAR_EXPANDO(*key); } if (rec != NULL) @@ -83,7 +86,7 @@ void expando_create(const char *key, EXPANDO_FUNC func, ...) if (key[1] != '\0') g_hash_table_insert(expandos, g_strdup(key), rec); else - char_expandos[(int) *key] = rec; + char_expandos[(int) (unsigned char) *key] = rec; } rec->func = func; @@ -99,7 +102,7 @@ static EXPANDO_REC *expando_find(const char *key) if (key[1] != '\0') return g_hash_table_lookup(expandos, key); else - return char_expandos[(int) *key]; + return CHAR_EXPANDO(*key); } /* Add new signal to expando */ @@ -136,9 +139,9 @@ void expando_destroy(const char *key, EXPANDO_FUNC func) if (key[1] == '\0') { /* single character expando */ - rec = char_expandos[(int) *key]; + rec = CHAR_EXPANDO(*key); if (rec != NULL && rec->func == func) { - char_expandos[(int) *key] = NULL; + char_expandos[(int) (unsigned char) *key] = NULL; g_free(rec); } } else if (g_hash_table_lookup_extended(expandos, key, &origkey, @@ -209,12 +212,42 @@ void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs) } } -EXPANDO_FUNC expando_find_char(char chr) +/* Returns [, EXPANDO_ARG_xxx, , ..., -1] */ +int *expando_get_signals(const char *key) { - g_return_val_if_fail(chr < CHAR_EXPANDOS_COUNT, NULL); + EXPANDO_REC *rec; + int *signals; + int n; + + g_return_val_if_fail(key != NULL, NULL); + + rec = expando_find(key); + if (rec == NULL || rec->signals < 0) + return NULL; + + if (rec->signals == 0) { + /* it's unknown when this expando changes.. + check it once in a second */ + signals = g_new(int, 3); + signals[0] = signal_get_uniq_id("expando timer"); + signals[1] = EXPANDO_ARG_NONE; + signals[2] = -1; + return signals; + } + + signals = g_new(int, rec->signals*2+1); + for (n = 0; n < rec->signals; n++) { + signals[n*2] = rec->signal_ids[n]; + signals[n*2+1] = rec->signal_args[n]; + } + signals[rec->signals*2] = -1; + return signals; +} - return char_expandos[(int) chr] == NULL ? NULL : - char_expandos[(int) chr]->func; +EXPANDO_FUNC expando_find_char(char chr) +{ + return CHAR_EXPANDO(chr) == NULL ? NULL : + CHAR_EXPANDO(chr)->func; } EXPANDO_FUNC expando_find_long(const char *key) @@ -327,10 +360,18 @@ static char *expando_target(SERVER_REC *server, void *item, int *free_ret) return item == NULL ? "" : ((WI_ITEM_REC *) item)->name; } -/* client release date (numeric version string) */ +/* client release date (in YYYYMMDD format) */ static char *expando_releasedate(SERVER_REC *server, void *item, int *free_ret) { - return IRSSI_VERSION_DATE; + *free_ret = TRUE; + return g_strdup_printf("%d", IRSSI_VERSION_DATE); +} + +/* client release time (in HHMM format) */ +static char *expando_releasetime(SERVER_REC *server, void *item, int *free_ret) +{ + *free_ret = TRUE; + return g_strdup_printf("%04d", IRSSI_VERSION_TIME); } /* current working directory */ @@ -439,13 +480,42 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg, static int sig_timer(void) { + time_t now; + struct tm *tm; + int last_min; + signal_emit("expando timer", 0); + + /* check if $Z has changed */ + now = time(NULL); + if (last_timestamp != now) { + if (!timestamp_seconds && last_timestamp != 0) { + /* assume it changes every minute */ + tm = localtime(&last_timestamp); + last_min = tm->tm_min; + + tm = localtime(&now); + if (tm->tm_min == last_min) + return 1; + } + + signal_emit("time changed", 0); + last_timestamp = now; + } + return 1; } static void read_settings(void) { - timestamp_format = settings_get_str("timestamp_format"); + timestamp_format = settings_get_str("timestamp_format"); + timestamp_seconds = + strstr(timestamp_format, "%r") != NULL || + strstr(timestamp_format, "%s") != NULL || + strstr(timestamp_format, "%S") != NULL || + strstr(timestamp_format, "%X") != NULL || + strstr(timestamp_format, "%T") != NULL; + } void expandos_init(void) @@ -459,10 +529,11 @@ void expandos_init(void) client_start_time = time(NULL); last_sent_msg = NULL; last_sent_msg_body = NULL; last_privmsg_from = NULL; last_public_from = NULL; + last_timestamp = 0; sysname = sysrelease = sysarch = NULL; #ifdef HAVE_SYS_UTSNAME_H - if (uname(&un) == 0) { + if (uname(&un) >= 0) { sysname = g_strdup(un.sysname); sysrelease = g_strdup(un.release); sysarch = g_strdup(un.machine); @@ -521,11 +592,14 @@ void expandos_init(void) "window item changed", EXPANDO_ARG_WINDOW, NULL); expando_create("V", expando_releasedate, "", EXPANDO_NEVER, NULL); + expando_create("versiontime", expando_releasetime, + "", EXPANDO_NEVER, NULL); expando_create("W", expando_workdir, NULL); expando_create("Y", expando_realname, "window changed", EXPANDO_ARG_NONE, "window server changed", EXPANDO_ARG_WINDOW, NULL); - expando_create("Z", expando_time, NULL); + expando_create("Z", expando_time, + "time changed", EXPANDO_ARG_NONE, NULL); expando_create("$", expando_dollar, "", EXPANDO_NEVER, NULL); @@ -549,7 +623,7 @@ void expandos_init(void) read_settings(); - timer_tag = g_timeout_add(1000, (GSourceFunc) sig_timer, NULL); + timer_tag = g_timeout_add(500, (GSourceFunc) sig_timer, NULL); signal_add("message public", (SIGNAL_FUNC) sig_message_public); signal_add("message private", (SIGNAL_FUNC) sig_message_private); signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private); @@ -560,7 +634,7 @@ void expandos_deinit(void) { int n; - for (n = 0; n < CHAR_EXPANDOS_COUNT; n++) + for (n = 0; n < sizeof(char_expandos)/sizeof(char_expandos[0]); n++) g_free_not_null(char_expandos[n]); expando_destroy("sysname", expando_sysname); diff --git a/apps/irssi/src/core/expandos.h b/apps/irssi/src/core/expandos.h index 3dcb527e..cf057994 100644 --- a/apps/irssi/src/core/expandos.h +++ b/apps/irssi/src/core/expandos.h @@ -5,7 +5,7 @@ /* first argument of signal must match to active .. */ typedef enum { - EXPANDO_ARG_NONE, + EXPANDO_ARG_NONE = 1, EXPANDO_ARG_SERVER, EXPANDO_ARG_WINDOW, EXPANDO_ARG_WINDOW_ITEM, @@ -28,6 +28,9 @@ void expando_destroy(const char *key, EXPANDO_FUNC func); void expando_bind(const char *key, int funccount, SIGNAL_FUNC *funcs); void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs); +/* Returns [, EXPANDO_ARG_xxx, , ..., -1] */ +int *expando_get_signals(const char *key); + /* internal: */ EXPANDO_FUNC expando_find_char(char chr); EXPANDO_FUNC expando_find_long(const char *key); diff --git a/apps/irssi/src/core/ignore.c b/apps/irssi/src/core/ignore.c index 69460c5b..cebbc3b0 100644 --- a/apps/irssi/src/core/ignore.c +++ b/apps/irssi/src/core/ignore.c @@ -161,9 +161,8 @@ int ignore_check(SERVER_REC *server, const char *nick, const char *host, g_return_val_if_fail(server != NULL, 0); if (nick == NULL) nick = ""; - chanrec = (channel != NULL && server != NULL && - server->ischannel(channel)) ? - channel_find(server, channel) : NULL; + chanrec = server == NULL || channel == NULL ? NULL : + channel_find(server, channel); if (chanrec != NULL && nick != NULL && (nickrec = nicklist_find(chanrec, nick)) != NULL) { /* nick found - check only ignores in nickmatch cache */ @@ -317,18 +316,24 @@ static void ignore_remove_config(IGNORE_REC *rec) if (node != NULL) iconfig_node_list_remove(node, ignore_index(rec)); } -void ignore_add_rec(IGNORE_REC *rec) +static void ignore_init_rec(IGNORE_REC *rec) { #ifdef HAVE_REGEX_H rec->regexp_compiled = !rec->regexp || rec->pattern == NULL ? FALSE : regcomp(&rec->preg, rec->pattern, REG_EXTENDED|REG_ICASE|REG_NOSUB) == 0; #endif +} + +void ignore_add_rec(IGNORE_REC *rec) +{ + ignore_init_rec(rec); ignores = g_slist_append(ignores, rec); ignore_set_config(rec); signal_emit("ignore created", 1, rec); + nickmatch_rebuild(nickmatch); } static void ignore_destroy(IGNORE_REC *rec, int send_signal) @@ -402,7 +407,8 @@ static void read_ignores(void) return; } - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { node = tmp->data; if (node->type != NODE_TYPE_BLOCK) @@ -412,25 +418,17 @@ static void read_ignores(void) ignores = g_slist_append(ignores, rec); rec->mask = g_strdup(config_node_get_str(node, "mask", NULL)); - if (rec->mask != NULL && strcmp(rec->mask, "*") == 0) { - /* FIXME: remove after .98 */ - g_free(rec->mask); - rec->mask = NULL; - } rec->pattern = g_strdup(config_node_get_str(node, "pattern", NULL)); rec->level = level2bits(config_node_get_str(node, "level", "")); rec->exception = config_node_get_bool(node, "exception", FALSE); - if (*config_node_get_str(node, "except_level", "") != '\0') { - /* FIXME: remove after .98 */ - rec->level = level2bits(config_node_get_str(node, "except_level", "")); - rec->exception = TRUE; - } rec->regexp = config_node_get_bool(node, "regexp", FALSE); rec->fullword = config_node_get_bool(node, "fullword", FALSE); rec->replies = config_node_get_bool(node, "replies", FALSE); node = config_node_section(node, "channels", -1); if (node != NULL) rec->channels = config_node_get_list(node); + + ignore_init_rec(rec); } nickmatch_rebuild(nickmatch); diff --git a/apps/irssi/src/core/levels.c b/apps/irssi/src/core/levels.c index aae506a8..0caf58b6 100644 --- a/apps/irssi/src/core/levels.c +++ b/apps/irssi/src/core/levels.c @@ -43,7 +43,7 @@ static const char *levels[] = { "CLIENTNOTICES", "CLIENTCRAP", "CLIENTERRORS", - "HILIGHT", + "HILIGHTS", "NOHILIGHT", NULL diff --git a/apps/irssi/src/core/log-away.c b/apps/irssi/src/core/log-away.c new file mode 100644 index 00000000..724e4b4a --- /dev/null +++ b/apps/irssi/src/core/log-away.c @@ -0,0 +1,119 @@ +/* + log-away.c : Awaylog handling + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "levels.h" +#include "log.h" +#include "servers.h" +#include "settings.h" + +static LOG_REC *awaylog; +static int away_filepos; +static int away_msgs; + +static void sig_log_written(LOG_REC *log) +{ + if (log != awaylog) return; + + away_msgs++; +} + +static void awaylog_open(void) +{ + const char *fname, *levelstr; + LOG_REC *log; + int level; + + fname = settings_get_str("awaylog_file"); + levelstr = settings_get_str("awaylog_level"); + if (*fname == '\0' || *levelstr == '\0') return; + + level = level2bits(levelstr); + if (level == 0) return; + + log = log_find(fname); + if (log != NULL && log->handle != -1) + return; /* already open */ + + if (log == NULL) { + log = log_create_rec(fname, level); + log->temp = TRUE; + log_update(log); + } + + if (!log_start_logging(log)) { + /* creating log file failed? close it. */ + log_close(log); + return; + } + + awaylog = log; + away_filepos = lseek(log->handle, 0, SEEK_CUR); + away_msgs = 0; +} + +static void awaylog_close(void) +{ + const char *fname; + LOG_REC *log; + + fname = settings_get_str("awaylog_file"); + if (*fname == '\0') return; + + log = log_find(fname); + if (log == NULL || log->handle == -1) { + /* awaylog not open */ + return; + } + + if (awaylog == log) awaylog = NULL; + + signal_emit("awaylog show", 3, log, GINT_TO_POINTER(away_msgs), + GINT_TO_POINTER(away_filepos)); + log_close(log); +} + +static void sig_away_changed(SERVER_REC *server) +{ + if (server->usermode_away) + awaylog_open(); + else + awaylog_close(); +} + +void log_away_init(void) +{ + awaylog = NULL; + away_filepos = 0; + away_msgs = 0; + + settings_add_str("log", "awaylog_file", IRSSI_DIR_SHORT"/away.log"); + settings_add_str("log", "awaylog_level", "msgs hilight"); + + signal_add("log written", (SIGNAL_FUNC) sig_log_written); + signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed); +} + +void log_away_deinit(void) +{ + signal_remove("log written", (SIGNAL_FUNC) sig_log_written); + signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed); +} diff --git a/apps/irssi/src/core/log.c b/apps/irssi/src/core/log.c index 368de25f..e843ffe7 100644 --- a/apps/irssi/src/core/log.c +++ b/apps/irssi/src/core/log.c @@ -30,7 +30,7 @@ #include "lib-config/iconfig.h" #include "settings.h" -#define DEFAULT_LOG_FILE_CREATE_MODE 644 +#define DEFAULT_LOG_FILE_CREATE_MODE 600 #ifdef HAVE_FCNTL static struct flock lock; @@ -165,7 +165,7 @@ void log_stop_logging(LOG_REC *log) static void log_rotate_check(LOG_REC *log) { - char *new_fname; + char *new_fname, *dir; g_return_if_fail(log != NULL); @@ -176,6 +176,12 @@ static void log_rotate_check(LOG_REC *log) if (strcmp(new_fname, log->real_fname) != 0) { /* rotate log */ log_stop_logging(log); + signal_emit("log rotated", 1, log); + + dir = g_dirname(new_fname); + mkpath(dir, LOG_DIR_CREATE_MODE); + g_free(dir); + log_start_logging(log); } g_free(new_fname); @@ -183,6 +189,7 @@ static void log_rotate_check(LOG_REC *log) void log_write_rec(LOG_REC *log, const char *str, int level) { + char *colorstr; struct tm *tm; time_t now; int hour, day; @@ -214,6 +221,11 @@ void log_write_rec(LOG_REC *log, const char *str, int level) log->last = now; + if (log->colorizer == NULL) + colorstr = NULL; + else + str = colorstr = log->colorizer(str); + if ((level & MSGLEVEL_LASTLOG) == 0) log_write_timestamp(log->handle, log_timestamp, str, now); else @@ -221,6 +233,8 @@ void log_write_rec(LOG_REC *log, const char *str, int level) write_buffer(log->handle, "\n", 1); signal_emit("log written", 2, log, str); + + g_free_not_null(colorstr); } LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item, @@ -243,11 +257,11 @@ LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item, return NULL; } -void log_file_write(SERVER_REC *server, const char *item, int level, +void log_file_write(const char *server_tag, const char *item, int level, const char *str, int no_fallbacks) { GSList *tmp, *fallbacks; - char *tmpstr, *servertag; + char *tmpstr; int found; g_return_if_fail(str != NULL); @@ -255,7 +269,6 @@ void log_file_write(SERVER_REC *server, const char *item, int level, if (logs == NULL) return; - servertag = server == NULL ? NULL : server->tag; fallbacks = NULL; found = FALSE; for (tmp = logs; tmp != NULL; tmp = tmp->next) { @@ -271,7 +284,7 @@ void log_file_write(SERVER_REC *server, const char *item, int level, fallbacks = g_slist_append(fallbacks, rec); else if (item != NULL && log_item_find(rec, LOG_ITEM_TARGET, item, - servertag) != NULL) + server_tag) != NULL) log_write_rec(rec, str, level); } @@ -343,6 +356,8 @@ static void log_update_config(LOG_REC *log) if (log->items != NULL) log_items_update_config(log, node); + + signal_emit("log config save", 2, log, node); } static void log_remove_config(LOG_REC *log) @@ -457,7 +472,8 @@ static void log_items_read_config(CONFIG_NODE *node, LOG_REC *log) char *item; int type; - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { node = tmp->data; if (node->type != NODE_TYPE_BLOCK) @@ -500,7 +516,8 @@ static void log_read_config(void) node = iconfig_node_traverse("logs", FALSE); if (node == NULL) return; - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { node = tmp->data; if (node->type != NODE_TYPE_BLOCK) @@ -514,6 +531,8 @@ static void log_read_config(void) log->autoopen = config_node_get_bool(node, "auto_open", FALSE); log->level = level2bits(config_node_get_str(node, "level", 0)); + signal_emit("log config read", 2, log, node); + node = config_node_section(node, "items", -1); if (node != NULL) log_items_read_config(node, log); @@ -548,9 +567,9 @@ void log_init(void) "--- Day changed %a %b %d %Y"); read_settings(); - log_read_config(); signal_add("setup changed", (SIGNAL_FUNC) read_settings); signal_add("setup reread", (SIGNAL_FUNC) log_read_config); + signal_add("irssi init finished", (SIGNAL_FUNC) log_read_config); } void log_deinit(void) @@ -562,4 +581,5 @@ void log_deinit(void) signal_remove("setup changed", (SIGNAL_FUNC) read_settings); signal_remove("setup reread", (SIGNAL_FUNC) log_read_config); + signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config); } diff --git a/apps/irssi/src/core/log.h b/apps/irssi/src/core/log.h index 7361b6a0..6ada7c65 100644 --- a/apps/irssi/src/core/log.h +++ b/apps/irssi/src/core/log.h @@ -1,11 +1,15 @@ #ifndef __LOG_H #define __LOG_H +#define LOG_DIR_CREATE_MODE 0700 + enum { LOG_ITEM_TARGET, /* channel, query, .. */ LOG_ITEM_WINDOW_REFNUM }; +typedef char *(*COLORIZE_FUNC)(const char *str); + typedef struct { int type; char *name; @@ -22,6 +26,7 @@ typedef struct { GSList *items; /* log only on these items */ time_t last; /* when last message was written */ + COLORIZE_FUNC colorizer; unsigned int autoopen:1; /* automatically start logging at startup */ unsigned int failed:1; /* opening log failed last time */ @@ -43,7 +48,7 @@ void log_item_destroy(LOG_REC *log, LOG_ITEM_REC *item); LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item, const char *servertag); -void log_file_write(SERVER_REC *server, const char *item, int level, +void log_file_write(const char *server_tag, const char *item, int level, const char *str, int no_fallbacks); void log_write_rec(LOG_REC *log, const char *str, int level); diff --git a/apps/irssi/src/core/memdebug.c b/apps/irssi/src/core/memdebug.c deleted file mode 100644 index 213d4542..00000000 --- a/apps/irssi/src/core/memdebug.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - memdebug.c : irssi - - Copyright (C) 1999-2000 Timo Sirainen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include - -#include -#include - -/*#define ENABLE_BUFFER_CHECKS*/ -#define BUFFER_CHECK_SIZE 5 -#define MIN_BUFFER_CHECK_SIZE 2 - -typedef struct { - void *p; - int size; - char *file; - int line; - char *comment; -} MEM_REC; - -static GHashTable *data = NULL, *preallocs = NULL; -static const char *comment = ""; - -static void add_flow_checks(char *p, unsigned long size) -{ -#ifdef ENABLE_BUFFER_CHECKS - int n; - - for (n = 0; n < BUFFER_CHECK_SIZE; n++) - p[n] = n ^ 0x7f; - for (n = 0; n < BUFFER_CHECK_SIZE; n++) - p[size-BUFFER_CHECK_SIZE+n] = n ^ 0x7f; -#endif -} - -void ig_memcheck_rec(void *key, MEM_REC *rec) -{ - guchar *p; - int n; - - if (rec->size != INT_MIN){ - p = rec->p; - - for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++) - if (p[n] != (n ^ 0x7f)) - g_error("buffer underflow, file %s line %d!\n", rec->file, rec->line); - - for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++) - if (p[rec->size-BUFFER_CHECK_SIZE+n] != (n ^ 0x7f)) - g_error("buffer overflow, file %s line %d!\n", rec->file, rec->line); - } -} - -static void mem_check(void) -{ -#ifdef ENABLE_BUFFER_CHECKS - g_hash_table_foreach(data, (GHFunc) ig_memcheck_rec, NULL); -#endif -} - -static void data_add(char *p, int size, const char *file, int line) -{ - MEM_REC *rec; - - if (size <= 0 && size != INT_MIN) - g_error("size = %d, file %s line %d", size, file, line); - - if (data == NULL) { - data = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal); - preallocs = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal); - } - - if (g_hash_table_lookup(data, p) != NULL) - g_error("data_add() already malloc()'ed %p (in %s:%d)", p, file, line); - - rec = g_new(MEM_REC, 1); - g_hash_table_insert(data, p, rec); - - rec->p = p; - rec->size = size; - rec->file = g_strdup(file); - rec->line = line; - rec->comment = g_strdup(comment); - - if (size == INT_MIN) - g_hash_table_insert(preallocs, p-BUFFER_CHECK_SIZE, p); - else - add_flow_checks(p, size); - mem_check(); -} - -static void data_clear(char *p) -{ - MEM_REC *rec; - - if (g_hash_table_lookup(preallocs, p) != NULL) - p += BUFFER_CHECK_SIZE; - - rec = g_hash_table_lookup(data, p); - if (rec != NULL && rec->size > 0) - memset(p, 'F', rec->size); -} - -static void *data_remove(char *p, const char *file, int line) -{ - MEM_REC *rec; - - mem_check(); - - if (g_hash_table_lookup(preallocs, p) != NULL) { - g_hash_table_remove(preallocs, p); - p += BUFFER_CHECK_SIZE; - } - - rec = g_hash_table_lookup(data, p); - if (rec == NULL) { - g_warning("data_remove() data %p not found (in %s:%d)", p, file, line); - return p+BUFFER_CHECK_SIZE; - } - - g_hash_table_remove(data, p); - g_free(rec->file); - g_free(rec->comment); - g_free(rec); - - return p; -} - -void *ig_malloc(int size, const char *file, int line) -{ - char *p; - - size += BUFFER_CHECK_SIZE*2; - p = g_malloc(size); - data_add(p, size, file, line); - return (void *) (p+BUFFER_CHECK_SIZE); -} - -void *ig_malloc0(int size, const char *file, int line) -{ - char *p; - - size += BUFFER_CHECK_SIZE*2; - p = g_malloc0(size); - data_add(p, size, file, line); - return (void *) (p+BUFFER_CHECK_SIZE); -} - -void *ig_realloc(void *mem, unsigned long size, const char *file, int line) -{ - char *p, *oldmem = mem; - - size += BUFFER_CHECK_SIZE*2; - oldmem -= BUFFER_CHECK_SIZE; - data_remove(oldmem, file, line); - p = g_realloc(oldmem, size); - data_add(p, size, file, line); - return (void *) (p+BUFFER_CHECK_SIZE); -} - -char *ig_strdup(const char *str, const char *file, int line) -{ - void *p; - - if (str == NULL) return NULL; - - p = ig_malloc(strlen(str)+1, file, line); - strcpy(p, str); - - return p; -} - -char *ig_strndup(const char *str, int count, const char *file, int line) -{ - char *p; - - if (str == NULL) return NULL; - - p = ig_malloc(count+1, file, line); - strncpy(p, str, count); p[count] = '\0'; - - return p; -} - -char *ig_strconcat(const char *file, int line, const char *str, ...) -{ - guint l; - va_list args; - char *s; - char *concat; - - g_return_val_if_fail (str != NULL, NULL); - - l = 1 + strlen (str); - va_start (args, str); - s = va_arg (args, char*); - while (s) - { - l += strlen (s); - s = va_arg (args, char*); - } - va_end (args); - - concat = ig_malloc(l, file, line); - concat[0] = 0; - - strcat (concat, str); - va_start (args, str); - s = va_arg (args, char*); - while (s) - { - strcat (concat, s); - s = va_arg (args, char*); - } - va_end (args); - - return concat; -} - -char *ig_strdup_printf(const char *file, int line, const char *format, ...) -{ - char *buffer, *p; - va_list args; - - va_start (args, format); - buffer = g_strdup_vprintf (format, args); - va_end (args); - - p = ig_malloc(strlen(buffer)+1, file, line); - strcpy(p, buffer); - g_free(buffer); - - return p; -} - -char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args) -{ - char *buffer, *p; - - buffer = g_strdup_vprintf (format, args); - - p = ig_malloc(strlen(buffer)+1, file, line); - strcpy(p, buffer); - g_free(buffer); - - return p; -} - -void ig_free(void *p) -{ - char *cp = p; - - if (cp == NULL) g_error("ig_free() : trying to free NULL"); - - cp -= BUFFER_CHECK_SIZE; - data_clear(cp); - cp = data_remove(cp, "??", 0); - if (cp != NULL) g_free(cp); -} - -GString *ig_string_new(const char *file, int line, const char *str) -{ - GString *ret; - - ret = g_string_new(str); - data_add((void *) ret, INT_MIN, file, line); - return ret; -} - -void ig_string_free(const char *file, int line, GString *str, gboolean freeit) -{ - data_remove((void *) str, file, line); - if (!freeit) - data_add(str->str, INT_MIN, file, line); - - g_string_free(str, freeit); -} - -char *ig_strjoinv(const char *file, int line, const char *sepa, char **array) -{ - char *ret; - - ret = g_strjoinv(sepa, array); - data_add(ret, INT_MIN, file, line); - return ret; -} - -char *ig_dirname(const char *file, int line, const char *fname) -{ - char *ret; - - ret = g_dirname(fname); - data_add(ret, INT_MIN, file, line); - return ret; -} - -char *ig_module_build_path(const char *file, int line, const char *dir, const char *module) -{ - char *ret; - - ret = g_module_build_path(dir, module); - data_add(ret, INT_MIN, file, line); - return ret; -} - -void ig_profile_line(void *key, MEM_REC *rec) -{ - char *data; - - if (*rec->comment == '\0' && - (strcmp(rec->file, "ig_strdup_printf") == 0 || - strcmp(rec->file, "ig_strdup_vprintf") == 0 || - strcmp(rec->file, "ig_strconcat") == 0 || - strcmp(rec->file, "ig_string_free (free = FALSE)") == 0)) - data = (char *) rec->p + BUFFER_CHECK_SIZE; - else - data = rec->comment; - fprintf(stderr, "%s:%d %d bytes (%s)\n", rec->file, rec->line, rec->size, data); -} - -void ig_mem_profile(void) -{ - g_hash_table_foreach(data, (GHFunc) ig_profile_line, NULL); - g_hash_table_destroy(data); - g_hash_table_destroy(preallocs); -} - -static MEM_REC *largest[10]; - -void ig_profile_largest(void *key, MEM_REC *rec) -{ - int n; - - for (n = 0; n < 10; n++) - { - if (largest[n] == NULL || rec->size > largest[n]->size) - { - g_memmove(largest+n+1, largest+n, sizeof(void *)*(9-n)); - largest[n] = rec; - } - } -} - -void ig_mem_profile_largest(void) -{ - /*int n;*/ - - memset(&largest, 0, sizeof(MEM_REC*)*10); - /*g_hash_table_foreach(data, (GHFunc) ig_profile_largest, NULL); - - for (n = 0; n < 10 && largest[n] != NULL; n++) - { - ig_profile_line(NULL, largest[n]); - }*/ -} - -void ig_set_data(const char *data) -{ - comment = data; -} diff --git a/apps/irssi/src/core/memdebug.h b/apps/irssi/src/core/memdebug.h deleted file mode 100644 index 3f328805..00000000 --- a/apps/irssi/src/core/memdebug.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef MEM_DEBUG -void ig_mem_profile(void); - -void ig_set_data(const char *data); - -void *ig_malloc(int size, const char *file, int line); -void *ig_malloc0(int size, const char *file, int line); -void *ig_realloc(void *mem, unsigned long size, const char *file, int line); -char *ig_strdup(const char *str, const char *file, int line); -char *ig_strndup(const char *str, int count, const char *file, int line); -char *ig_strconcat(const char *file, int line, const char *str, ...); -char *ig_strdup_printf(const char *file, int line, const char *format, ...) G_GNUC_PRINTF (3, 4); -char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args); -void ig_free(void *p); -GString *ig_string_new(const char *file, int line, const char *str); -void ig_string_free(const char *file, int line, GString *str, int freeit); -char *ig_strjoinv(const char *file, int line, const char *sepa, char **array); -char *ig_dirname(const char *file, int line, const char *fname); -char *ig_module_build_path(const char *file, int line, const char *dir, const char *module); - -#define g_malloc(a) ig_malloc(a, __FILE__, __LINE__) -#define g_malloc0(a) ig_malloc0(a, __FILE__, __LINE__) -#define g_free ig_free -#define g_realloc(a,b) ig_realloc(a, b, __FILE__, __LINE__) -#define g_strdup(a) ig_strdup(a, __FILE__, __LINE__) -#define g_strndup(a, b) ig_strndup(a, b, __FILE__, __LINE__) -#define g_string_new(a) ig_string_new(__FILE__, __LINE__, a) -#define g_string_free(a, b) ig_string_free(__FILE__, __LINE__, a, b) -#define g_strjoinv(a,b) ig_strjoinv(__FILE__, __LINE__, a, b) -#define g_dirname(a) ig_dirname(__FILE__, __LINE__, a) -#define g_module_build_path(a, b) ig_module_build_path(__FILE__, __LINE__, a, b) - -#ifndef __STRICT_ANSI__ -#define g_strconcat(a...) ig_strconcat(__FILE__, __LINE__, ##a) -#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b) -#define g_strdup_vprintf(a, b...) ig_strdup_vprintf(__FILE__, __LINE__, a, ##b) -#endif -#endif diff --git a/apps/irssi/src/core/misc.c b/apps/irssi/src/core/misc.c index 1af327af..d57c7f66 100644 --- a/apps/irssi/src/core/misc.c +++ b/apps/irssi/src/core/misc.c @@ -126,7 +126,7 @@ int find_substr(const char *list, const char *item) return FALSE; for (;;) { - while (isspace((gint) *list)) list++; + while (i_isspace(*list)) list++; if (*list == '\0') break; ptr = strchr(list, ' '); @@ -324,7 +324,7 @@ char *stristr(const char *data, const char *key) if (key[pos] == '\0') return (char *) data; - if (toupper(data[pos]) == toupper(key[pos])) + if (i_toupper(data[pos]) == i_toupper(key[pos])) pos++; else { data++; @@ -337,7 +337,7 @@ char *stristr(const char *data, const char *key) #define isbound(c) \ ((unsigned char) (c) < 128 && \ - (isspace((int) (c)) || ispunct((int) (c)))) + (i_isspace(c) || i_ispunct(c))) char *strstr_full_case(const char *data, const char *key, int icase) { @@ -364,7 +364,7 @@ char *strstr_full_case(const char *data, const char *key, int icase) return (char *) data; } - match = icase ? (toupper(data[pos]) == toupper(key[pos])) : + match = icase ? (i_toupper(data[pos]) == i_toupper(key[pos])) : data[pos] == key[pos]; if (match && (pos != 0 || data == start || isbound(data[-1]))) @@ -430,10 +430,11 @@ int mkpath(const char *path, int mode) dir = g_strndup(path, (int) (p-path)); if (stat(dir, &statbuf) != 0) { #ifndef WIN32 - if (mkdir(dir, mode) == -1) { + if (mkdir(dir, mode) == -1) #else - if (_mkdir(dir) == -1) { + if (_mkdir(dir) == -1) #endif + { g_free(dir); return -1; } @@ -472,7 +473,7 @@ unsigned int g_istr_hash(gconstpointer v) unsigned int h = 0, g; while (*s != '\0') { - h = (h << 4) + toupper(*s); + h = (h << 4) + i_toupper(*s); if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; @@ -492,7 +493,7 @@ int match_wildcards(const char *cmask, const char *data) newmask = mask = g_strdup(cmask); for (; *mask != '\0' && *data != '\0'; mask++) { if (*mask != '*') { - if (*mask != '?' && toupper(*mask) != toupper(*data)) + if (*mask != '?' && i_toupper(*mask) != i_toupper(*data)) break; data++; @@ -538,7 +539,7 @@ int is_numeric(const char *str, char end_char) return FALSE; while (*str != '\0' && *str != end_char) { - if (!isdigit(*str)) return FALSE; + if (!i_isdigit(*str)) return FALSE; str++; } @@ -725,3 +726,47 @@ GSList *columns_sort_list(GSList *list, int rows) g_slist_length(list), sorted); return sorted; } + +/* Expand escape string, the first character in data should be the + one after '\'. Returns the expanded character or -1 if error. */ +int expand_escape(const char **data) +{ + char digit[4]; + + switch (**data) { + case 't': + return '\t'; + case 'r': + return '\r'; + case 'n': + return '\n'; + case 'e': + return 27; /* ESC */ + + case 'x': + /* hex digit */ + if (!isxdigit((*data)[1]) || !isxdigit((*data)[2])) + return -1; + + digit[0] = (*data)[1]; + digit[1] = (*data)[2]; + digit[2] = '\0'; + *data += 2; + return strtol(digit, NULL, 16); + case 'c': + /* control character (\cA = ^A) */ + (*data)++; + return i_toupper(**data) - 64; + default: + if (!i_isdigit(**data)) + return -1; + + /* octal */ + digit[0] = (*data)[0]; + digit[1] = (*data)[1]; + digit[2] = (*data)[2]; + digit[3] = '\0'; + *data += 2; + return strtol(digit, NULL, 8); + } +} diff --git a/apps/irssi/src/core/misc.h b/apps/irssi/src/core/misc.h index 45c8ce11..804cffc2 100644 --- a/apps/irssi/src/core/misc.h +++ b/apps/irssi/src/core/misc.h @@ -101,4 +101,8 @@ int get_max_column_count(GSList *items, COLUMN_LEN_FUNC len_func, /* Return a column sorted copy of a list. */ GSList *columns_sort_list(GSList *list, int rows); +/* Expand escape string, the first character in data should be the + one after '\'. Returns the expanded character or -1 if error. */ +int expand_escape(const char **data); + #endif diff --git a/apps/irssi/src/core/modules-load.c b/apps/irssi/src/core/modules-load.c new file mode 100644 index 00000000..e7e78091 --- /dev/null +++ b/apps/irssi/src/core/modules-load.c @@ -0,0 +1,414 @@ +/* + modules-load.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "modules.h" +#include "modules-load.h" +#include "signals.h" + +#include "settings.h" +#include "commands.h" +#include "misc.h" + +#ifdef HAVE_GMODULE + +/* Returns the module name without path, "lib" prefix or ".so" suffix */ +static char *module_get_name(const char *path, int *start, int *end) +{ + const char *name; + char *module_name, *ptr; + + name = NULL; + if (*path == '~' || g_path_is_absolute(path)) { + name = strrchr(path, G_DIR_SEPARATOR); + if (name != NULL) name++; + } + + if (name == NULL) + name = path; + + if (strncmp(name, "lib", 3) == 0) + name += 3; + + module_name = g_strdup(name); + ptr = strchr(module_name, '.'); + if (ptr != NULL) *ptr = '\0'; + + *start = (int) (name-path); + *end = *start + (ptr == NULL ? strlen(name) : + (int) (ptr-module_name)); + + return module_name; +} + +/* Returns the root module name for given submodule (eg. perl_core -> perl) */ +static char *module_get_root(const char *name, char **prefixes) +{ + int len; + + /* skip any of the prefixes.. */ + while (*prefixes != NULL) { + len = strlen(*prefixes); + if (strncmp(name, *prefixes, len) == 0 && name[len] == '_') { + name += len+1; + break; + } + prefixes++; + } + + /* skip the _core part */ + len = strlen(name); + if (len > 5 && strcmp(name+len-5, "_core") == 0) + return g_strndup(name, len-5); + + return g_strdup(name); +} + +/* Returns the sub module name for given submodule (eg. perl_core -> core) */ +static char *module_get_sub(const char *name, const char *root) +{ + int rootlen, namelen; + + namelen = strlen(name); + rootlen = strlen(root); + g_return_val_if_fail(namelen >= rootlen, g_strdup(name)); + + if (strncmp(name, root, rootlen) == 0 && + strcmp(name+rootlen, "_core") == 0) + return g_strdup("core"); + + if (namelen+1 > rootlen && name[namelen-rootlen-1] == '_' && + strcmp(name+namelen-rootlen, root) == 0) + return g_strndup(name, namelen-rootlen-1); + + return g_strdup(name); +} + +static GModule *module_open(const char *name, int *found) +{ + struct stat statbuf; + GModule *module; + char *path, *str; + + if (g_path_is_absolute(name) || *name == '~' || + (*name == '.' && name[1] == G_DIR_SEPARATOR)) + path = g_strdup(name); + else { + /* first try from home dir */ + str = g_strdup_printf("%s/modules", get_irssi_dir()); + path = g_module_build_path(str, name); + g_free(str); + + if (stat(path, &statbuf) == 0) { + module = g_module_open(path, (GModuleFlags) 0); + g_free(path); + *found = TRUE; + return module; + } + + /* module not found from home dir, try global module dir */ + g_free(path); + path = g_module_build_path(MODULEDIR, name); + } + + *found = stat(path, &statbuf) == 0; + module = g_module_open(path, (GModuleFlags) 0); + g_free(path); + return module; +} + +static char *module_get_func(const char *rootmodule, const char *submodule, + const char *function) +{ + if (strcmp(submodule, "core") == 0) + return g_strconcat(rootmodule, "_core_", function, NULL); + + if (strcmp(rootmodule, submodule) == 0) + return g_strconcat(rootmodule, "_", function, NULL); + + return g_strconcat(submodule, "_", rootmodule, "_", function, NULL); +} + +#define module_error(error, text, rootmodule, submodule) \ + signal_emit("module error", 4, GINT_TO_POINTER(error), text, \ + rootmodule, submodule) + +/* Returns 1 if ok, 0 if error in module and + -1 if module wasn't found */ +static int module_load_name(const char *path, const char *rootmodule, + const char *submodule, int silent) +{ + void (*module_init) (void); + void (*module_deinit) (void); + GModule *gmodule; + MODULE_REC *module; + MODULE_FILE_REC *rec; + char *initfunc, *deinitfunc; + int found; + + gmodule = module_open(path, &found); + if (gmodule == NULL) { + if (!silent || found) { + module_error(MODULE_ERROR_LOAD, g_module_error(), + rootmodule, submodule); + } + return found ? 0 : -1; + } + + /* get the module's init() and deinit() functions */ + initfunc = module_get_func(rootmodule, submodule, "init"); + deinitfunc = module_get_func(rootmodule, submodule, "deinit"); + found = g_module_symbol(gmodule, initfunc, (gpointer *) &module_init) && + g_module_symbol(gmodule, deinitfunc, (gpointer *) &module_deinit); + g_free(initfunc); + g_free(deinitfunc); + + if (!found) { + module_error(MODULE_ERROR_INVALID, NULL, + rootmodule, submodule); + g_module_close(gmodule); + return 0; + } + + /* Call the module's init() function - it should register itself + with module_register() function, abort if it doesn't. */ + module_init(); + + module = module_find(rootmodule); + rec = module == NULL ? NULL : + strcmp(rootmodule, submodule) == 0 ? + module_file_find(module, "core") : + module_file_find(module, submodule); + if (rec == NULL) { + rec = module_register_full(rootmodule, submodule, NULL); + rec->gmodule = gmodule; + module_file_unload(rec); + + module_error(MODULE_ERROR_INVALID, NULL, + rootmodule, submodule); + return 0; + } + + rec->module_deinit = module_deinit; + rec->gmodule = gmodule; + rec->initialized = TRUE; + + settings_check_module(rec->defined_module_name); + + signal_emit("module loaded", 2, rec->root, rec); + return 1; +} + +static int module_load_prefixes(const char *path, const char *module, + int start, int end, char **prefixes) +{ + GString *realpath; + int status, ok; + + /* load module_core */ + realpath = g_string_new(path); + g_string_insert(realpath, end, "_core"); + + /* Don't print the error message the first time, since the module + may not have the core part at all. */ + status = module_load_name(realpath->str, module, "core", TRUE); + ok = status > 0; + + if (prefixes != NULL) { + /* load all the "prefix modules", like the fe-common, irc, + etc. part of the module */ + while (*prefixes != NULL) { + g_string_assign(realpath, path); + g_string_insert_c(realpath, start, '_'); + g_string_insert(realpath, start, *prefixes); + + status = module_load_name(realpath->str, module, + *prefixes, TRUE); + if (status > 0) + ok = TRUE; + + prefixes++; + } + } + + if (!ok) { + /* error loading module, print the error message */ + g_string_assign(realpath, path); + g_string_insert(realpath, end, "_core"); + module_load_name(realpath->str, module, "core", FALSE); + } + + g_string_free(realpath, TRUE); + return ok; +} + +static int module_load_full(const char *path, const char *rootmodule, + const char *submodule, int start, int end, + char **prefixes) +{ + MODULE_REC *module; + int status, try_prefixes; + + if (!g_module_supported()) + return FALSE; + + module = module_find(rootmodule); + if (module != NULL && (strcmp(submodule, rootmodule) == 0 || + module_file_find(module, submodule) != NULL)) { + /* module is already loaded */ + module_error(MODULE_ERROR_ALREADY_LOADED, NULL, + rootmodule, submodule); + return FALSE; + } + + /* check if the given module exists.. */ + try_prefixes = strcmp(rootmodule, submodule) == 0; + status = module_load_name(path, rootmodule, submodule, try_prefixes); + if (status == -1 && try_prefixes) { + /* nope, try loading the module_core, + fe_module, etc. */ + status = module_load_prefixes(path, rootmodule, + start, end, prefixes); + } + + return status > 0; +} + +/* Load module - automatically tries to load also the related non-core + modules given in `prefixes' (like irc, fe, fe_text, ..) */ +int module_load(const char *path, char **prefixes) +{ + char *exppath, *name, *submodule, *rootmodule; + int start, end, ret; + + g_return_val_if_fail(path != NULL, FALSE); + + exppath = convert_home(path); + + name = module_get_name(exppath, &start, &end); + rootmodule = module_get_root(name, prefixes); + submodule = module_get_sub(name, rootmodule); + g_free(name); + + ret = module_load_full(exppath, rootmodule, submodule, + start, end, prefixes); + + g_free(rootmodule); + g_free(submodule); + g_free(exppath); + return ret; +} + +/* Load a sub module. */ +int module_load_sub(const char *path, const char *submodule, char **prefixes) +{ + GString *full_path; + char *exppath, *name, *rootmodule; + int start, end, ret; + + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(submodule != NULL, FALSE); + + exppath = convert_home(path); + + name = module_get_name(exppath, &start, &end); + rootmodule = module_get_root(name, prefixes); + g_free(name); + + full_path = g_string_new(exppath); + if (strcmp(submodule, "core") == 0) + g_string_insert(full_path, end, "_core"); + else { + g_string_insert_c(full_path, start, '_'); + g_string_insert(full_path, start, submodule); + } + + ret = module_load_full(full_path->str, rootmodule, submodule, + start, end, NULL); + + g_string_free(full_path, TRUE); + g_free(rootmodule); + g_free(exppath); + return ret; +} + +static void module_file_deinit_gmodule(MODULE_FILE_REC *file) +{ + /* call the module's deinit() function */ + if (file->module_deinit != NULL) + file->module_deinit(); + + if (file->defined_module_name != NULL) { + settings_remove_module(file->defined_module_name); + commands_remove_module(file->defined_module_name); + signals_remove_module(file->defined_module_name); + } + + g_module_close(file->gmodule); +} + +void module_file_unload(MODULE_FILE_REC *file) +{ + MODULE_REC *root; + + root = file->root; + root->files = g_slist_remove(root->files, file); + + if (file->initialized) + signal_emit("module unloaded", 2, file->root, file); + + if (file->gmodule != NULL) + module_file_deinit_gmodule(file); + + g_free(file->name); + g_free(file->defined_module_name); + g_free(file); + + if (root->files == NULL && g_slist_find(modules, root) != NULL) + module_unload(root); +} + +void module_unload(MODULE_REC *module) +{ + g_return_if_fail(module != NULL); + + modules = g_slist_remove(modules, module); + + signal_emit("module unloaded", 1, module); + + while (module->files != NULL) + module_file_unload(module->files->data); + + g_free(module->name); + g_free(module); +} + +#else /* !HAVE_GMODULE - modules are not supported */ + +int module_load(const char *path, char **prefixes) +{ + return FALSE; +} + +void module_unload(MODULE_REC *module) +{ +} + +#endif diff --git a/apps/irssi/src/core/modules-load.h b/apps/irssi/src/core/modules-load.h new file mode 100644 index 00000000..42e14382 --- /dev/null +++ b/apps/irssi/src/core/modules-load.h @@ -0,0 +1,16 @@ +#ifndef __MODULES_LOAD_H +#define __MODULES_LOAD_H + +#include "modules.h" + +/* Load module - automatically tries to load also the related non-core + modules given in `prefixes' (like irc, fe, fe_text, ..) */ +int module_load(const char *path, char **prefixes); + +/* Load a sub module. */ +int module_load_sub(const char *path, const char *submodule, char **prefixes); + +void module_unload(MODULE_REC *module); +void module_file_unload(MODULE_FILE_REC *file); + +#endif diff --git a/apps/irssi/src/core/modules.c b/apps/irssi/src/core/modules.c index 9797a059..dc373189 100644 --- a/apps/irssi/src/core/modules.c +++ b/apps/irssi/src/core/modules.c @@ -1,7 +1,7 @@ /* modules.c : irssi - Copyright (C) 1999-2000 Timo Sirainen + Copyright (C) 1999-2001 Timo Sirainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,9 +22,6 @@ #include "modules.h" #include "signals.h" -#include "commands.h" -#include "settings.h" - GSList *modules; static GHashTable *uniqids, *uniqstrids; @@ -201,206 +198,62 @@ void module_uniq_destroy(const char *module) } } -MODULE_REC *module_find(const char *name) +/* Register a new module. The `name' is the root module name, `submodule' + specifies the current module to be registered (eg. "perl", "fe"). + The module is registered as statically loaded by default. */ +MODULE_FILE_REC *module_register_full(const char *name, const char *submodule, + const char *defined_module_name) { - GSList *tmp; + MODULE_REC *module; + MODULE_FILE_REC *file; - for (tmp = modules; tmp != NULL; tmp = tmp->next) { - MODULE_REC *rec = tmp->data; + module = module_find(name); + if (module == NULL) { + module = g_new0(MODULE_REC, 1); + module->name = g_strdup(name); - if (g_strcasecmp(rec->name, name) == 0) - return rec; + modules = g_slist_append(modules, module); } - return NULL; -} - -#ifdef HAVE_GMODULE -static char *module_get_name(const char *path, int *start, int *end) -{ - const char *name; - char *module_name, *ptr; + file = module_file_find(module, submodule); + if (file != NULL) + return file; - name = NULL; - if (g_path_is_absolute(path)) { - name = strrchr(path, G_DIR_SEPARATOR); - if (name != NULL) name++; - } + file = g_new0(MODULE_FILE_REC, 1); + file->root = module; + file->name = g_strdup(submodule); + file->defined_module_name = g_strdup(defined_module_name); - if (name == NULL) - name = path; - - if (strncmp(name, "lib", 3) == 0) - name += 3; - - module_name = g_strdup(name); - ptr = strchr(module_name, '.'); - if (ptr != NULL) *ptr = '\0'; - - *start = (int) (name-path); - *end = *start + (ptr == NULL ? strlen(name) : - (int) (module_name-ptr)); - - return module_name; + module->files = g_slist_append(module->files, file); + return file; } -static GModule *module_open(const char *name) +MODULE_REC *module_find(const char *name) { - struct stat statbuf; - GModule *module; - char *path, *str; - - if (g_path_is_absolute(name) || - (*name == '.' && name[1] == G_DIR_SEPARATOR)) - path = g_strdup(name); - else { - /* first try from home dir */ - str = g_strdup_printf("%s/.silc/modules", g_get_home_dir()); - path = g_module_build_path(str, name); - g_free(str); - - if (stat(path, &statbuf) == 0) { - module = g_module_open(path, (GModuleFlags) 0); - g_free(path); - return module; - } - - /* module not found from home dir, try global module dir */ - g_free(path); - path = g_module_build_path(MODULEDIR, name); - } - - module = g_module_open(path, (GModuleFlags) 0); - g_free(path); - return module; -} - -#define module_error(error, module, text) \ - signal_emit("module error", 3, GINT_TO_POINTER(error), module, text) + GSList *tmp; -static int module_load_name(const char *path, const char *name, int silent) -{ - void (*module_init) (void); - GModule *gmodule; - MODULE_REC *rec; - char *initfunc; - - gmodule = module_open(path); - if (gmodule == NULL) { - if (!silent) { - module_error(MODULE_ERROR_LOAD, name, - g_module_error()); - } - return FALSE; - } + for (tmp = modules; tmp != NULL; tmp = tmp->next) { + MODULE_REC *rec = tmp->data; - /* get the module's init() function */ - initfunc = g_strconcat(name, "_init", NULL); - if (!g_module_symbol(gmodule, initfunc, (gpointer *) &module_init)) { - if (!silent) - module_error(MODULE_ERROR_INVALID, name, NULL); - g_module_close(gmodule); - g_free(initfunc); - return FALSE; + if (g_strcasecmp(rec->name, name) == 0) + return rec; } - g_free(initfunc); - - rec = g_new0(MODULE_REC, 1); - rec->name = g_strdup(name); - rec->gmodule = gmodule; - modules = g_slist_append(modules, rec); - module_init(); - settings_check_module(name); - - signal_emit("module loaded", 1, rec); - return TRUE; + return NULL; } -#endif -/* Load module - automatically tries to load also the related non-core - modules given in `prefixes' (like irc, fe, fe_text, ..) */ -int module_load(const char *path, char **prefixes) +MODULE_FILE_REC *module_file_find(MODULE_REC *module, const char *name) { -#ifdef HAVE_GMODULE - GString *realpath; - char *name, *pname; - int ret, start, end; - - g_return_val_if_fail(path != NULL, FALSE); - - if (!g_module_supported()) - return FALSE; + GSList *tmp; - name = module_get_name(path, &start, &end); - if (module_find(name)) { - module_error(MODULE_ERROR_ALREADY_LOADED, name, NULL); - g_free(name); - return FALSE; - } + for (tmp = module->files; tmp != NULL; tmp = tmp->next) { + MODULE_FILE_REC *rec = tmp->data; - /* load "module_core" instead of "module" if it exists */ - realpath = g_string_new(path); - g_string_insert(realpath, end, "_core"); - - pname = g_strconcat(name, "_core", NULL); - ret = module_load_name(realpath->str, pname, TRUE); - g_free(pname); - - if (!ret) { - /* load "module" - complain if it's not found */ - ret = module_load_name(path, name, FALSE); - } else if (prefixes != NULL) { - /* load all the "prefix modules", like the fe-common, irc, - etc. part of the module */ - while (*prefixes != NULL) { - g_string_assign(realpath, path); - g_string_insert(realpath, start, "_"); - g_string_insert(realpath, start, *prefixes); - - pname = g_strconcat(*prefixes, "_", name, NULL); - module_load_name(realpath->str, pname, TRUE); - g_free(pname); - - prefixes++; - } + if (strcmp(rec->name, name) == 0) + return rec; } - g_string_free(realpath, TRUE); - g_free(name); - return ret; -#else - return FALSE; -#endif -} - -void module_unload(MODULE_REC *module) -{ -#ifdef HAVE_GMODULE - void (*module_deinit) (void); - char *deinitfunc; - - g_return_if_fail(module != NULL); - - modules = g_slist_remove(modules, module); - - signal_emit("module unloaded", 1, module); - - /* call the module's deinit() function */ - deinitfunc = g_strconcat(module->name, "_deinit", NULL); - if (g_module_symbol(module->gmodule, deinitfunc, - (gpointer *) &module_deinit)) - module_deinit(); - g_free(deinitfunc); - - settings_remove_module(module->name); - commands_remove_module(module->name); - signals_remove_module(module->name); - - g_module_close(module->gmodule); - g_free(module->name); - g_free(module); -#endif + return NULL; } static void uniq_get_modules(char *key, void *value, GSList **list) diff --git a/apps/irssi/src/core/modules.h b/apps/irssi/src/core/modules.h index 7a83819f..75a77c77 100644 --- a/apps/irssi/src/core/modules.h +++ b/apps/irssi/src/core/modules.h @@ -10,30 +10,57 @@ #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)) diff --git a/apps/irssi/src/core/net-disconnect.c b/apps/irssi/src/core/net-disconnect.c index b0e9535b..f232f1f4 100644 --- a/apps/irssi/src/core/net-disconnect.c +++ b/apps/irssi/src/core/net-disconnect.c @@ -42,6 +42,7 @@ static void net_disconnect_remove(NET_DISCONNECT_REC *rec) disconnects = g_slist_remove(disconnects, rec); g_source_remove(rec->tag); + net_disconnect(rec->handle); g_free(rec); } diff --git a/apps/irssi/src/core/net-sendbuffer.c b/apps/irssi/src/core/net-sendbuffer.c index 04eab80a..fd1039d6 100644 --- a/apps/irssi/src/core/net-sendbuffer.c +++ b/apps/irssi/src/core/net-sendbuffer.c @@ -62,7 +62,7 @@ void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close) g_free(rec); } -/* Transmit all data from buffer - return TRUE if successful */ +/* Transmit all data from buffer - return TRUE if the whole buffer was sent */ static int buffer_send(NET_SENDBUF_REC *rec) { int ret; @@ -140,6 +140,25 @@ int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size) return buffer_add(rec, data, size) ? 0 : -1; } +/* Flush the buffer, blocks until finished. */ +void net_sendbuffer_flush(NET_SENDBUF_REC *rec) +{ + int handle; + + if (rec->buffer == NULL) + return; + + /* set the socket blocking while doing this */ + handle = g_io_channel_unix_get_fd(rec->handle); +#ifndef WIN32 + fcntl(handle, F_SETFL, 0); +#endif + while (!buffer_send(rec)) ; +#ifndef WIN32 + fcntl(handle, F_SETFL, O_NONBLOCK); +#endif +} + /* Returns the socket handle */ GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec) { diff --git a/apps/irssi/src/core/net-sendbuffer.h b/apps/irssi/src/core/net-sendbuffer.h index bb6d8e07..d2388b41 100644 --- a/apps/irssi/src/core/net-sendbuffer.h +++ b/apps/irssi/src/core/net-sendbuffer.h @@ -14,6 +14,9 @@ void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close); occured. */ int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size); +/* Flush the buffer, blocks until finished. */ +void net_sendbuffer_flush(NET_SENDBUF_REC *rec); + /* Returns the socket handle */ GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec); diff --git a/apps/irssi/src/core/network.c b/apps/irssi/src/core/network.c index 4fc06c05..6be87113 100644 --- a/apps/irssi/src/core/network.c +++ b/apps/irssi/src/core/network.c @@ -196,10 +196,14 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt)); - /* set our own address, ignore if bind() fails */ + /* set our own address */ if (my_ip != NULL) { sin_set_ip(&so, my_ip); - bind(handle, &so.sa, SIZEOF_SOCKADDR(so)); + if (bind(handle, &so.sa, SIZEOF_SOCKADDR(so)) == -1) { + /* failed, set it back to INADDR_ANY */ + sin_set_ip(&so, NULL); + bind(handle, &so.sa, SIZEOF_SOCKADDR(so)); + } } /* connect */ @@ -208,10 +212,11 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) ret = connect(handle, &so.sa, SIZEOF_SOCKADDR(so)); #ifndef WIN32 - if (ret < 0 && errno != EINPROGRESS) { + if (ret < 0 && errno != EINPROGRESS) #else - if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) { + if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) #endif + { close(handle); return NULL; } @@ -245,7 +250,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port) /* create the socket */ handle = socket(so.sin.sin_family, SOCK_STREAM, 0); #ifdef HAVE_IPV6 - if (handle == -1 && errno == EINVAL) { + if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) { /* IPv6 is not supported by OS */ so.sin.sin_family = AF_INET; so.sin.sin_addr.s_addr = INADDR_ANY; @@ -373,9 +378,8 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6) { #ifdef HAVE_IPV6 union sockaddr_union *so; - struct addrinfo hints, *ai, *origai; - char hbuf[NI_MAXHOST]; - int host_error, count; + struct addrinfo hints, *ai, *ailist; + int ret, count; #else struct hostent *hp; #endif @@ -390,16 +394,12 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6) hints.ai_socktype = SOCK_STREAM; /* save error to host_error for later use */ - host_error = getaddrinfo(addr, NULL, &hints, &ai); - if (host_error != 0) - return host_error; + ret = getaddrinfo(addr, NULL, &hints, &ailist); + if (ret != 0) + return ret; - if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, - sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) - return 1; - - origai = ai; count = 0; - while (ai != NULL && count < 2) { + count = 0; + for (ai = ailist; ai != NULL && count < 2; ai = ai->ai_next) { so = (union sockaddr_union *) ai->ai_addr; if (ai->ai_family == AF_INET6 && ip6->family == 0) { @@ -409,18 +409,20 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6) sin_get_ip(so, ip4); count++; } - ai = ai->ai_next; } - freeaddrinfo(origai); + freeaddrinfo(ailist); + return count > 0 ? 0 : 1; #else hp = gethostbyname(addr); - if (hp == NULL) return h_errno; + if (hp == NULL) + return -1; + //return h_errno; ip4->family = AF_INET; memcpy(&ip4->ip, hp->h_addr, 4); -#endif return 0; +#endif } /* Get name for host, *name should be g_free()'d unless it's NULL. @@ -577,7 +579,7 @@ char *net_getservbyport(int port) int is_ipv4_address(const char *host) { while (*host != '\0') { - if (*host != '.' && !isdigit(*host)) + if (*host != '.' && !i_isdigit(*host)) return 0; host++; } diff --git a/apps/irssi/src/core/nick-rec.h b/apps/irssi/src/core/nick-rec.h index c4cd8bfd..d0dbce3d 100644 --- a/apps/irssi/src/core/nick-rec.h +++ b/apps/irssi/src/core/nick-rec.h @@ -20,6 +20,8 @@ unsigned int op:1; unsigned int halfop:1; unsigned int voice:1; +/*GHashTable *module_data;*/ + void *unique_id; /* unique ID to use for comparing if one nick is in another channels, or NULL = nicks are unique, just keep comparing them. */ NICK_REC *next; /* support for multiple identically named nicks */ diff --git a/apps/irssi/src/core/nicklist.c b/apps/irssi/src/core/nicklist.c index 96f6a8ea..b7e85407 100644 --- a/apps/irssi/src/core/nicklist.c +++ b/apps/irssi/src/core/nicklist.c @@ -28,7 +28,7 @@ #include "masks.h" #define isalnumhigh(a) \ - (isalnum(a) || (unsigned char) (a) >= 128) + (i_isalnum(a) || (unsigned char) (a) >= 128) static void nick_hash_add(CHANNEL_REC *channel, NICK_REC *nick) { @@ -76,6 +76,8 @@ static void nick_hash_remove(CHANNEL_REC *channel, NICK_REC *nick) /* Add new nick to list */ void nicklist_insert(CHANNEL_REC *channel, NICK_REC *nick) { + /*MODULE_DATA_INIT(nick);*/ + nick->type = module_get_uniq_id("NICK", 0); nick->chat_type = channel->chat_type; @@ -100,6 +102,7 @@ static void nicklist_destroy(CHANNEL_REC *channel, NICK_REC *nick) { signal_emit("nicklist remove", 2, channel, nick); + /*MODULE_DATA_DEINIT(nick);*/ g_free(nick->nick); g_free_not_null(nick->realname); g_free_not_null(nick->host); @@ -353,17 +356,39 @@ GSList *nicklist_get_same_unique(SERVER_REC *server, void *id) /* nick record comparision for sort functions */ int nicklist_compare(NICK_REC *p1, NICK_REC *p2) { + int status1, status2; + if (p1 == NULL) return -1; if (p2 == NULL) return 1; - if (p1->op && !p2->op) return -1; - if (!p1->op && p2->op) return 1; - - if (!p1->op) { - if (p1->voice && !p2->voice) return -1; - if (!p1->voice && p2->voice) return 1; - } - + /* we assign each status (op, halfop, voice, normal) a number + * and compare them. this is easier than 100,000 if's and + * returns :-) + * -- yath */ + + if (p1->op) + status1 = 4; + else if (p1->halfop) + status1 = 3; + else if (p1->voice) + status1 = 2; + else + status1 = 1; + + if (p2->op) + status2 = 4; + else if (p2->halfop) + status2 = 3; + else if (p2->voice) + status2 = 2; + else + status2 = 1; + + if (status1 < status2) + return 1; + else if (status1 > status2) + return -1; + return g_strcasecmp(p1->nick, p2->nick); } @@ -507,10 +532,10 @@ int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick) /* check if it matches for alphanumeric parts of nick */ while (*nick != '\0' && *msg != '\0') { - if (toupper(*nick) == toupper(*msg)) { + if (i_toupper(*nick) == i_toupper(*msg)) { /* total match */ msg++; - } else if (isalnum(*msg) && !isalnum(*nick)) { + } else if (i_isalnum(*msg) && !i_isalnum(*nick)) { /* some strange char in your nick, pass it */ fullmatch = FALSE; } else @@ -527,7 +552,7 @@ int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick) /* remove the rest of the non-alphanum chars from nick and check if it then matches. */ fullmatch = FALSE; - while (*nick != '\0' && !isalnum(*nick)) + while (*nick != '\0' && !i_isalnum(*nick)) nick++; } diff --git a/apps/irssi/src/core/pidwait.c b/apps/irssi/src/core/pidwait.c index f54aa34d..bb48d0d0 100644 --- a/apps/irssi/src/core/pidwait.c +++ b/apps/irssi/src/core/pidwait.c @@ -41,6 +41,13 @@ void pidwait_remove(int pid) pids = g_slist_remove(pids, GINT_TO_POINTER(pid)); } +/* return list of pids that are being waited. + don't free the return value. */ +GSList *pidwait_get_pids(void) +{ + return pids; +} + static int child_check(void) { GSList *tmp, *next; diff --git a/apps/irssi/src/core/pidwait.h b/apps/irssi/src/core/pidwait.h index 3f6b84cd..ae1ba01e 100644 --- a/apps/irssi/src/core/pidwait.h +++ b/apps/irssi/src/core/pidwait.h @@ -9,4 +9,8 @@ void pidwait_add(int pid); /* remove pid from wait list */ void pidwait_remove(int pid); +/* return list of pids that are being waited. + don't free the return value. */ +GSList *pidwait_get_pids(void); + #endif diff --git a/apps/irssi/src/core/queries.c b/apps/irssi/src/core/queries.c index d1c51352..cc16ad1e 100644 --- a/apps/irssi/src/core/queries.c +++ b/apps/irssi/src/core/queries.c @@ -36,6 +36,7 @@ void query_init(QUERY_REC *query, int automatic) MODULE_DATA_INIT(query); query->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY"); + query->destroy = (void (*) (WI_ITEM_REC *)) query_destroy; if (query->server_tag != NULL) { query->server = server_find_tag(query->server_tag); if (query->server != NULL) { @@ -66,6 +67,8 @@ void query_destroy(QUERY_REC *query) g_free_not_null(query->server_tag); g_free_not_null(query->address); g_free(query->name); + + query->type = 0; g_free(query); } diff --git a/apps/irssi/src/core/rawlog.c b/apps/irssi/src/core/rawlog.c index 4e47040c..d605ccb5 100644 --- a/apps/irssi/src/core/rawlog.c +++ b/apps/irssi/src/core/rawlog.c @@ -22,10 +22,13 @@ #include "rawlog.h" #include "modules.h" #include "signals.h" +#include "commands.h" #include "misc.h" #include "write-buffer.h" #include "settings.h" +#include "servers.h" + static int rawlog_lines; static int signal_rawlog; static int log_file_create_mode; @@ -158,6 +161,43 @@ static void read_settings(void) log_file_create_mode = octal2dec(settings_get_int("log_create_mode")); } +static void cmd_rawlog(const char *data, SERVER_REC *server, void *item) +{ + command_runsub("rawlog", data, server, item); +} + +/* SYNTAX: RAWLOG SAVE */ +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 */ +static void cmd_rawlog_open(const char *data, SERVER_REC *server) +{ + g_return_if_fail(data != NULL); + if (server == NULL || server->rawlog == NULL) + cmd_return_error(CMDERR_NOT_CONNECTED); + + if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + rawlog_open(server->rawlog, data); +} + +/* SYNTAX: RAWLOG CLOSE */ +static void cmd_rawlog_close(const char *data, SERVER_REC *server) +{ + g_return_if_fail(data != NULL); + if (server == NULL || server->rawlog == NULL) + cmd_return_error(CMDERR_NOT_CONNECTED); + + rawlog_close(server->rawlog); +} + void rawlog_init(void) { signal_rawlog = signal_get_uniq_id("rawlog"); @@ -166,9 +206,19 @@ void rawlog_init(void) read_settings(); signal_add("setup changed", (SIGNAL_FUNC) read_settings); + + command_bind("rawlog", NULL, (SIGNAL_FUNC) cmd_rawlog); + command_bind("rawlog save", NULL, (SIGNAL_FUNC) cmd_rawlog_save); + command_bind("rawlog open", NULL, (SIGNAL_FUNC) cmd_rawlog_open); + command_bind("rawlog close", NULL, (SIGNAL_FUNC) cmd_rawlog_close); } void rawlog_deinit(void) { signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + command_unbind("rawlog", (SIGNAL_FUNC) cmd_rawlog); + command_unbind("rawlog save", (SIGNAL_FUNC) cmd_rawlog_save); + command_unbind("rawlog open", (SIGNAL_FUNC) cmd_rawlog_open); + command_unbind("rawlog close", (SIGNAL_FUNC) cmd_rawlog_close); } diff --git a/apps/irssi/src/core/server-connect-rec.h b/apps/irssi/src/core/server-connect-rec.h index f1b3d075..a59880e4 100644 --- a/apps/irssi/src/core/server-connect-rec.h +++ b/apps/irssi/src/core/server-connect-rec.h @@ -3,12 +3,15 @@ int type; /* module_get_uniq_id("SERVER CONNECT", 0) */ int chat_type; /* chat_protocol_lookup(xx) */ +int refcount; + /* if we're connecting via proxy, or just NULLs */ char *proxy; int proxy_port; -char *proxy_string, *proxy_password; +char *proxy_string, *proxy_string_after, *proxy_password; unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */ +char *tag; /* try to keep this tag when connected to server */ char *address; int port; char *chatnet; @@ -22,5 +25,6 @@ char *realname; /* when reconnecting, the old server status */ unsigned int reconnection:1; /* we're trying to reconnect */ +unsigned int no_autojoin_channels:1; /* don't autojoin any channels */ char *channels; char *away_reason; diff --git a/apps/irssi/src/core/server-rec.h b/apps/irssi/src/core/server-rec.h index 7cdc66e5..0003f698 100644 --- a/apps/irssi/src/core/server-rec.h +++ b/apps/irssi/src/core/server-rec.h @@ -3,6 +3,8 @@ int type; /* module_get_uniq_id("SERVER", 0) */ int chat_type; /* chat_protocol_lookup(xx) */ +int refcount; + STRUCT_SERVER_CONNECT_REC *connrec; time_t connect_time; /* connection time */ time_t real_connect_time; /* time when server replied that we really are connected */ @@ -10,8 +12,11 @@ time_t real_connect_time; /* time when server replied that we really are connect char *tag; /* tag name for addressing server */ char *nick; /* current nick */ -unsigned int connected:1; /* connected to server */ +unsigned int connected:1; /* Connected to server */ +unsigned int disconnected:1; /* Disconnected, waiting for refcount to drop zero */ unsigned int connection_lost:1; /* Connection lost unintentionally */ +unsigned int session_reconnect:1; /* Connected to this server with /UPGRADE */ +unsigned int no_reconnect:1; /* Don't reconnect to server */ NET_SENDBUF_REC *handle; int readtag; /* input tag */ @@ -21,11 +26,6 @@ GIOChannel *connect_pipe[2]; int connect_tag; int connect_pid; -/* For deciding if event should be handled internally */ -GHashTable *eventtable; /* "event xxx" : GSList* of REDIRECT_RECs */ -GHashTable *eventgrouptable; /* event group : GSList* of REDIRECT_RECs */ -GHashTable *cmdtable; /* "command xxx" : REDIRECT_CMD_REC* */ - RAWLOG_REC *rawlog; LINEBUF_REC *buffer; /* receive buffer */ GHashTable *module_data; @@ -38,7 +38,7 @@ unsigned int usermode_away:1; unsigned int banned:1; /* not allowed to connect to this server */ unsigned int dns_error:1; /* DNS said the host doesn't exist */ -time_t lag_sent; /* 0 or time when last lag query was sent to server */ +GTimeVal lag_sent; /* 0 or time when last lag query was sent to server */ time_t lag_last_check; /* last time we checked lag */ int lag; /* server lag in milliseconds */ @@ -55,12 +55,13 @@ void (*channels_join)(SERVER_REC *server, const char *data, int automatic); /* returns true if `flag' indicates a nick flag (op/voice/halfop) */ int (*isnickflag)(char flag); /* returns true if `data' indicates a channel */ -int (*ischannel)(const char *data); +int (*ischannel)(SERVER_REC *server, const char *data); /* returns all nick flag characters in order op, voice, halfop. If some of them aren't supported '\0' can be used. */ const char *(*get_nick_flags)(void); /* send public or private message to server */ -void (*send_message)(SERVER_REC *server, const char *target, const char *msg); +void (*send_message)(SERVER_REC *server, const char *target, + const char *msg, int target_type); /* -- Default implementations are used if NULL -- */ CHANNEL_REC *(*channel_find_func)(SERVER_REC *server, const char *name); diff --git a/apps/irssi/src/core/server-setup-rec.h b/apps/irssi/src/core/server-setup-rec.h index 4352bef7..f94ec9ae 100644 --- a/apps/irssi/src/core/server-setup-rec.h +++ b/apps/irssi/src/core/server-setup-rec.h @@ -14,6 +14,7 @@ IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */ time_t last_connect; /* to avoid reconnecting too fast.. */ unsigned int autoconnect:1; +unsigned int no_proxy:1; unsigned int last_failed:1; /* if last connection attempt failed */ unsigned int banned:1; /* if we're banned from this server */ unsigned int dns_error:1; /* DNS said the host doesn't exist */ diff --git a/apps/irssi/src/core/servers-reconnect.c b/apps/irssi/src/core/servers-reconnect.c index a9491f92..e51a3fa2 100644 --- a/apps/irssi/src/core/servers-reconnect.c +++ b/apps/irssi/src/core/servers-reconnect.c @@ -37,6 +37,9 @@ static int reconnect_time; void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server) { + g_free_not_null(conn->tag); + conn->tag = g_strdup(server->tag); + g_free_not_null(conn->away_reason); conn->away_reason = !server->usermode_away ? NULL : g_strdup(server->away_reason); @@ -53,20 +56,22 @@ static void server_reconnect_add(SERVER_CONNECT_REC *conn, rec = g_new(RECONNECT_REC, 1); rec->tag = ++last_reconnect_tag; - rec->conn = conn; rec->next_connect = next_connect; + rec->conn = conn; + server_connect_ref(conn); + reconnects = g_slist_append(reconnects, rec); } -void server_reconnect_destroy(RECONNECT_REC *rec, int free_conn) +void server_reconnect_destroy(RECONNECT_REC *rec) { g_return_if_fail(rec != NULL); reconnects = g_slist_remove(reconnects, rec); signal_emit("server reconnect remove", 1, rec); - if (free_conn) server_connect_free(rec->conn); + server_connect_unref(rec->conn); g_free(rec); if (reconnects == NULL) @@ -92,8 +97,10 @@ static int server_reconnect_timeout(void) if (rec->next_connect <= now) { conn = rec->conn; - server_reconnect_destroy(rec, FALSE); + server_connect_ref(conn); + server_reconnect_destroy(rec); CHAT_PROTOCOL(conn)->server_connect(conn); + server_connect_unref(conn); } } @@ -108,13 +115,8 @@ static void sserver_connect(SERVER_SETUP_REC *rec, SERVER_CONNECT_REC *conn) if (conn->port == 0) conn->port = rec->port; server_setup_fill_reconn(conn, rec); - if (rec->last_connect > time(NULL)-reconnect_time) { - /* can't reconnect this fast, wait.. */ - server_reconnect_add(conn, rec->last_connect+reconnect_time); - } else { - /* connect to server.. */ - CHAT_PROTOCOL(conn)->server_connect(conn); - } + server_reconnect_add(conn, rec->last_connect+reconnect_time); + server_connect_unref(conn); } static SERVER_CONNECT_REC * @@ -126,13 +128,17 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) signal_emit("server connect copy", 2, &dest, src); g_return_val_if_fail(dest != NULL, NULL); + server_connect_ref(dest); dest->type = module_get_uniq_id("SERVER CONNECT", 0); dest->reconnection = src->reconnection; dest->proxy = g_strdup(src->proxy); dest->proxy_port = src->proxy_port; dest->proxy_string = g_strdup(src->proxy_string); + dest->proxy_string_after = g_strdup(src->proxy_string_after); dest->proxy_password = g_strdup(src->proxy_password); + dest->tag = g_strdup(src->tag); + if (connect_info) { dest->family = src->family; dest->address = g_strdup(src->address); @@ -161,8 +167,9 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) } #define server_should_reconnect(server) \ - ((server)->connection_lost && ((server)->connrec->chatnet != NULL || \ - (!(server)->banned && !(server)->dns_error))) + ((server)->connection_lost && !(server)->no_reconnect && \ + ((server)->connrec->chatnet != NULL || \ + (!(server)->banned && !(server)->dns_error))) #define sserver_connect_ok(rec, net) \ (!(rec)->banned && !(rec)->dns_error && (rec)->chatnet != NULL && \ @@ -192,7 +199,8 @@ static void sig_reconnect(SERVER_REC *server) } sserver = server_setup_find(server->connrec->address, - server->connrec->port); + server->connrec->port, + server->connrec->chatnet); if (sserver != NULL) { /* save the last connection time/status */ @@ -210,21 +218,14 @@ static void sig_reconnect(SERVER_REC *server) conn->port = server->connrec->port; conn->password = g_strdup(server->connrec->password); - if (server->connect_time != 0 && - time(NULL)-server->connect_time > reconnect_time) { - /* there's been enough time since last connection, - reconnect back immediately */ - CHAT_PROTOCOL(conn)->server_connect(conn); - } else { - /* reconnect later.. */ - server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) : - server->connect_time) + reconnect_time); - } + server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) : + server->connect_time) + reconnect_time); + server_connect_unref(conn); return; } /* always try to first connect to the first on the list where we - haven't got unsuccessful connection attempts for the last half + haven't got unsuccessful connection attempts for the past half an hour. */ now = time(NULL); @@ -264,7 +265,7 @@ static void sig_reconnect(SERVER_REC *server) if (through) { /* shouldn't happen unless there's no servers in this chatnet in setup.. */ - server_connect_free(conn); + server_connect_unref(conn); break; } @@ -288,7 +289,7 @@ static void sig_connected(SERVER_REC *server) static void cmd_rmreconns(void) { while (reconnects != NULL) - server_reconnect_destroy(reconnects->data, TRUE); + server_reconnect_destroy(reconnects->data); } static RECONNECT_REC *reconnect_find_tag(int tag) @@ -319,7 +320,8 @@ static void reconnect_all(void) rec = reconnects->data; list = g_slist_append(list, rec->conn); - server_reconnect_destroy(rec, FALSE); + server_connect_ref(rec->conn); + server_reconnect_destroy(rec); } @@ -327,6 +329,7 @@ static void reconnect_all(void) conn = list->data; CHAT_PROTOCOL(conn)->server_connect(conn); + server_connect_unref(conn); list = g_slist_remove(list, conn); } } @@ -342,14 +345,13 @@ static void cmd_reconnect(const char *data, SERVER_REC *server) /* reconnect back to same server */ conn = server_connect_copy_skeleton(server->connrec, TRUE); - if (server->connected) { + if (server->connected) reconnect_save_status(conn, server); - signal_emit("command disconnect", 2, - "* Reconnecting", server); - } + signal_emit("command disconnect", 2, "* Reconnecting", server); conn->reconnection = TRUE; CHAT_PROTOCOL(conn)->server_connect(conn); + server_connect_unref(conn); return; } @@ -378,25 +380,25 @@ static void cmd_reconnect(const char *data, SERVER_REC *server) } conn = rec->conn; - server_reconnect_destroy(rec, FALSE); + server_connect_ref(conn); + server_reconnect_destroy(rec); CHAT_PROTOCOL(conn)->server_connect(conn); + server_connect_unref(conn); } static void cmd_disconnect(const char *data, SERVER_REC *server) { RECONNECT_REC *rec; - int tag; if (g_strncasecmp(data, "RECON-", 6) != 0) return; /* handle only reconnection removing */ - rec = sscanf(data+6, "%d", &tag) == 1 && tag > 0 ? - reconnect_find_tag(tag) : NULL; + rec = reconnect_find_tag(atoi(data+6)); if (rec == NULL) signal_emit("server reconnect not found", 1, data); else - server_reconnect_destroy(rec, TRUE); + server_reconnect_destroy(rec); signal_stop(); } @@ -409,7 +411,7 @@ static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto) next = tmp->next; if (rec->conn->chat_type == proto->id) - server_reconnect_destroy(rec, TRUE); + server_reconnect_destroy(rec); } } diff --git a/apps/irssi/src/core/servers-reconnect.h b/apps/irssi/src/core/servers-reconnect.h index b51486f6..835d58d5 100644 --- a/apps/irssi/src/core/servers-reconnect.h +++ b/apps/irssi/src/core/servers-reconnect.h @@ -6,7 +6,7 @@ #define FAILED_RECONNECT_WAIT (60*30) typedef struct { - int tag; + int tag; time_t next_connect; SERVER_CONNECT_REC *conn; @@ -15,7 +15,7 @@ typedef struct { extern GSList *reconnects; void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server); -void server_reconnect_destroy(RECONNECT_REC *rec, int free_conn); +void server_reconnect_destroy(RECONNECT_REC *rec); void servers_reconnect_init(void); void servers_reconnect_deinit(void); diff --git a/apps/irssi/src/core/servers-redirect.c b/apps/irssi/src/core/servers-redirect.c deleted file mode 100644 index ca340fc6..00000000 --- a/apps/irssi/src/core/servers-redirect.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - server-redirect.c : irssi - - Copyright (C) 1999-2000 Timo Sirainen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "module.h" -#include "signals.h" -#include "misc.h" - -#include "servers.h" -#include "servers-redirect.h" - -static int redirect_group; - -static void server_eventtable_destroy(char *key, GSList *value) -{ - GSList *tmp; - - g_free(key); - - for (tmp = value; tmp != NULL; tmp = tmp->next) { - REDIRECT_REC *rec = tmp->data; - - g_free_not_null(rec->arg); - g_free(rec->name); - g_free(rec); - } - g_slist_free(value); -} - -static void server_eventgrouptable_destroy(gpointer key, GSList *value) -{ - g_slist_foreach(value, (GFunc) g_free, NULL); - g_slist_free(value); -} - -static void server_cmdtable_destroy(char *key, REDIRECT_CMD_REC *value) -{ - g_free(key); - - g_slist_foreach(value->events, (GFunc) g_free, NULL); - g_slist_free(value->events); - g_free(value); -} - -static void sig_disconnected(SERVER_REC *server) -{ - g_return_if_fail(IS_SERVER(server)); - - if (server->eventtable != NULL) { - g_hash_table_foreach(server->eventtable, - (GHFunc) server_eventtable_destroy, NULL); - g_hash_table_destroy(server->eventtable); - } - - g_hash_table_foreach(server->eventgrouptable, - (GHFunc) server_eventgrouptable_destroy, NULL); - g_hash_table_destroy(server->eventgrouptable); - - if (server->cmdtable != NULL) { - g_hash_table_foreach(server->cmdtable, - (GHFunc) server_cmdtable_destroy, NULL); - g_hash_table_destroy(server->cmdtable); - } -} - -void server_redirect_initv(SERVER_REC *server, const char *command, - int last, GSList *list) -{ - REDIRECT_CMD_REC *rec; - - g_return_if_fail(IS_SERVER(server)); - g_return_if_fail(command != NULL); - g_return_if_fail(last > 0); - - if (g_hash_table_lookup(server->cmdtable, command) != NULL) { - /* already in hash table. list of events SHOULD be the same. */ - g_slist_foreach(list, (GFunc) g_free, NULL); - g_slist_free(list); - return; - } - - rec = g_new(REDIRECT_CMD_REC, 1); - rec->last = last; - rec->events = list; - g_hash_table_insert(server->cmdtable, g_strdup(command), rec); -} - -void server_redirect_init(SERVER_REC *server, const char *command, - int last, ...) -{ - va_list args; - GSList *list; - char *event; - - va_start(args, last); - list = NULL; - while ((event = va_arg(args, gchar *)) != NULL) - list = g_slist_append(list, g_strdup(event)); - va_end(args); - - server_redirect_initv(server, command, last, list); -} - -int server_redirect_single_event(SERVER_REC *server, const char *arg, - int last, int group, const char *event, - const char *signal, int argpos) -{ - REDIRECT_REC *rec; - GSList *list, *grouplist; - char *origkey; - - g_return_val_if_fail(IS_SERVER(server), 0); - g_return_val_if_fail(event != NULL, 0); - g_return_val_if_fail(signal != NULL, 0); - g_return_val_if_fail(arg != NULL || argpos == -1, 0); - - if (group == 0) group = ++redirect_group; - - rec = g_new0(REDIRECT_REC, 1); - rec->arg = arg == NULL ? NULL : g_strdup(arg); - rec->argpos = argpos; - rec->name = g_strdup(signal); - rec->group = group; - rec->last = last; - - if (g_hash_table_lookup_extended(server->eventtable, event, - (gpointer *) &origkey, - (gpointer *) &list)) { - g_hash_table_remove(server->eventtable, origkey); - } else { - list = NULL; - origkey = g_strdup(event); - } - - grouplist = g_hash_table_lookup(server->eventgrouptable, - GINT_TO_POINTER(group)); - if (grouplist != NULL) { - g_hash_table_remove(server->eventgrouptable, - GINT_TO_POINTER(group)); - } - - list = g_slist_append(list, rec); - grouplist = g_slist_append(grouplist, g_strdup(event)); - - g_hash_table_insert(server->eventtable, origkey, list); - g_hash_table_insert(server->eventgrouptable, - GINT_TO_POINTER(group), grouplist); - - return group; -} - -void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...) -{ - va_list args; - char *event, *signal; - int argpos, group; - - g_return_if_fail(IS_SERVER(server)); - - va_start(args, last); - - group = 0; - while ((event = va_arg(args, gchar *)) != NULL) { - signal = va_arg(args, gchar *); - argpos = va_arg(args, gint); - - group = server_redirect_single_event(server, arg, last > 0, - group, event, signal, - argpos); - last--; - } - - va_end(args); -} - -void server_redirect_default(SERVER_REC *server, const char *command) -{ - REDIRECT_CMD_REC *cmdrec; - REDIRECT_REC *rec; - GSList *events, *list, *grouplist; - char *event, *origkey; - int last; - - g_return_if_fail(IS_SERVER(server)); - g_return_if_fail(command != NULL); - - if (server->cmdtable == NULL) - return; /* not connected yet */ - - cmdrec = g_hash_table_lookup(server->cmdtable, command); - if (cmdrec == NULL) return; - - /* add all events used by command to eventtable and eventgrouptable */ - redirect_group++; grouplist = NULL; last = cmdrec->last; - for (events = cmdrec->events; events != NULL; events = events->next) { - event = events->data; - - if (g_hash_table_lookup_extended(server->eventtable, event, - (gpointer *) &origkey, - (gpointer *) &list)) { - g_hash_table_remove(server->eventtable, origkey); - } else { - list = NULL; - origkey = g_strdup(event); - } - - rec = g_new0(REDIRECT_REC, 1); - rec->argpos = -1; - rec->name = g_strdup(event); - rec->group = redirect_group; - rec->last = last > 0; - - grouplist = g_slist_append(grouplist, g_strdup(event)); - list = g_slist_append(list, rec); - g_hash_table_insert(server->eventtable, origkey, list); - - last--; - } - - g_hash_table_insert(server->eventgrouptable, - GINT_TO_POINTER(redirect_group), grouplist); -} - -void server_redirect_remove_next(SERVER_REC *server, const char *event, - GSList *item) -{ - REDIRECT_REC *rec; - GSList *grouplist, *list, *events, *tmp; - char *origkey; - int group; - - g_return_if_fail(IS_SERVER(server)); - g_return_if_fail(event != NULL); - - if (!g_hash_table_lookup_extended(server->eventtable, event, - (gpointer *) &origkey, - (gpointer *) &list)) - return; - - rec = item == NULL ? list->data : item->data; - if (!rec->last) { - /* this wasn't last expected event */ - return; - } - group = rec->group; - - /* get list of events from this group */ - grouplist = g_hash_table_lookup(server->eventgrouptable, - GINT_TO_POINTER(group)); - - /* remove all of them */ - for (list = grouplist; list != NULL; list = list->next) { - char *event = list->data; - - if (!g_hash_table_lookup_extended(server->eventtable, event, - (gpointer *) &origkey, - (gpointer *) &events)) { - g_warning("server_redirect_remove_next() : " - "event in eventgrouptable but not in " - "eventtable"); - continue; - } - - /* remove the right group */ - for (tmp = events; tmp != NULL; tmp = tmp->next) { - rec = tmp->data; - - if (rec->group == group) - break; - } - - if (rec == NULL) { - g_warning("server_redirect_remove_next() : " - "event in eventgrouptable but not in " - "eventtable (group)"); - continue; - } - - g_free(event); - - events = g_slist_remove(events, rec); - g_free_not_null(rec->arg); - g_free(rec->name); - g_free(rec); - - /* update hash table */ - g_hash_table_remove(server->eventtable, origkey); - if (events == NULL) - g_free(origkey); - else { - g_hash_table_insert(server->eventtable, - origkey, events); - } - } - - g_hash_table_remove(server->eventgrouptable, GINT_TO_POINTER(group)); - g_slist_free(grouplist); -} - -GSList *server_redirect_getqueue(SERVER_REC *server, const char *event, - const char *args) -{ - REDIRECT_REC *rec; - GSList *list; - char **arglist; - int found; - - g_return_val_if_fail(IS_SERVER(server), NULL); - g_return_val_if_fail(event != NULL, NULL); - - list = g_hash_table_lookup(server->eventtable, event); - - for (; list != NULL; list = list->next) { - rec = list->data; - if (rec->argpos == -1) - break; - - if (rec->arg == NULL || args == NULL) - continue; - - /* we need to check that the argument is right.. */ - arglist = g_strsplit(args, " ", -1); - found = (strarray_length(arglist) > rec->argpos && - find_substr(rec->arg, arglist[rec->argpos])); - g_strfreev(arglist); - - if (found) break; - } - - return list; -} - -void servers_redirect_init(void) -{ - redirect_group = 0; - - signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected); -} - -void servers_redirect_deinit(void) -{ - signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected); -} diff --git a/apps/irssi/src/core/servers-redirect.h b/apps/irssi/src/core/servers-redirect.h deleted file mode 100644 index c9e45ce3..00000000 --- a/apps/irssi/src/core/servers-redirect.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef __SERVERS_REDIRECT_H -#define __SERVERS_REDIRECT_H - -typedef struct { - int last; /* number of "last" events at the start of the events list */ - GSList *events; /* char* list of events */ -} REDIRECT_CMD_REC; - -typedef struct { - char *name; /* event name */ - - char *arg; /* argument for event we are expecting or NULL */ - int argpos; /* argument position */ - - int group; /* group of events this belongs to */ - int last; /* if this event is received, remove all the events in this group */ -} -REDIRECT_REC; - -void server_redirect_init(SERVER_REC *server, const char *command, int last, ...); -void server_redirect_initv(SERVER_REC *server, const char *command, int last, GSList *list); -/* ... = char *event1, char *event2, ..., NULL */ - -void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...); -/* ... = char *event, char *callback_signal, int argpos, ..., NULL */ - -int server_redirect_single_event(SERVER_REC *server, const char *arg, int last, int group, - const char *event, const char *signal, int argpos); -void server_redirect_default(SERVER_REC *server, const char *command); -void server_redirect_remove_next(SERVER_REC *server, const char *event, GSList *item); -GSList *server_redirect_getqueue(SERVER_REC *server, const char *event, const char *args); - -void servers_redirect_init(void); -void servers_redirect_deinit(void); - -#endif diff --git a/apps/irssi/src/core/servers-setup.c b/apps/irssi/src/core/servers-setup.c index deaf3cc9..99f9f579 100644 --- a/apps/irssi/src/core/servers-setup.c +++ b/apps/irssi/src/core/servers-setup.c @@ -131,6 +131,7 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn, conn->proxy = g_strdup(settings_get_str("proxy_address")); conn->proxy_port = settings_get_int("proxy_port"); conn->proxy_string = g_strdup(settings_get_str("proxy_string")); + conn->proxy_string_after = g_strdup(settings_get_str("proxy_string_after")); conn->proxy_password = g_strdup(settings_get_str("proxy_password")); } @@ -143,6 +144,8 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn, conn->own_ip6 = g_new(IPADDR, 1); memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR)); } + + signal_emit("server setup fill connect", 1, conn); } static void server_setup_fill_server(SERVER_CONNECT_REC *conn, @@ -153,6 +156,9 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn, sserver->last_connect = time(NULL); + if (sserver->no_proxy) + g_free_and_null(conn->proxy); + if (sserver->family != 0 && conn->family == 0) conn->family = sserver->family; if (sserver->port > 0 && conn->port <= 0) @@ -168,15 +174,15 @@ static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn, g_return_if_fail(IS_SERVER_CONNECT(conn)); g_return_if_fail(IS_CHATNET(chatnet)); - if (chatnet->nick) { + if (chatnet->nick != NULL) { g_free(conn->nick); conn->nick = g_strdup(chatnet->nick);; } - if (chatnet->username) { + if (chatnet->username != NULL) { g_free(conn->username); conn->username = g_strdup(chatnet->username);; } - if (chatnet->realname) { + if (chatnet->realname != NULL) { g_free(conn->realname); conn->realname = g_strdup(chatnet->realname);; } @@ -200,7 +206,7 @@ create_addr_conn(int chat_type, const char *address, int port, g_return_val_if_fail(address != NULL, NULL); - sserver = server_setup_find(address, port); + sserver = server_setup_find(address, port, chatnet); if (sserver != NULL) { if (chat_type < 0) chat_type = sserver->chat_type; @@ -212,6 +218,8 @@ create_addr_conn(int chat_type, const char *address, int port, chat_protocol_get_default(); conn = proto->create_server_connect(); + server_connect_ref(conn); + conn->chat_type = proto->id; if (chatnet != NULL && *chatnet != '\0') conn->chatnet = g_strdup(chatnet); @@ -240,7 +248,6 @@ create_addr_conn(int chat_type, const char *address, int port, conn->nick = g_strdup(nick); } - signal_emit("server setup fill connect", 1, conn); return conn; } @@ -288,22 +295,29 @@ server_create_conn(int chat_type, const char *dest, int port, const char *nick) { SERVER_CONNECT_REC *rec; + CHATNET_REC *chatrec; g_return_val_if_fail(dest != NULL, NULL); - if (chatnet_find(dest) != NULL) { - rec = create_chatnet_conn(dest, port, password, nick); + chatrec = chatnet_find(dest); + if (chatrec != NULL) { + rec = create_chatnet_conn(chatrec->name, port, password, nick); if (rec != NULL) return rec; } + chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet); + if (chatrec != NULL) + chatnet = chatrec->name; + return create_addr_conn(chat_type, dest, port, chatnet, password, nick); } /* Find matching server from setup. Try to find record with a same port, but fallback to any server with the same address. */ -SERVER_SETUP_REC *server_setup_find(const char *address, int port) +SERVER_SETUP_REC *server_setup_find(const char *address, int port, + const char *chatnet) { SERVER_SETUP_REC *server; GSList *tmp; @@ -314,7 +328,9 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port) for (tmp = setupservers; tmp != NULL; tmp = tmp->next) { SERVER_SETUP_REC *rec = tmp->data; - if (g_strcasecmp(rec->address, address) == 0) { + if (g_strcasecmp(rec->address, address) == 0 && + (chatnet == NULL || rec->chatnet == NULL || + g_strcasecmp(rec->chatnet, chatnet) == 0)) { server = rec; if (rec->port == port) break; @@ -329,7 +345,7 @@ SERVER_SETUP_REC *server_setup_find_port(const char *address, int port) { SERVER_SETUP_REC *rec; - rec = server_setup_find(address, port); + rec = server_setup_find(address, port, NULL); return rec == NULL || rec->port != port ? NULL : rec; } @@ -355,14 +371,6 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) rec = NULL; chatnet = config_node_get_str(node, "chatnet", NULL); - if (chatnet == NULL) /* FIXME: remove this after .98... */ { - chatnet = config_node_get_str(node, "ircnet", NULL); - if (chatnet != NULL) { - iconfig_node_set_str(node, "chatnet", chatnet); - iconfig_node_set_str(node, "ircnet", NULL); - chatnet = config_node_get_str(node, "chatnet", NULL); - } - } chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet); if (chatnetrec == NULL && chatnet != NULL) { @@ -385,6 +393,7 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) rec->password = g_strdup(config_node_get_str(node, "password", NULL)); rec->port = port; rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE); + rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE); rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL)); signal_emit("server setup read", 2, rec, node); @@ -419,6 +428,8 @@ static void server_setup_save(SERVER_SETUP_REC *rec) if (rec->autoconnect) iconfig_node_set_bool(node, "autoconnect", TRUE); + if (rec->no_proxy) + iconfig_node_set_bool(node, "no_proxy", TRUE); signal_emit("server setup saved", 2, rec, node); } @@ -474,7 +485,8 @@ static void read_servers(void) /* Read servers */ node = iconfig_node_traverse("servers", FALSE); if (node != NULL) { - for (tmp = node->value; tmp != NULL; tmp = tmp->next) + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) server_setup_read(tmp->data); } } @@ -503,6 +515,7 @@ void servers_setup_init(void) settings_add_str("proxy", "proxy_address", ""); settings_add_int("proxy", "proxy_port", 6667); settings_add_str("proxy", "proxy_string", "CONNECT %s %d"); + settings_add_str("proxy", "proxy_string_after", ""); settings_add_str("proxy", "proxy_password", ""); setupservers = NULL; diff --git a/apps/irssi/src/core/servers-setup.h b/apps/irssi/src/core/servers-setup.h index d0807d11..d0101bcb 100644 --- a/apps/irssi/src/core/servers-setup.h +++ b/apps/irssi/src/core/servers-setup.h @@ -33,7 +33,8 @@ server_create_conn(int chat_type, const char *dest, int port, /* Find matching server from setup. Try to find record with a same port, but fallback to any server with the same address. */ -SERVER_SETUP_REC *server_setup_find(const char *address, int port); +SERVER_SETUP_REC *server_setup_find(const char *address, int port, + const char *chatnet); /* Find matching server from setup. Ports must match or NULL is returned. */ SERVER_SETUP_REC *server_setup_find_port(const char *address, int port); diff --git a/apps/irssi/src/core/servers.c b/apps/irssi/src/core/servers.c index f74e7f58..499f1f91 100644 --- a/apps/irssi/src/core/servers.c +++ b/apps/irssi/src/core/servers.c @@ -31,7 +31,6 @@ #include "chat-protocols.h" #include "servers.h" #include "servers-reconnect.h" -#include "servers-redirect.h" #include "servers-setup.h" #include "channels.h" #include "queries.h" @@ -46,23 +45,26 @@ void server_connect_failed(SERVER_REC *server, const char *msg) lookup_servers = g_slist_remove(lookup_servers, server); signal_emit("server connect failed", 2, server, msg); - if (server->connect_tag != -1) + + if (server->connect_tag != -1) { g_source_remove(server->connect_tag); - if (server->handle != NULL) + server->connect_tag = -1; + } + if (server->handle != NULL) { net_sendbuffer_destroy(server->handle, TRUE); + server->handle = NULL; + } if (server->connect_pipe[0] != NULL) { g_io_channel_close(server->connect_pipe[0]); g_io_channel_unref(server->connect_pipe[0]); g_io_channel_close(server->connect_pipe[1]); g_io_channel_unref(server->connect_pipe[1]); + server->connect_pipe[0] = NULL; + server->connect_pipe[1] = NULL; } - MODULE_DATA_DEINIT(server); - server_connect_free(server->connrec); - g_free_not_null(server->nick); - g_free(server->tag); - g_free(server); + server_unref(server); } /* generate tag from server's address */ @@ -101,12 +103,24 @@ static char *server_create_tag(SERVER_CONNECT_REC *conn) char *tag; int num; - g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL); + g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL); tag = conn->chatnet != NULL && *conn->chatnet != '\0' ? g_strdup(conn->chatnet) : server_create_address_tag(conn->address); + if (conn->tag != NULL && server_find_tag(conn->tag) == NULL && + strncmp(conn->tag, tag, strlen(tag)) == 0) { + /* use the existing tag if it begins with the same ID - + this is useful when you have several connections to + same server and you want to keep the same tags with + the servers (or it would cause problems when rejoining + /LAYOUT SAVEd channels). */ + g_free(tag); + return g_strdup(conn->tag); + } + + /* then just append numbers after tag until unused is found.. */ str = g_string_new(tag); for (num = 2; server_find_tag(str->str) != NULL; num++) @@ -122,11 +136,6 @@ static char *server_create_tag(SERVER_CONNECT_REC *conn) void server_connect_finished(SERVER_REC *server) { server->connect_time = time(NULL); - server->rawlog = rawlog_create(); - - server->eventtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); - server->eventgrouptable = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal); - server->cmdtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); servers = g_slist_append(servers, server); signal_emit("server connected", 1, server); @@ -189,10 +198,15 @@ static void server_connect_callback_readpipe(SERVER_REC *server) own_ip = ip == NULL ? NULL : (IPADDR_IS_V6(ip) ? conn->own_ip6 : conn->own_ip4); - if (ip != NULL) + handle = NULL; + if (ip != NULL) { signal_emit("server connecting", 2, server, ip); + if (server->handle == NULL) + handle = net_connect_ip(ip, port, own_ip); + else + handle = net_sendbuffer_handle(server->handle); + } - handle = ip == NULL ? NULL : net_connect_ip(ip, port, own_ip); if (handle == NULL) { /* failed */ if (iprec.error != 0 && net_hosterror_notfound(iprec.error)) { @@ -215,7 +229,8 @@ static void server_connect_callback_readpipe(SERVER_REC *server) return; } - server->handle = net_sendbuffer_create(handle, 0); + if (server->handle == NULL) + server->handle = net_sendbuffer_create(handle, 0); server->connect_tag = g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ, (GInputFunction) server_connect_callback_init, @@ -229,6 +244,7 @@ void server_connect_init(SERVER_REC *server) MODULE_DATA_INIT(server); server->type = module_get_uniq_id("SERVER", 0); + server_ref(server); server->nick = g_strdup(server->connrec->nick); if (server->connrec->username == NULL || *server->connrec->username == '\0') { @@ -279,6 +295,7 @@ int server_start_connect(SERVER_REC *server) g_input_add(server->connect_pipe[0], G_INPUT_READ, (GInputFunction) server_connect_callback_readpipe, server); + server->rawlog = rawlog_create(); lookup_servers = g_slist_append(lookup_servers, server); @@ -317,6 +334,9 @@ void server_disconnect(SERVER_REC *server) g_return_if_fail(IS_SERVER(server)); + if (server->disconnected) + return; + if (server->connect_tag != -1) { /* still connecting to server.. */ if (server->connect_pid != -1) @@ -327,6 +347,7 @@ void server_disconnect(SERVER_REC *server) servers = g_slist_remove(servers, server); + server->disconnected = TRUE; signal_emit("server disconnected", 1, server); /* close all channels */ @@ -345,18 +366,46 @@ void server_disconnect(SERVER_REC *server) server->handle = NULL; } - if (server->readtag > 0) + if (server->readtag > 0) { g_source_remove(server->readtag); + server->readtag = -1; + } + + server_unref(server); +} + +void server_ref(SERVER_REC *server) +{ + g_return_if_fail(IS_SERVER(server)); + + server->refcount++; +} + +int server_unref(SERVER_REC *server) +{ + g_return_val_if_fail(IS_SERVER(server), FALSE); + + if (--server->refcount > 0) + return TRUE; + + if (g_slist_find(servers, server) != NULL) { + g_warning("Non-referenced server wasn't disconnected"); + server_disconnect(server); + return TRUE; + } MODULE_DATA_DEINIT(server); - server_connect_free(server->connrec); - rawlog_destroy(server->rawlog); - line_split_free(server->buffer); - g_free_not_null(server->version); - g_free_not_null(server->away_reason); + server_connect_unref(server->connrec); + if (server->rawlog != NULL) rawlog_destroy(server->rawlog); + if (server->buffer != NULL) line_split_free(server->buffer); + g_free(server->version); + g_free(server->away_reason); g_free(server->nick); g_free(server->tag); + + server->type = 0; g_free(server); + return FALSE; } SERVER_REC *server_find_tag(const char *tag) @@ -401,15 +450,30 @@ SERVER_REC *server_find_chatnet(const char *chatnet) return NULL; } -void server_connect_free(SERVER_CONNECT_REC *conn) +void server_connect_ref(SERVER_CONNECT_REC *conn) +{ + conn->refcount++; +} + +void server_connect_unref(SERVER_CONNECT_REC *conn) { g_return_if_fail(IS_SERVER_CONNECT(conn)); - signal_emit("server connect free", 1, conn); - g_free_not_null(conn->proxy); + if (--conn->refcount > 0) + return; + if (conn->refcount < 0) { + g_warning("Connection '%s' refcount = %d", + conn->tag, conn->refcount); + } + + CHAT_PROTOCOL(conn)->destroy_server_connect(conn); + + g_free_not_null(conn->proxy); g_free_not_null(conn->proxy_string); + g_free_not_null(conn->proxy_string_after); g_free_not_null(conn->proxy_password); + g_free_not_null(conn->tag); g_free_not_null(conn->address); g_free_not_null(conn->chatnet); @@ -423,14 +487,14 @@ void server_connect_free(SERVER_CONNECT_REC *conn) g_free_not_null(conn->channels); g_free_not_null(conn->away_reason); - g_free(conn); + + conn->type = 0; + g_free(conn); } void server_change_nick(SERVER_REC *server, const char *nick) { - g_free(server->connrec->nick); g_free(server->nick); - server->connrec->nick = g_strdup(nick); server->nick = g_strdup(nick); signal_emit("server nick changed", 1, server); @@ -527,7 +591,6 @@ void servers_init(void) signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit); servers_reconnect_init(); - servers_redirect_init(); servers_setup_init(); } @@ -536,7 +599,6 @@ void servers_deinit(void) signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit); servers_setup_deinit(); - servers_redirect_deinit(); servers_reconnect_deinit(); module_uniq_destroy("SERVER"); diff --git a/apps/irssi/src/core/servers.h b/apps/irssi/src/core/servers.h index 75e4cbf0..dddde263 100644 --- a/apps/irssi/src/core/servers.h +++ b/apps/irssi/src/core/servers.h @@ -17,6 +17,9 @@ #define IS_SERVER_CONNECT(conn) \ (SERVER_CONNECT(conn) ? TRUE : FALSE) +#define server_ischannel(server, channel) \ + (server)->ischannel(server, channel) + /* all strings should be either NULL or dynamically allocated */ /* address and nick are mandatory, rest are optional */ struct _SERVER_CONNECT_REC { @@ -28,6 +31,9 @@ struct _SERVER_REC { #include "server-rec.h" }; +#define SEND_TARGET_CHANNEL 0 +#define SEND_TARGET_NICK 1 + extern GSList *servers, *lookup_servers; void servers_init(void); @@ -36,12 +42,16 @@ void servers_deinit(void); /* Disconnect from server */ void server_disconnect(SERVER_REC *server); +void server_ref(SERVER_REC *server); +int server_unref(SERVER_REC *server); + SERVER_REC *server_find_tag(const char *tag); SERVER_REC *server_find_chatnet(const char *chatnet); /* starts connecting to server */ int server_start_connect(SERVER_REC *server); -void server_connect_free(SERVER_CONNECT_REC *conn); +void server_connect_ref(SERVER_CONNECT_REC *conn); +void server_connect_unref(SERVER_CONNECT_REC *conn); /* initializes server record but doesn't start connecting */ void server_connect_init(SERVER_REC *server); diff --git a/apps/irssi/src/core/session.c b/apps/irssi/src/core/session.c new file mode 100644 index 00000000..0248f13e --- /dev/null +++ b/apps/irssi/src/core/session.c @@ -0,0 +1,374 @@ +/* + session.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "args.h" +#include "net-sendbuffer.h" +#include "pidwait.h" +#include "lib-config/iconfig.h" + +#include "chat-protocols.h" +#include "servers.h" +#include "servers-setup.h" +#include "channels.h" +#include "nicklist.h" + +static char *session_file; +static char *irssi_binary; + +static char **session_args; + +void session_set_binary(const char *path) +{ + char **paths, **tmp; + char *str; + + g_free_and_null(irssi_binary); + + if (g_path_is_absolute(path)) { + /* full path - easy */ + irssi_binary = g_strdup(path); + return; + } + + if (strchr(path, G_DIR_SEPARATOR) != NULL) { + /* relative path */ + str = g_get_current_dir(); + irssi_binary = g_strconcat(str, G_DIR_SEPARATOR_S, path, NULL); + g_free(str); + return; + } + + /* we'll need to find it from path. */ + str = g_getenv("PATH"); + if (str == NULL) return; + + paths = g_strsplit(str, ":", -1); + for (tmp = paths; *tmp != NULL; tmp++) { + str = g_strconcat(*tmp, G_DIR_SEPARATOR_S, path, NULL); + if (access(str, X_OK) == 0) { + irssi_binary = str; + break; + } + g_free(str); + } + g_strfreev(paths); +} + +void session_upgrade(void) +{ + if (session_args == NULL) + return; + + execvp(session_args[0], session_args); + fprintf(stderr, "exec failed: %s: %s\n", + session_args[0], g_strerror(errno)); +} + +/* SYNTAX: UPGRADE [] */ +static void cmd_upgrade(const char *data) +{ + CONFIG_REC *session; + char *session_file, *str; + + if (*data == '\0') + data = irssi_binary; + if (data == NULL) + cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + /* save the session */ + session_file = g_strdup_printf("%s/session", get_irssi_dir()); + session = config_open(session_file, 0600); + unlink(session_file); + + signal_emit("session save", 1, session); + config_write(session, NULL, -1); + config_close(session); + + /* data may contain some other program as well, like + /UPGRADE /usr/bin/screen irssi */ + str = g_strdup_printf("%s --noconnect --session=%s --home=%s --config=%s", + data, session_file, get_irssi_dir(), get_irssi_config()); + session_args = g_strsplit(str, " ", -1); + g_free(str); + + signal_emit("gui exit", 0); +} + +static void session_save_nick(CHANNEL_REC *channel, NICK_REC *nick, + CONFIG_REC *config, CONFIG_NODE *node) +{ + node = config_node_section(node, NULL, NODE_TYPE_BLOCK); + + config_node_set_str(config, node, "nick", nick->nick); + config_node_set_bool(config, node, "op", nick->op); + config_node_set_bool(config, node, "halfop", nick->halfop); + config_node_set_bool(config, node, "voice", nick->voice); + + signal_emit("session save nick", 4, channel, nick, config, node); +} + +static void session_save_channel_nicks(CHANNEL_REC *channel, CONFIG_REC *config, + CONFIG_NODE *node) +{ + GSList *tmp, *nicks; + + node = config_node_section(node, "nicks", NODE_TYPE_LIST); + nicks = nicklist_getnicks(channel); + for (tmp = nicks; tmp != NULL; tmp = tmp->next) + session_save_nick(channel, tmp->data, config, node); + g_slist_free(nicks); +} + +static void session_save_channel(CHANNEL_REC *channel, CONFIG_REC *config, + CONFIG_NODE *node) +{ + node = config_node_section(node, NULL, NODE_TYPE_BLOCK); + + config_node_set_str(config, node, "name", channel->name); + config_node_set_str(config, node, "topic", channel->topic); + config_node_set_str(config, node, "key", channel->key); + + signal_emit("session save channel", 3, channel, config, node); +} + +static void session_save_server_channels(SERVER_REC *server, + CONFIG_REC *config, + CONFIG_NODE *node) +{ + GSList *tmp; + + /* save channels */ + node = config_node_section(node, "channels", NODE_TYPE_LIST); + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) + session_save_channel(tmp->data, config, node); +} + +static void session_save_server(SERVER_REC *server, CONFIG_REC *config, + CONFIG_NODE *node) +{ + int handle; + + node = config_node_section(node, NULL, NODE_TYPE_BLOCK); + + config_node_set_str(config, node, "chat_type", + chat_protocol_find_id(server->chat_type)->name); + config_node_set_str(config, node, "address", server->connrec->address); + config_node_set_int(config, node, "port", server->connrec->port); + config_node_set_str(config, node, "chatnet", server->connrec->chatnet); + config_node_set_str(config, node, "password", server->connrec->password); + config_node_set_str(config, node, "nick", server->nick); + + handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle)); + config_node_set_int(config, node, "handle", handle); + + signal_emit("session save server", 3, server, config, node); + + /* fake the server disconnection */ + g_io_channel_unref(net_sendbuffer_handle(server->handle)); + net_sendbuffer_destroy(server->handle, FALSE); + server->handle = NULL; + + server->connection_lost = TRUE; + server->no_reconnect = TRUE; + server_disconnect(server); +} + +static void session_restore_channel_nicks(CHANNEL_REC *channel, + CONFIG_NODE *node) +{ + GSList *tmp; + + /* restore nicks */ + node = config_node_section(node, "nicks", -1); + if (node != NULL && node->type == NODE_TYPE_LIST) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + signal_emit("session restore nick", 2, + channel, tmp->data); + } + } +} + +static void session_restore_channel(SERVER_REC *server, CONFIG_NODE *node) +{ + CHANNEL_REC *channel; + const char *name; + + name = config_node_get_str(node, "name", NULL); + if (name == NULL) + return; + + channel = CHAT_PROTOCOL(server)->channel_create(server, name, TRUE); + channel->topic = g_strdup(config_node_get_str(node, "topic", NULL)); + channel->key = g_strdup(config_node_get_str(node, "key", NULL)); + channel->session_rejoin = TRUE; + + signal_emit("session restore channel", 2, channel, node); +} + +static void session_restore_server_channels(SERVER_REC *server, + CONFIG_NODE *node) +{ + GSList *tmp; + + /* restore channels */ + node = config_node_section(node, "channels", -1); + if (node != NULL && node->type == NODE_TYPE_LIST) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) + session_restore_channel(server, tmp->data); + } +} + +static void session_restore_server(CONFIG_NODE *node) +{ + CHAT_PROTOCOL_REC *proto; + SERVER_CONNECT_REC *conn; + SERVER_REC *server; + const char *chat_type, *address, *chatnet, *password, *nick; + int port, handle; + + chat_type = config_node_get_str(node, "chat_type", NULL); + address = config_node_get_str(node, "address", NULL); + port = config_node_get_int(node, "port", 0); + chatnet = config_node_get_str(node, "chatnet", NULL); + password = config_node_get_str(node, "password", NULL); + nick = config_node_get_str(node, "nick", NULL); + handle = config_node_get_int(node, "handle", -1); + + if (chat_type == NULL || address == NULL || nick == NULL || handle < 0) + return; + + proto = chat_protocol_find(chat_type); + if (proto == NULL || proto->not_initialized) { + if (handle < 0) close(handle); + return; + } + + conn = server_create_conn(proto->id, address, port, + chatnet, password, nick); + if (conn != NULL) { + conn->reconnection = TRUE; + + server = proto->server_connect(conn); + server->handle = net_sendbuffer_create(g_io_channel_unix_new(handle), 0); + server->session_reconnect = TRUE; + + signal_emit("session restore server", 2, server, node); + } +} + +static void sig_session_save(CONFIG_REC *config) +{ + CONFIG_NODE *node; + GSList *tmp; + GString *str; + + /* save servers */ + node = config_node_traverse(config, "(servers", TRUE); + while (servers != NULL) + session_save_server(servers->data, config, node); + + /* save pids */ + str = g_string_new(NULL); + for (tmp = pidwait_get_pids(); tmp != NULL; tmp = tmp->next) + g_string_sprintfa(str, "%d ", GPOINTER_TO_INT(tmp->data)); + config_node_set_str(config, config->mainnode, "pids", str->str); + g_string_free(str, TRUE); +} + +static void sig_session_restore(CONFIG_REC *config) +{ + CONFIG_NODE *node; + GSList *tmp; + char **pids, **pid; + + /* restore servers */ + node = config_node_traverse(config, "(servers", FALSE); + if (node != NULL) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) + session_restore_server(tmp->data); + } + + /* restore pids (so we don't leave zombies) */ + pids = g_strsplit(config_node_get_str(config->mainnode, "pids", ""), " ", -1); + for (pid = pids; *pid != NULL; pid++) + pidwait_add(atoi(*pid)); + g_strfreev(pids); +} + +static void sig_init_finished(void) +{ + CONFIG_REC *session; + + if (session_file == NULL) + return; + + session = config_open(session_file, -1); + if (session == NULL) + return; + + config_parse(session); + signal_emit("session restore", 1, session); + config_close(session); + + unlink(session_file); + session_file = NULL; +} + +void session_init(void) +{ + static struct poptOption options[] = { + { "session", 0, POPT_ARG_STRING, &session_file, 0, "Used by /UPGRADE command", "PATH" }, + { NULL, '\0', 0, NULL } + }; + + session_file = NULL; + args_register(options); + + command_bind("upgrade", NULL, (SIGNAL_FUNC) cmd_upgrade); + + signal_add("session save", (SIGNAL_FUNC) sig_session_save); + signal_add("session restore", (SIGNAL_FUNC) sig_session_restore); + signal_add("session save server", (SIGNAL_FUNC) session_save_server_channels); + signal_add("session restore server", (SIGNAL_FUNC) session_restore_server_channels); + signal_add("session save channel", (SIGNAL_FUNC) session_save_channel_nicks); + signal_add("session restore channel", (SIGNAL_FUNC) session_restore_channel_nicks); + signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished); +} + +void session_deinit(void) +{ + g_free_not_null(irssi_binary); + + command_unbind("upgrade", (SIGNAL_FUNC) cmd_upgrade); + + signal_remove("session save", (SIGNAL_FUNC) sig_session_save); + signal_remove("session restore", (SIGNAL_FUNC) sig_session_restore); + signal_remove("session save server", (SIGNAL_FUNC) session_save_server_channels); + signal_remove("session restore server", (SIGNAL_FUNC) session_restore_server_channels); + signal_remove("session save channel", (SIGNAL_FUNC) session_save_channel_nicks); + signal_remove("session restore channel", (SIGNAL_FUNC) session_restore_channel_nicks); + signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished); +} diff --git a/apps/irssi/src/core/session.h b/apps/irssi/src/core/session.h new file mode 100644 index 00000000..5788b5f0 --- /dev/null +++ b/apps/irssi/src/core/session.h @@ -0,0 +1,10 @@ +#ifndef __SESSION_H +#define __SESSION_H + +void session_set_binary(const char *path); +void session_upgrade(void); + +void session_init(void); +void session_deinit(void); + +#endif diff --git a/apps/irssi/src/core/settings.c b/apps/irssi/src/core/settings.c index 10ecba92..1a98462a 100644 --- a/apps/irssi/src/core/settings.c +++ b/apps/irssi/src/core/settings.c @@ -32,7 +32,6 @@ CONFIG_REC *mainconfig; static GString *last_errors; -static char *last_config_error_msg; static GSList *last_invalid_modules; static int fe_initialized; static int config_changed; /* FIXME: remove after .98 (unless needed again) */ @@ -274,11 +273,6 @@ static void sig_init_finished(void) g_string_free(last_errors, TRUE); } - if (last_config_error_msg != NULL) { - signal_emit("gui dialog", 2, "error", last_config_error_msg); - g_free_and_null(last_config_error_msg); - } - if (config_changed) { /* some backwards compatibility changes were made to config file, reload it */ @@ -286,20 +280,6 @@ static void sig_init_finished(void) } } -/* FIXME: remove after 0.7.98 - only for backward compatibility */ -static void settings_move(SETTINGS_REC *rec, char *value) -{ - CONFIG_NODE *setnode, *node; - - setnode = iconfig_node_traverse("settings", TRUE); - node = config_node_section(setnode, rec->module, NODE_TYPE_BLOCK); - - iconfig_node_set_str(node, rec->key, value); - iconfig_node_set_str(setnode, rec->key, NULL); - - config_changed = TRUE; -} - static void settings_clean_invalid_module(const char *module) { CONFIG_NODE *node; @@ -312,9 +292,9 @@ static void settings_clean_invalid_module(const char *module) node = config_node_section(node, module, -1); if (node == NULL) return; - for (tmp = node->value; tmp != NULL; tmp = next) { + for (tmp = config_node_first(node->value); tmp != NULL; tmp = next) { CONFIG_NODE *subnode = tmp->data; - next = tmp->next; + next = config_node_next(tmp); set = g_hash_table_lookup(settings, subnode->key); if (set == NULL || strcmp(set->module, module) != 0) @@ -344,25 +324,12 @@ void settings_check_module(const char *module) SETTINGS_REC *set; CONFIG_NODE *node; GString *errors; - GSList *tmp, *next; + GSList *tmp; int count; g_return_if_fail(module != NULL); node = iconfig_node_traverse("settings", FALSE); - if (node != NULL) { - /* FIXME: remove after 0.7.98 */ - for (tmp = node->value; tmp != NULL; tmp = next) { - CONFIG_NODE *node = tmp->data; - - next = tmp->next; - if (node->type != NODE_TYPE_KEY) - continue; - set = g_hash_table_lookup(settings, node->key); - if (set != NULL) - settings_move(set, node->value); - } - } node = node == NULL ? NULL : config_node_section(node, module, -1); if (node == NULL) return; @@ -371,7 +338,8 @@ void settings_check_module(const char *module) "file for module %s:", module); count = 0; - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { node = tmp->data; set = g_hash_table_lookup(settings, node->key); @@ -489,18 +457,17 @@ static CONFIG_REC *parse_configfile(const char *fname) CONFIG_REC *config; struct stat statbuf; const char *path; - char *real_fname; + char *str; - real_fname = fname != NULL ? g_strdup(fname) : - g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc" - G_DIR_SEPARATOR_S"config", g_get_home_dir()); + if (fname == NULL) + fname = get_irssi_config(); - if (stat(real_fname, &statbuf) == 0) - path = real_fname; + if (stat(fname, &statbuf) == 0) + path = fname; else { /* user configuration file not found, use the default one from sysconfdir */ - path = SYSCONFDIR"/irssi/config"; + path = SYSCONFDIR"/irssi.conf"; if (stat(path, &statbuf) != 0) { /* no configuration file in sysconfdir .. use the build-in configuration */ @@ -510,9 +477,11 @@ static CONFIG_REC *parse_configfile(const char *fname) config = config_open(path, -1); if (config == NULL) { - last_config_error_msg = - g_strdup_printf("Error opening configuration file %s: %s", - path, g_strerror(errno)); + str = g_strdup_printf("Error opening configuration file %s: %s", + path, g_strerror(errno)); + signal_emit("gui dialog", 2, "error", str); + g_free(str); + config = config_open(NULL, -1); } @@ -521,9 +490,8 @@ static CONFIG_REC *parse_configfile(const char *fname) else config_parse_data(config, default_config, "internal"); - config_change_file_name(config, real_fname, 0660); - irssi_config_save_state(real_fname); - g_free(real_fname); + config_change_file_name(config, fname, 0660); + irssi_config_save_state(fname); return config; } @@ -532,27 +500,26 @@ static void init_configfile(void) struct stat statbuf; char *str; - str = g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc", g_get_home_dir()); - if (stat(str, &statbuf) != 0) { + if (stat(get_irssi_dir(), &statbuf) != 0) { /* ~/.irssi not found, create it. */ - if (mkpath(str, 0700) != 0) { - g_error("Couldn't create %s directory", str); + if (mkpath(get_irssi_dir(), 0700) != 0) { + g_error("Couldn't create %s directory", get_irssi_dir()); } } else if (!S_ISDIR(statbuf.st_mode)) { g_error("%s is not a directory.\n" - "You should remove it with command: rm ~/.irssi", str); + "You should remove it with command: rm %s", + get_irssi_dir(), get_irssi_dir()); } - g_free(str); mainconfig = parse_configfile(NULL); config_last_modifycounter = mainconfig->modifycounter; /* any errors? */ if (config_last_error(mainconfig) != NULL) { - last_config_error_msg = - g_strdup_printf("Ignored errors in configuration " - "file:\n%s", - config_last_error(mainconfig)); + str = g_strdup_printf("Ignored errors in configuration file:\n%s", + config_last_error(mainconfig)); + signal_emit("gui dialog", 2, "error", str); + g_free(str); } signal(SIGTERM, sig_term); @@ -563,11 +530,9 @@ int settings_reread(const char *fname) CONFIG_REC *tempconfig; char *str; - if (fname == NULL) fname = "~/.silc/config"; - - str = convert_home(fname); + str = fname == NULL ? NULL : convert_home(fname); tempconfig = parse_configfile(str); - g_free(str); + g_free_not_null(str); if (tempconfig == NULL) { signal_emit("gui dialog", 2, "error", g_strerror(errno)); @@ -589,11 +554,11 @@ int settings_reread(const char *fname) config_last_modifycounter = mainconfig->modifycounter; signal_emit("setup changed", 0); - signal_emit("setup reread", 0); + signal_emit("setup reread", 1, mainconfig->fname); return TRUE; } -int settings_save(const char *fname) +int settings_save(const char *fname, int autosave) { char *str; int error; @@ -610,19 +575,20 @@ int settings_save(const char *fname) signal_emit("gui dialog", 2, "error", str); g_free(str); } + signal_emit("setup saved", 2, fname, GINT_TO_POINTER(autosave)); return !error; } -static void sig_autosave(void) +static int sig_autosave(void) { char *fname, *str; if (!settings_get_bool("settings_autosave") || config_last_modifycounter == mainconfig->modifycounter) - return; + return 1; if (!irssi_config_is_changed(NULL)) - settings_save(NULL); + settings_save(NULL, TRUE); else { fname = g_strconcat(mainconfig->fname, ".autosave", NULL); str = g_strdup_printf("Configuration file was modified " @@ -633,18 +599,19 @@ static void sig_autosave(void) signal_emit("gui dialog", 2, "warning", str); g_free(str); - settings_save(fname); + settings_save(fname, TRUE); g_free(fname); } + + return 1; } void settings_init(void) { - settings = g_hash_table_new((GHashFunc) g_str_hash, - (GCompareFunc) g_str_equal); + settings = g_hash_table_new((GHashFunc) g_istr_hash, + (GCompareFunc) g_istr_equal); last_errors = NULL; - last_config_error_msg = NULL; last_invalid_modules = NULL; fe_initialized = FALSE; config_changed = FALSE; diff --git a/apps/irssi/src/core/settings.h b/apps/irssi/src/core/settings.h index cea8c4d0..e0df975a 100644 --- a/apps/irssi/src/core/settings.h +++ b/apps/irssi/src/core/settings.h @@ -35,6 +35,7 @@ typedef struct { #define iconfig_node_add_list(a, b) config_node_add_list(mainconfig, a, b) extern CONFIG_REC *mainconfig; +extern const char *default_config; /* Functions for handling the "settings" node of Irssi configuration */ const char *settings_get_str(const char *key); @@ -80,7 +81,7 @@ void settings_clean_invalid(void); /* if `fname' is NULL, the default is used */ int settings_reread(const char *fname); -int settings_save(const char *fname); +int settings_save(const char *fname, int autosave); int irssi_config_is_changed(const char *fname); void settings_init(void); diff --git a/apps/irssi/src/core/signals.c b/apps/irssi/src/core/signals.c index cb964eb0..92064e8d 100644 --- a/apps/irssi/src/core/signals.c +++ b/apps/irssi/src/core/signals.c @@ -26,9 +26,9 @@ typedef struct { int id; /* signal id */ + int refcount; int emitting; /* signal is being emitted */ - int altered; /* some signal functions are marked as NULL */ int stop_emit; /* this signal was stopped */ GPtrArray *modulelist[SIGNAL_LISTS]; /* list of what signals belong @@ -43,6 +43,37 @@ static GMemChunk *signals_chunk; static GHashTable *signals; static SIGNAL_REC *current_emitted_signal; +#define signal_ref(signal) ++(signal)->refcount +#define signal_unref(rec) (signal_unref_full(rec, TRUE)) + +static int signal_unref_full(SIGNAL_REC *rec, int remove_hash) +{ + if (rec->refcount == 0) { + g_error("signal_unref(%s) : BUG - reference counter == 0", + signal_get_id_str(rec->id)); + } + + if (--rec->refcount != 0) + return FALSE; + + /* remove whole signal from memory */ + if (!signal_is_emitlist_empty(rec)) { + g_error("signal_unref(%s) : BUG - emitlist wasn't empty", + signal_get_id_str(rec->id)); + } + + if (remove_hash) + g_hash_table_remove(signals, GINT_TO_POINTER(rec->id)); + g_mem_chunk_free(signals_chunk, rec); + return TRUE; +} + +static void signal_unref_count(SIGNAL_REC *rec, int count) +{ + while (count-- > 0) + signal_unref(rec); +} + void signal_add_to(const char *module, int pos, const char *signal, SIGNAL_FUNC func) { @@ -64,7 +95,7 @@ void signal_add_to_id(const char *module, int pos, rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); if (rec == NULL) { rec = g_mem_chunk_alloc0(signals_chunk); - rec->id = signal_id; + rec->id = signal_id; g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), rec); } @@ -75,19 +106,8 @@ void signal_add_to_id(const char *module, int pos, g_ptr_array_add(rec->siglist[pos], (void *) func); g_ptr_array_add(rec->modulelist[pos], (void *) module); -} - -/* Destroy the whole signal */ -static void signal_destroy(int signal_id) -{ - SIGNAL_REC *rec; - rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); - if (rec != NULL) { - /* remove whole signal from memory */ - g_hash_table_remove(signals, GINT_TO_POINTER(signal_id)); - g_free(rec); - } + signal_ref(rec); } static int signal_list_find(GPtrArray *array, void *data) @@ -102,23 +122,28 @@ static int signal_list_find(GPtrArray *array, void *data) return -1; } -static void signal_remove_from_list(SIGNAL_REC *rec, int signal_id, - int list, int index) +static void signal_list_free(SIGNAL_REC *rec, int list) { - if (rec->emitting) { - g_ptr_array_index(rec->siglist[list], index) = NULL; - rec->altered = TRUE; - } else { - g_ptr_array_remove_index(rec->siglist[list], index); - g_ptr_array_remove_index(rec->modulelist[list], index); - if (signal_is_emitlist_empty(rec)) - signal_destroy(signal_id); - } + g_ptr_array_free(rec->siglist[list], TRUE); + g_ptr_array_free(rec->modulelist[list], TRUE); + rec->siglist[list] = NULL; + rec->modulelist[list] = NULL; +} + +/* Returns TRUE if the whole signal is removed after this remove */ +static void signal_remove_from_list(SIGNAL_REC *rec, int list, int index) +{ + g_ptr_array_remove_index(rec->siglist[list], index); + g_ptr_array_remove_index(rec->modulelist[list], index); + + if (rec->siglist[list]->len == 0) + signal_list_free(rec, list); + + signal_unref(rec); } /* Remove signal from emit lists */ -static int signal_remove_from_lists(SIGNAL_REC *rec, int signal_id, - SIGNAL_FUNC func) +static int signal_remove_from_lists(SIGNAL_REC *rec, SIGNAL_FUNC func) { int n, index; @@ -129,7 +154,7 @@ static int signal_remove_from_lists(SIGNAL_REC *rec, int signal_id, index = signal_list_find(rec->siglist[n], (void *) func); if (index != -1) { /* remove the function from emit list */ - signal_remove_from_list(rec, signal_id, n, index); + signal_remove_from_list(rec, n, index); return 1; } } @@ -146,7 +171,7 @@ void signal_remove_id(int signal_id, SIGNAL_FUNC func) rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); if (rec != NULL) - signal_remove_from_lists(rec, signal_id, func); + signal_remove_from_lists(rec, func); } /* unbind signal */ @@ -157,34 +182,22 @@ void signal_remove(const char *signal, SIGNAL_FUNC func) signal_remove_id(signal_get_uniq_id(signal), func); } -/* Remove all NULL functions from signal list */ -static void signal_list_clean(SIGNAL_REC *rec) -{ - int n, index; - - for (n = 0; n < SIGNAL_LISTS; n++) { - if (rec->siglist[n] == NULL) - continue; - - for (index = rec->siglist[n]->len-1; index >= 0; index--) { - if (g_ptr_array_index(rec->siglist[n], index) == NULL) { - g_ptr_array_remove_index(rec->siglist[n], index); - g_ptr_array_remove_index(rec->modulelist[n], index); - } - } - } -} - -static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist) +static int signal_emit_real(SIGNAL_REC *rec, int params, va_list va) { + gconstpointer arglist[SIGNAL_MAX_ARGUMENTS]; SIGNAL_REC *prev_emitted_signal; SIGNAL_FUNC func; int n, index, stopped, stop_emit_count; + for (n = 0; n < SIGNAL_MAX_ARGUMENTS; n++) + arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer); + /* signal_stop_by_name("signal"); signal_emit("signal", ...); fails if we compare rec->stop_emit against 0. */ stop_emit_count = rec->stop_emit; + signal_ref(rec); + stopped = FALSE; rec->emitting++; for (n = 0; n < SIGNAL_LISTS; n++) { @@ -195,15 +208,13 @@ static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist) for (index = rec->siglist[n]->len-1; index >= 0; index--) { func = (SIGNAL_FUNC) g_ptr_array_index(rec->siglist[n], index); - if (func != NULL) { - prev_emitted_signal = current_emitted_signal; - current_emitted_signal = rec; + prev_emitted_signal = current_emitted_signal; + current_emitted_signal = rec; #if SIGNAL_MAX_ARGUMENTS != 6 -# error SIGNAL_MAX_ARGS changed - update code +# error SIGNAL_MAX_ARGUMENTS changed - update code #endif - func(arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5]); - current_emitted_signal = prev_emitted_signal; - } + func(arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5]); + current_emitted_signal = prev_emitted_signal; if (rec->stop_emit != stop_emit_count) { stopped = TRUE; @@ -220,60 +231,48 @@ static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist) /* signal_stop() used too many times */ rec->stop_emit = 0; } - if (rec->altered) { - signal_list_clean(rec); - rec->altered = FALSE; - } } + signal_unref(rec); return stopped; } -static int signal_emitv_id(int signal_id, int params, va_list va) +int signal_emit(const char *signal, int params, ...) { - gconstpointer arglist[SIGNAL_MAX_ARGUMENTS]; SIGNAL_REC *rec; - int n; + va_list va; + int signal_id; - g_return_val_if_fail(signal_id >= 0, FALSE); g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE); - for (n = 0; n < SIGNAL_MAX_ARGUMENTS; n++) - arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer); + signal_id = signal_get_uniq_id(signal); rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); - if (rec != NULL && signal_emit_real(rec, arglist)) - return TRUE; + if (rec != NULL) { + va_start(va, params); + signal_emit_real(rec, params, va); + va_end(va); + } return rec != NULL; } -int signal_emit(const char *signal, int params, ...) -{ - va_list va; - int signal_id, ret; - - /* get arguments */ - signal_id = signal_get_uniq_id(signal); - - va_start(va, params); - ret = signal_emitv_id(signal_id, params, va); - va_end(va); - - return ret; -} - int signal_emit_id(int signal_id, int params, ...) { + SIGNAL_REC *rec; va_list va; - int ret; - /* get arguments */ - va_start(va, params); - ret = signal_emitv_id(signal_id, params, va); - va_end(va); + g_return_val_if_fail(signal_id >= 0, FALSE); + g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE); - return ret; + rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); + if (rec != NULL) { + va_start(va, params); + signal_emit_real(rec, params, va); + va_end(va); + } + + return rec != NULL; } /* stop the current ongoing signal emission */ @@ -332,28 +331,37 @@ int signal_is_stopped(int signal_id) static void signal_remove_module(void *signal, SIGNAL_REC *rec, const char *module) { - unsigned int index; - int signal_id, list; - - signal_id = GPOINTER_TO_INT(signal); + unsigned int index; + int list; for (list = 0; list < SIGNAL_LISTS; list++) { if (rec->modulelist[list] == NULL) continue; - for (index = 0; index < rec->modulelist[list]->len; index++) { - if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index), module) == 0) - signal_remove_from_list(rec, signal_id, list, index); - } + for (index = rec->modulelist[list]->len; index > 0; index--) + if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index-1), module) == 0) + signal_remove_from_list(rec, list, index-1); } } +static void signal_foreach_ref(void *signal, SIGNAL_REC *rec) +{ + signal_ref(rec); +} + +static int signal_foreach_unref(void *signal, SIGNAL_REC *rec) +{ + return signal_unref_full(rec, FALSE); +} + /* remove all signals that belong to `module' */ void signals_remove_module(const char *module) { g_return_if_fail(module != NULL); + g_hash_table_foreach(signals, (GHFunc) signal_foreach_ref, NULL); g_hash_table_foreach(signals, (GHFunc) signal_remove_module, (void *) module); + g_hash_table_foreach_remove(signals, (GHRFunc) signal_foreach_unref, NULL); } void signals_init(void) @@ -367,15 +375,19 @@ static void signal_free(void *key, SIGNAL_REC *rec) { int n; + signal_ref(rec); + for (n = 0; n < SIGNAL_LISTS; n++) { if (rec->siglist[n] != NULL) { - g_ptr_array_free(rec->siglist[n], TRUE); - g_ptr_array_free(rec->modulelist[n], TRUE); + signal_unref_count(rec, rec->siglist[n]->len); + signal_list_free(rec, n); } } - g_mem_chunk_free(signals_chunk, rec); - current_emitted_signal = NULL; + if (!signal_unref_full(rec, FALSE)) { + g_error("signal_free(%s) : BUG - signal still has %d references", + signal_get_id_str(rec->id), rec->refcount); + } } void signals_deinit(void) diff --git a/apps/irssi/src/core/special-vars.c b/apps/irssi/src/core/special-vars.c index 40372f93..4b1f535b 100644 --- a/apps/irssi/src/core/special-vars.c +++ b/apps/irssi/src/core/special-vars.c @@ -23,6 +23,7 @@ #include "special-vars.h" #include "expandos.h" #include "settings.h" +#include "servers.h" #include "misc.h" #define ALIGN_RIGHT 0x01 @@ -30,7 +31,10 @@ #define ALIGN_PAD 0x04 #define isvarchar(c) \ - (isalnum(c) || (c) == '_') + (i_isalnum(c) || (c) == '_') + +#define isarg(c) \ + (i_isdigit(c) || (c) == '*' || (c) == '~' || (c) == '-') static SPECIAL_HISTORY_FUNC history_func = NULL; @@ -43,7 +47,7 @@ static char *get_argument(char **cmd, char **arglist) arg = 0; max = -1; - argcount = strarray_length(arglist); + argcount = arglist == NULL ? 0 : strarray_length(arglist); if (**cmd == '*') { /* get all arguments */ @@ -51,7 +55,7 @@ static char *get_argument(char **cmd, char **arglist) /* get last argument */ arg = max = argcount-1; } else { - if (isdigit(**cmd)) { + if (i_isdigit(**cmd)) { /* first argument */ arg = max = (**cmd)-'0'; (*cmd)++; @@ -60,7 +64,7 @@ static char *get_argument(char **cmd, char **arglist) if (**cmd == '-') { /* get more than one argument */ (*cmd)++; - if (!isdigit(**cmd)) + if (!i_isdigit(**cmd)) max = -1; /* get all the rest */ else { max = (**cmd)-'0'; @@ -71,7 +75,7 @@ static char *get_argument(char **cmd, char **arglist) } str = g_string_new(NULL); - while (arg < argcount && (arg <= max || max == -1)) { + while (arg >= 0 && arg < argcount && (arg <= max || max == -1)) { g_string_append(str, arglist[arg]); g_string_append_c(str, ' '); arg++; @@ -152,7 +156,7 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item, { EXPANDO_FUNC func; - if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') { + if (isarg(**cmd)) { /* argument */ *free_ret = TRUE; if (arg_used != NULL) *arg_used = TRUE; @@ -160,7 +164,7 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item, get_argument(cmd, arglist); } - if (isalpha(**cmd) && isvarchar((*cmd)[1])) { + if (i_isalpha(**cmd) && isvarchar((*cmd)[1])) { /* long variable name.. */ return get_long_variable(cmd, server, item, free_ret, getname); } @@ -186,7 +190,7 @@ static char *get_history(char **cmd, void *item, int *free_ret) if (history_func == NULL) ret = NULL; else { - text = g_strndup(start, (int) (*cmd-start)+1); + text = g_strndup(start, (int) (*cmd-start)); ret = history_func(text, item, free_ret); g_free(text); } @@ -202,6 +206,11 @@ static char *get_special_value(char **cmd, SERVER_REC *server, void *item, char command, *value, *p; int len; + if ((flags & PARSE_FLAG_ONLY_ARGS) && !isarg(**cmd)) { + *free_ret = TRUE; + return g_strdup_printf("$%c", **cmd); + } + if (**cmd == '!') { /* find text from command history */ if (flags & PARSE_FLAG_GETNAME) @@ -279,7 +288,7 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad) /* '!' = don't cut, '-' = right padding */ str = *data; - while (*str != '\0' && *str != ']' && !isdigit(*str)) { + while (*str != '\0' && *str != ']' && !i_isdigit(*str)) { if (*str == '!') *flags &= ~ALIGN_CUT; else if (*str == '-') @@ -288,11 +297,11 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad) *flags &= ~ALIGN_PAD; str++; } - if (!isdigit(*str)) + if (!i_isdigit(*str)) return FALSE; /* expecting number */ /* get the alignment size */ - while (isdigit(*str)) { + while (i_isdigit(*str)) { *align = (*align) * 10 + (*str-'0'); str++; } @@ -435,11 +444,31 @@ char *parse_special(char **cmd, SERVER_REC *server, void *item, return value; } -static void gstring_append_escaped(GString *str, const char *text) +static void gstring_append_escaped(GString *str, const char *text, int flags) { + char esc[4], *escpos; + + escpos = esc; + if (flags & PARSE_FLAG_ESCAPE_VARS) + *escpos++ = '%'; + if (flags & PARSE_FLAG_ESCAPE_THEME) { + *escpos++ = '{'; + *escpos++ = '}'; + } + + if (escpos == esc) { + g_string_append(str, text); + return; + } + + *escpos = '\0'; while (*text != '\0') { - if (*text == '%') - g_string_append_c(str, '%'); + for (escpos = esc; *escpos != '\0'; escpos++) { + if (*text == *escpos) { + g_string_append_c(str, '%'); + break; + } + } g_string_append_c(str, *text); text++; } @@ -451,7 +480,7 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item, { char code, **arglist, *ret; GString *str; - int need_free; + int need_free, chr; g_return_val_if_fail(cmd != NULL, NULL); g_return_val_if_fail(data != NULL, NULL); @@ -463,16 +492,12 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item, code = 0; str = g_string_new(NULL); while (*cmd != '\0') { - if (code == '\\'){ - switch (*cmd) { - case 't': - g_string_append_c(str, '\t'); - break; - case 'n': - g_string_append_c(str, '\n'); - break; - default: - g_string_append_c(str, *cmd); + if (code == '\\') { + if (*cmd == ';') + g_string_append_c(str, ';'); + else { + chr = expand_escape(&cmd); + g_string_append_c(str, chr != -1 ? chr : *cmd); } code = 0; } else if (code == '$') { @@ -482,10 +507,7 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item, arglist, &need_free, arg_used, flags); if (ret != NULL) { - if ((flags & PARSE_FLAG_ESCAPE_VARS) == 0) - g_string_append(str, ret); - else - gstring_append_escaped(str, ret); + gstring_append_escaped(str, ret, flags); if (need_free) g_free(ret); } code = 0; @@ -525,25 +547,28 @@ void eval_special_string(const char *cmd, const char *data, /* get a list of all the commands to run */ orig = start = str = g_strdup(cmd); do { - if (is_split_char(str, start)) + if (is_split_char(str, start)) { *str++ = '\0'; - else if (*str != '\0') { + while (*str == ' ') str++; + } else if (*str != '\0') { str++; continue; } ret = parse_special_string(start, server, item, data, &arg_used, 0); - if (arg_used) arg_used_ever = TRUE; + if (*ret != '\0') { + if (arg_used) arg_used_ever = TRUE; - if (strchr(cmdchars, *ret) == NULL) { - /* no command char - let's put it there.. */ - char *old = ret; + if (strchr(cmdchars, *ret) == NULL) { + /* no command char - let's put it there.. */ + char *old = ret; - ret = g_strdup_printf("%c%s", *cmdchars, old); - g_free(old); + ret = g_strdup_printf("%c%s", *cmdchars, old); + g_free(old); + } + commands = g_slist_append(commands, ret); } - commands = g_slist_append(commands, ret); start = str; } while (*start != '\0'); @@ -558,8 +583,20 @@ void eval_special_string(const char *cmd, const char *data, ret = g_strconcat(old, " ", data, NULL); g_free(old); } + + if (server != NULL) + server_ref(server); signal_emit("send command", 3, ret, server, item); + if (server != NULL && !server_unref(server)) { + /* the server was destroyed */ + server = NULL; + item = NULL; + } + + /* FIXME: window item would need reference counting as well, + eg. "/EVAL win close;say hello" wouldn't work now.. */ + g_free(ret); commands = g_slist_remove(commands, commands->data); } @@ -571,41 +608,126 @@ void special_history_func_set(SPECIAL_HISTORY_FUNC func) history_func = func; } -static void special_vars_signals_do(const char *text, int funccount, - SIGNAL_FUNC *funcs, int bind) +static void update_signals_hash(GHashTable **hash, int *signals) { - char *ret; - int need_free; + void *signal_id; + int arg_type; + + if (*hash == NULL) { + *hash = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + } + + while (*signals != -1) { + signal_id = GINT_TO_POINTER(*signals); + arg_type = GPOINTER_TO_INT(g_hash_table_lookup(*hash, signal_id)); + if (arg_type != 0 && arg_type != signals[1]) { + /* same signal is used for different purposes .. + not sure if this should ever happen, but change + the argument type to none so it will at least + work. */ + arg_type = EXPANDO_ARG_NONE; + } + + if (arg_type == 0) arg_type = signals[1]; + g_hash_table_insert(*hash, signal_id, + GINT_TO_POINTER(arg_type)); + signals += 2; + } +} +static void get_signal_hash(void *signal_id, void *arg_type, int **pos) +{ + (*pos)[0] = GPOINTER_TO_INT(signal_id); + (*pos)[1] = GPOINTER_TO_INT(arg_type); + (*pos) += 2; +} + +static int *get_signals_list(GHashTable *hash) +{ + int *signals, *pos; + + if (hash == NULL) { + /* no expandos in text - never needs updating */ + return NULL; + } + + pos = signals = g_new(int, g_hash_table_size(hash)*2 + 1); + g_hash_table_foreach(hash, (GHFunc) get_signal_hash, &pos); + *pos = -1; + + g_hash_table_destroy(hash); + return signals; + +} + +#define TASK_BIND 1 +#define TASK_UNBIND 2 +#define TASK_GET_SIGNALS 3 + +static int *special_vars_signals_task(const char *text, int funccount, + SIGNAL_FUNC *funcs, int task) +{ + GHashTable *signals; + char *expando; + int need_free, *expando_signals; + + signals = NULL; while (*text != '\0') { if (*text == '\\' && text[1] != '\0') { + /* escape */ text += 2; } else if (*text == '$' && text[1] != '\0') { + /* expando */ text++; - ret = parse_special((char **) &text, NULL, NULL, - NULL, &need_free, NULL, - PARSE_FLAG_GETNAME); - if (ret != NULL) { - if (bind) - expando_bind(ret, funccount, funcs); - else - expando_unbind(ret, funccount, funcs); - if (need_free) g_free(ret); + expando = parse_special((char **) &text, NULL, NULL, + NULL, &need_free, NULL, + PARSE_FLAG_GETNAME); + if (expando == NULL) + continue; + + switch (task) { + case TASK_BIND: + expando_bind(expando, funccount, funcs); + break; + case TASK_UNBIND: + expando_unbind(expando, funccount, funcs); + break; + case TASK_GET_SIGNALS: + expando_signals = expando_get_signals(expando); + if (expando_signals != NULL) { + update_signals_hash(&signals, + expando_signals); + g_free(expando_signals); + } + break; } - + if (need_free) g_free(expando); + } else { + /* just a char */ + text++; } - else text++; } + + if (task == TASK_GET_SIGNALS) + return get_signals_list(signals); + + return NULL; } void special_vars_add_signals(const char *text, int funccount, SIGNAL_FUNC *funcs) { - special_vars_signals_do(text, funccount, funcs, TRUE); + special_vars_signals_task(text, funccount, funcs, TASK_BIND); } void special_vars_remove_signals(const char *text, int funccount, SIGNAL_FUNC *funcs) { - special_vars_signals_do(text, funccount, funcs, FALSE); + special_vars_signals_task(text, funccount, funcs, TASK_UNBIND); +} + +int *special_vars_get_signals(const char *text) +{ + return special_vars_signals_task(text, 0, NULL, TASK_GET_SIGNALS); } diff --git a/apps/irssi/src/core/special-vars.h b/apps/irssi/src/core/special-vars.h index af02e121..11262dad 100644 --- a/apps/irssi/src/core/special-vars.h +++ b/apps/irssi/src/core/special-vars.h @@ -6,6 +6,8 @@ #define PARSE_FLAG_GETNAME 0x01 /* return argument name instead of it's value */ #define PARSE_FLAG_ISSET_ANY 0x02 /* arg_used field specifies that at least one of the $variables was non-empty */ #define PARSE_FLAG_ESCAPE_VARS 0x04 /* if any arguments/variables contain % chars, escape them with another % */ +#define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */ +#define PARSE_FLAG_ONLY_ARGS 0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */ typedef char* (*SPECIAL_HISTORY_FUNC) (const char *text, void *item, int *free_ret); @@ -29,5 +31,7 @@ void special_vars_add_signals(const char *text, int funccount, SIGNAL_FUNC *funcs); void special_vars_remove_signals(const char *text, int funccount, SIGNAL_FUNC *funcs); +/* Returns [, EXPANDO_ARG_xxx, , ..., -1] */ +int *special_vars_get_signals(const char *text); #endif diff --git a/apps/irssi/src/core/window-item-rec.h b/apps/irssi/src/core/window-item-rec.h index 5c09a5b0..eeb465f4 100644 --- a/apps/irssi/src/core/window-item-rec.h +++ b/apps/irssi/src/core/window-item-rec.h @@ -12,4 +12,6 @@ time_t createtime; int data_level; char *hilight_color; +void (*destroy)(WI_ITEM_REC *item); + #undef STRUCT_SERVER_REC diff --git a/apps/irssi/src/core/write-buffer.c b/apps/irssi/src/core/write-buffer.c index 762fc24e..1c3eef82 100644 --- a/apps/irssi/src/core/write-buffer.c +++ b/apps/irssi/src/core/write-buffer.c @@ -126,13 +126,16 @@ void write_buffer_flush(void) block_count = 0; } +static int flush_timeout(void) +{ + write_buffer_flush(); + return 1; +} + static void read_settings(void) { int msecs; - if (timeout_tag != -1) - g_source_remove(timeout_tag); - write_buffer_flush(); write_buffer_max_blocks = settings_get_int("write_buffer_kb") * @@ -140,9 +143,14 @@ static void read_settings(void) if (settings_get_int("write_buffer_mins") > 0) { msecs = settings_get_int("write_buffer_mins")*60*1000; - timeout_tag = g_timeout_add(msecs, - (GSourceFunc) write_buffer_flush, - NULL); + if (timeout_tag == -1) { + timeout_tag = g_timeout_add(msecs, + (GSourceFunc) flush_timeout, + NULL); + } + } else if (timeout_tag != -1) { + g_source_remove(timeout_tag); + timeout_tag = -1; } } diff --git a/apps/irssi/src/fe-common/core/.cvsignore b/apps/irssi/src/fe-common/core/.cvsignore new file mode 100644 index 00000000..8553e9e9 --- /dev/null +++ b/apps/irssi/src/fe-common/core/.cvsignore @@ -0,0 +1,8 @@ +*.la +*.lo +*.o +.deps +.libs +Makefile +Makefile.in +so_locations diff --git a/apps/irssi/src/fe-common/core/Makefile.am b/apps/irssi/src/fe-common/core/Makefile.am index 798c271c..43bb1cd1 100644 --- a/apps/irssi/src/fe-common/core/Makefile.am +++ b/apps/irssi/src/fe-common/core/Makefile.am @@ -3,10 +3,10 @@ noinst_LIBRARIES = libfe_common_core.a include $(top_srcdir)/Makefile.defines.in INCLUDES = \ - $(GLIB_CFLAGS) \ -I$(top_srcdir)/src -I$(top_srcdir)/src/core/ \ + $(GLIB_CFLAGS) \ -DHELPDIR=\""$(silc_helpdir)"\" \ - -DSYSCONFDIR=\""$(silc_etcdir)"\" + -DTHEMESDIR=\""$(silc_etcdir)"\" libfe_common_core_a_SOURCES = \ autorun.c \ @@ -41,6 +41,7 @@ libfe_common_core_a_SOURCES = \ fe-windows.c noinst_HEADERS = \ + autorun.h \ command-history.h \ chat-completion.h \ completion.h \ diff --git a/apps/irssi/src/fe-common/core/autorun.c b/apps/irssi/src/fe-common/core/autorun.c index f49b6c30..9c1050db 100644 --- a/apps/irssi/src/fe-common/core/autorun.c +++ b/apps/irssi/src/fe-common/core/autorun.c @@ -1,7 +1,7 @@ /* autorun.c : irssi - Copyright (C) 1999-2000 Timo Sirainen + Copyright (C) 1999-2001 Timo Sirainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,14 +25,14 @@ #include "fe-windows.h" -static void sig_autorun(void) +void autorun_startup(void) { char tmpbuf[1024], *str, *path; LINEBUF_REC *buffer = NULL; int f, ret, recvlen; - /* open ~/.silc/startup and run all commands in it */ - path = g_strdup_printf("%s/.silc/startup", g_get_home_dir()); + /* open ~/.irssi/startup and run all commands in it */ + path = g_strdup_printf("%s/startup", get_irssi_dir()); f = open(path, O_RDONLY); g_free(path); if (f == -1) { @@ -44,19 +44,13 @@ static void sig_autorun(void) recvlen = read(f, tmpbuf, sizeof(tmpbuf)); ret = line_split(tmpbuf, recvlen, &str, &buffer); - if (ret > 0) eval_special_string(str, "", active_win->active_server, active_win->active); + if (ret > 0) { + eval_special_string(str, "", + active_win->active_server, + active_win->active); + } } while (ret > 0); line_split_free(buffer); close(f); } - -void autorun_init(void) -{ - signal_add_last("irssi init finished", (SIGNAL_FUNC) sig_autorun); -} - -void autorun_deinit(void) -{ - signal_remove("irssi init finished", (SIGNAL_FUNC) sig_autorun); -} diff --git a/apps/irssi/src/fe-common/core/autorun.h b/apps/irssi/src/fe-common/core/autorun.h new file mode 100644 index 00000000..59d8e846 --- /dev/null +++ b/apps/irssi/src/fe-common/core/autorun.h @@ -0,0 +1,6 @@ +#ifndef __AUTORUN_H +#define __AUTORUN_H + +void autorun_startup(void); + +#endif diff --git a/apps/irssi/src/fe-common/core/chat-completion.c b/apps/irssi/src/fe-common/core/chat-completion.c index 3cbcbf02..a3d92ccd 100644 --- a/apps/irssi/src/fe-common/core/chat-completion.c +++ b/apps/irssi/src/fe-common/core/chat-completion.c @@ -25,6 +25,7 @@ #include "settings.h" #include "chatnets.h" +#include "servers.h" #include "servers-setup.h" #include "channels.h" #include "channels-setup.h" @@ -156,6 +157,16 @@ static void sig_message_public(SERVER_REC *server, const char *msg, } } +static void sig_message_join(SERVER_REC *server, const char *channel, + const char *nick, const char *address) +{ + CHANNEL_REC *chanrec; + + chanrec = channel_find(server, channel); + if (chanrec != NULL) + CHANNEL_LAST_MSG_ADD(chanrec, nick, FALSE); +} + static void sig_message_private(SERVER_REC *server, const char *msg, const char *nick, const char *address) { @@ -382,7 +393,7 @@ static GList *completion_nicks_nonstrict(CHANNEL_REC *channel, /* remove non alnum chars from nick */ in = rec->nick; out = str; while (*in != '\0') { - if (isalnum(*in)) + if (i_isalnum(*in)) *out++ = *in; in++; } @@ -543,7 +554,8 @@ static void complete_window_nicks(GList **list, WINDOW_REC *window, } static void sig_complete_word(GList **list, WINDOW_REC *window, - const char *word, const char *linestart) + const char *word, const char *linestart, + int *want_space) { SERVER_REC *server; CHANNEL_REC *channel; @@ -559,7 +571,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window, if (server == NULL && servers != NULL) server = servers->data; - if (server != NULL && server->ischannel(word)) { + if (server != NULL && server_ischannel(server, word)) { /* probably completing a channel name */ *list = completion_get_channels(window->active_server, word); return; @@ -590,7 +602,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window, } else if (channel != NULL) { /* nick completion .. we could also be completing a nick after /MSG from nicks in channel */ - complete_window_nicks(list, window, word, linestart); + complete_window_nicks(list, window, word, linestart); } if (*list != NULL) signal_stop(); @@ -634,6 +646,41 @@ static void sig_complete_msg(GList **list, WINDOW_REC *window, if (*list != NULL) signal_stop(); } +static void sig_erase_complete_msg(WINDOW_REC *window, const char *word, + const char *line) +{ + SERVER_REC *server; + MODULE_SERVER_REC *mserver; + GSList *tmp; + + server = line_get_server(line); + if (server == NULL){ + server = window->active_server; + if (server == NULL) + return; + } + + if (*word == '\0') + return; + + /* check from global list */ + completion_last_message_remove(word); + + /* check from server specific list */ + if (server != NULL) { + mserver = MODULE_DATA(server); + for (tmp = mserver->lastmsgs; tmp != NULL; tmp = tmp->next) { + LAST_MSG_REC *rec = tmp->data; + + if (g_strcasecmp(rec->nick, word) == 0) { + last_msg_destroy(&mserver->lastmsgs, rec); + break; + } + } + + } +} + GList *completion_get_chatnets(const char *word) { GList *list; @@ -688,11 +735,30 @@ static void sig_complete_connect(GList **list, WINDOW_REC *window, if (*list != NULL) signal_stop(); } +static void sig_complete_topic(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + const char *topic; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + if (*word == '\0' && IS_CHANNEL(window->active)) { + topic = CHANNEL(window->active)->topic; + if (topic != NULL) { + *list = g_list_append(NULL, g_strdup(topic)); + signal_stop(); + } + } +} + /* expand \n, \t and \\ */ static char *expand_escapes(const char *line, SERVER_REC *server, WI_ITEM_REC *item) { char *ptr, *ret; + int chr; ret = ptr = g_malloc(strlen(line)+1); for (; *line != '\0'; line++) { @@ -707,25 +773,23 @@ static char *expand_escapes(const char *line, SERVER_REC *server, break; } - switch (*line) { - case 'n': + chr = expand_escape(&line); + if (chr == '\r' || chr == '\n') { /* newline .. we need to send another "send text" event to handle it (or actually the text before the newline..) */ - *ptr = '\0'; - signal_emit("send text", 3, ret, server, item); - ptr = ret; - break; - case 't': - *ptr++ = '\t'; - break; - case '\\': - *ptr++ = '\\'; - break; - default: + if (ret != ptr) { + *ptr = '\0'; + signal_emit("send text", 3, ret, server, item); + ptr = ret; + } + } else if (chr != -1) { + /* escaping went ok */ + *ptr++ = chr; + } else { + /* unknown escape, add it as-is */ *ptr++ = '\\'; *ptr++ = *line; - break; } } @@ -733,48 +797,62 @@ static char *expand_escapes(const char *line, SERVER_REC *server, return ret; } -static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +static char *auto_complete(CHANNEL_REC *channel, const char *line) { - CHANNEL_REC *channel; GList *comp; - char *line, *str, *ptr, comp_char; + const char *p; + char *nick, *ret; + + p = strstr(line, completion_char); + if (p == NULL) + return NULL; + + nick = g_strndup(line, (int) (p-line)); + + ret = NULL; + if (nicklist_find(channel, nick) == NULL) { + /* not an exact match, use the first possible completion */ + comp = completion_channel_nicks(channel, nick, NULL); + if (comp != NULL) { + ret = g_strconcat(comp->data, p, NULL); + g_list_foreach(comp, (GFunc) g_free, NULL); + g_list_free(comp); + } + } + + g_free(nick); + + return ret; +} + +static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + char *line, *str; g_return_if_fail(data != NULL); if (item == NULL) return; line = settings_get_bool("expand_escapes") ? expand_escapes(data, server, item) : g_strdup(data); - comp_char = *completion_char; /* check for automatic nick completion */ - ptr = NULL; - comp = NULL; - channel = CHANNEL(item); - - if (completion_auto && channel != NULL && comp_char != '\0') { - ptr = strchr(line, comp_char); - if (ptr != NULL) { - *ptr++ = '\0'; - if (nicklist_find(channel, line) == NULL) { - comp = completion_channel_nicks(channel, - line, NULL); - } + if (completion_auto && IS_CHANNEL(item)) { + str = auto_complete(CHANNEL(item), line); + if (str != NULL) { + g_free(line); + line = str; } } - str = g_strdup_printf(ptr == NULL ? "%s %s" : "%s %s%c%s", item->name, - comp != NULL ? (char *) comp->data : line, - comp_char, ptr); + str = g_strdup_printf(IS_CHANNEL(item) ? "-channel %s %s" : + IS_QUERY(item) ? "-nick %s %s" : "%s %s", + item->name, line); + signal_emit("command msg", 3, str, server, item); g_free(str); g_free(line); - if (comp != NULL) { - g_list_foreach(comp, (GFunc) g_free, NULL); - g_list_free(comp); - } - signal_stop(); } @@ -811,6 +889,11 @@ static void read_settings(void) cmdchars = settings_get_str("cmdchars"); completion_auto = settings_get_bool("completion_auto"); completion_strict = settings_get_bool("completion_strict"); + + if (*completion_char == '\0') { + /* this would break.. */ + completion_auto = FALSE; + } } void chat_completion_init(void) @@ -826,9 +909,14 @@ void chat_completion_init(void) read_settings(); signal_add("complete word", (SIGNAL_FUNC) sig_complete_word); signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg); + signal_add("complete command query", (SIGNAL_FUNC) sig_complete_msg); + signal_add("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_add("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg); signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect); signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect); + signal_add("complete command topic", (SIGNAL_FUNC) sig_complete_topic); signal_add("message public", (SIGNAL_FUNC) sig_message_public); + signal_add("message join", (SIGNAL_FUNC) sig_message_join); signal_add("message private", (SIGNAL_FUNC) sig_message_private); signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public); signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private); @@ -847,9 +935,14 @@ void chat_completion_deinit(void) signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word); signal_remove("complete command msg", (SIGNAL_FUNC) sig_complete_msg); + signal_remove("complete command query", (SIGNAL_FUNC) sig_complete_msg); + signal_remove("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_remove("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg); signal_remove("complete command connect", (SIGNAL_FUNC) sig_complete_connect); signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect); + signal_remove("complete command topic", (SIGNAL_FUNC) sig_complete_topic); signal_remove("message public", (SIGNAL_FUNC) sig_message_public); + signal_remove("message join", (SIGNAL_FUNC) sig_message_join); signal_remove("message private", (SIGNAL_FUNC) sig_message_private); signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public); signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private); diff --git a/apps/irssi/src/fe-common/core/command-history.c b/apps/irssi/src/fe-common/core/command-history.c index cdf4b5a5..70f68594 100644 --- a/apps/irssi/src/fe-common/core/command-history.c +++ b/apps/irssi/src/fe-common/core/command-history.c @@ -27,119 +27,217 @@ #include "fe-windows.h" #include "window-items.h" +#include "command-history.h" + /* command history */ -static GList *history, *history_pos; -static int history_lines, history_over_counter; +static HISTORY_REC *global_history; static int window_history; +static GSList *histories; -void command_history_add(WINDOW_REC *window, const char *text) +void command_history_add(HISTORY_REC *history, const char *text) { - GList **phistory, *link; - int *phistory_lines; + GList *link; + g_return_if_fail(history != NULL); g_return_if_fail(text != NULL); - if (window_history) { - /* window specific command history */ - phistory = &window->history; - phistory_lines = &window->history_lines; - } else { - /* global command history */ - phistory = &history; - phistory_lines = &history_lines; - } + link = g_list_last(history->list); + if (link != NULL && strcmp(link->data, text) == 0) + return; /* same as previous entry */ - if (settings_get_int("max_command_history") < 1 || *phistory_lines < settings_get_int("max_command_history")) - (*phistory_lines)++; + if (settings_get_int("max_command_history") < 1 || + history->lines < settings_get_int("max_command_history")) + history->lines++; else { - link = *phistory; + link = history->list; g_free(link->data); - *phistory = g_list_remove_link(*phistory, link); + history->list = g_list_remove_link(history->list, link); g_list_free_1(link); } - *phistory = g_list_append(*phistory, g_strdup(text)); + history->list = g_list_append(history->list, g_strdup(text)); } -const char *command_history_prev(WINDOW_REC *window, const char *text) +HISTORY_REC *command_history_find(HISTORY_REC *history) { - GList *pos, **phistory_pos; - int *phistory_over_counter; + GSList *tmp; + tmp = g_slist_find(histories, history); - if (window_history) { - phistory_pos = &window->history_pos; - phistory_over_counter = &window->history_over_counter; - } else { - phistory_pos = &history_pos; - phistory_over_counter = &history_over_counter; + if (tmp == NULL) + return NULL; + else + return tmp->data; +} + +HISTORY_REC *command_history_find_name(const char *name) +{ + GSList *tmp; + + if (name == NULL) + return NULL; + + for (tmp = histories; tmp != NULL; tmp = tmp->next) { + HISTORY_REC *rec = tmp->data; + + if (rec->name != NULL && g_strcasecmp(rec->name, name) == 0) + return rec; } + + return NULL; +} + +HISTORY_REC *command_history_current(WINDOW_REC *window) +{ + HISTORY_REC *rec; + + if (window == NULL) + return global_history; - pos = *phistory_pos; - if (*phistory_pos != NULL) { - *phistory_pos = (*phistory_pos)->prev; - if (*phistory_pos == NULL) - (*phistory_over_counter)++; + if (window_history) + return window->history; + + rec = command_history_find_name(window->history_name); + if (rec != NULL) + return rec; + + return global_history; +} + +const char *command_history_prev(WINDOW_REC *window, const char *text) +{ + HISTORY_REC *history; + GList *pos; + + history = command_history_current(window); + pos = history->pos; + + if (pos != NULL) { + history->pos = history->pos->prev; + if (history->pos == NULL) + history->over_counter++; } else { - *phistory_pos = g_list_last(window_history ? - window->history : history); + history->pos = g_list_last(history->list); } if (*text != '\0' && (pos == NULL || strcmp(pos->data, text) != 0)) { /* save the old entry to history */ - command_history_add(window, text); + command_history_add(history, text); } - return *phistory_pos == NULL ? "" : (*phistory_pos)->data; + return history->pos == NULL ? "" : history->pos->data; } const char *command_history_next(WINDOW_REC *window, const char *text) { - GList *pos, **phistory_pos; - int *phistory_over_counter; - - if (window_history) { - phistory_pos = &window->history_pos; - phistory_over_counter = &window->history_over_counter; - } else { - phistory_pos = &history_pos; - phistory_over_counter = &history_over_counter; - } + HISTORY_REC *history; + GList *pos; - pos = *phistory_pos; + history = command_history_current(window); + pos = history->pos; if (pos != NULL) - *phistory_pos = (*phistory_pos)->next; - else if (*phistory_over_counter > 0) { - (*phistory_over_counter)--; - *phistory_pos = window_history ? window->history : history; + history->pos = history->pos->next; + else if (history->over_counter > 0) { + history->over_counter--; + history->pos = history->list; } if (*text != '\0' && (pos == NULL || strcmp(pos->data, text) != 0)) { /* save the old entry to history */ - command_history_add(window, text); + command_history_add(history, text); } - return *phistory_pos == NULL ? "" : (*phistory_pos)->data; + return history->pos == NULL ? "" : history->pos->data; +} + +void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data) +{ + history->over_counter = 0; + history->pos = NULL; } void command_history_clear_pos(WINDOW_REC *window) { - window->history_over_counter = 0; - window->history_pos = NULL; - history_over_counter = 0; - history_pos = NULL; + g_slist_foreach(histories, + (GFunc) command_history_clear_pos_func, NULL); +} + +HISTORY_REC *command_history_create(const char *name) +{ + HISTORY_REC *rec; + + rec = g_new0(HISTORY_REC, 1); + + if (name != NULL) + rec->name = g_strdup(name); + + histories = g_slist_append(histories, rec); + + return rec; +} + +void command_history_destroy(HISTORY_REC *history) +{ + g_return_if_fail(history != NULL); + + /* history->refcount should be 0 here, or somthing is wrong... */ + g_return_if_fail(history->refcount == 0); + + histories = g_slist_remove(histories, history); + + g_list_foreach(history->list, (GFunc) g_free, NULL); + g_list_free(history->list); + + g_free_not_null(history->name); + g_free(history); +} + +void command_history_link(const char *name) +{ + HISTORY_REC *rec; + rec = command_history_find_name(name); + + if (rec == NULL) + rec = command_history_create(name); + + rec->refcount++; +} + +void command_history_unlink(const char *name) +{ + HISTORY_REC *rec; + rec = command_history_find_name(name); + + if (rec == NULL) + return; + + if (--(rec->refcount) <= 0) + command_history_destroy(rec); +} + +static void sig_window_created(WINDOW_REC *window, int automatic) +{ + window->history = command_history_create(NULL); } static void sig_window_destroyed(WINDOW_REC *window) { - g_list_foreach(window->history, (GFunc) g_free, NULL); - g_list_free(window->history); + command_history_unlink(window->history_name); + command_history_destroy(window->history); + g_free_not_null(window->history_name); +} + +static void sig_window_history_changed(WINDOW_REC *window, const char *oldname) +{ + command_history_link(window->history_name); + command_history_unlink(oldname); } static char *special_history_func(const char *text, void *item, int *free_ret) { WINDOW_REC *window; + HISTORY_REC *history; GList *tmp; char *findtext, *ret; @@ -148,8 +246,8 @@ static char *special_history_func(const char *text, void *item, int *free_ret) findtext = g_strdup_printf("*%s*", text); ret = NULL; - tmp = window_history ? window->history : history; - for (; tmp != NULL; tmp = tmp->next) { + history = command_history_current(window); + for (tmp = history->list; tmp != NULL; tmp = tmp->next) { const char *line = tmp->data; if (match_wildcards(findtext, line)) { @@ -174,19 +272,21 @@ void command_history_init(void) special_history_func_set(special_history_func); - history_lines = 0; history_over_counter = 0; - history = NULL; history_pos = NULL; + global_history = command_history_create(NULL); read_settings(); + signal_add("window created", (SIGNAL_FUNC) sig_window_created); signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_add("window history changed", (SIGNAL_FUNC) sig_window_history_changed); signal_add("setup changed", (SIGNAL_FUNC) read_settings); } void command_history_deinit(void) { + signal_remove("window created", (SIGNAL_FUNC) sig_window_created); signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_remove("window history changed", (SIGNAL_FUNC) sig_window_history_changed); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); - g_list_foreach(history, (GFunc) g_free, NULL); - g_list_free(history); + command_history_destroy(global_history); } diff --git a/apps/irssi/src/fe-common/core/command-history.h b/apps/irssi/src/fe-common/core/command-history.h index 9f37f069..2ca312e1 100644 --- a/apps/irssi/src/fe-common/core/command-history.h +++ b/apps/irssi/src/fe-common/core/command-history.h @@ -1,16 +1,35 @@ #ifndef __COMMAND_HISTORY_H #define __COMMAND_HISTORY_H -#include "fe-windows.h" +#include "common.h" + +typedef struct { + char *name; + + GList *list, *pos; + int lines, over_counter; + + int refcount; +} HISTORY_REC; + +HISTORY_REC *command_history_find(HISTORY_REC *history); +HISTORY_REC *command_history_find_name(const char *name); + +HISTORY_REC *command_history_current(WINDOW_REC *window); void command_history_init(void); void command_history_deinit(void); -void command_history_add(WINDOW_REC *window, const char *text, int prepend); +void command_history_add(HISTORY_REC *window, const char *text); const char *command_history_prev(WINDOW_REC *window, const char *text); const char *command_history_next(WINDOW_REC *window, const char *text); void command_history_clear_pos(WINDOW_REC *window); +HISTORY_REC *command_history_create(const char *name); +void command_history_destroy(HISTORY_REC *history); +void command_history_link(const char *name); +void command_history_unlink(const char *name); + #endif diff --git a/apps/irssi/src/fe-common/core/completion.c b/apps/irssi/src/fe-common/core/completion.c index cd86ab4f..f10fbb6d 100644 --- a/apps/irssi/src/fe-common/core/completion.c +++ b/apps/irssi/src/fe-common/core/completion.c @@ -41,7 +41,7 @@ static int last_want_space, last_line_pos; ((c) == ',') #define isseparator(c) \ - (isspace((int) (c)) || isseparator_notspace(c)) + (i_isspace(c) || isseparator_notspace(c)) void chat_completion_init(void); void chat_completion_deinit(void); @@ -113,27 +113,28 @@ static void free_completions(void) } /* manual word completion - called when TAB is pressed */ -char *word_complete(WINDOW_REC *window, const char *line, int *pos) +char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase) { static int startpos = 0, wordlen = 0; + int old_startpos, old_wordlen; GString *result; char *word, *wordstart, *linestart, *ret; - int want_space; + int continue_complete, want_space; g_return_val_if_fail(line != NULL, NULL); g_return_val_if_fail(pos != NULL, NULL); - if (complist != NULL && *pos == last_line_pos && - strcmp(line, last_line) == 0) { - /* complete from old list */ - complist = complist->next != NULL ? complist->next : - g_list_first(complist); - want_space = last_want_space; - } else { - /* get new completion list */ - free_completions(); + continue_complete = complist != NULL && *pos == last_line_pos && + strcmp(line, last_line) == 0; + + old_startpos = startpos; + old_wordlen = wordlen; + if (!erase && continue_complete) { + word = NULL; + linestart = NULL; + } else { /* get the word we want to complete */ word = get_word_at(line, *pos, &wordstart); startpos = (int) (wordstart-line); @@ -156,7 +157,8 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos) BUT if we start completion with "/msg ", we don't want to complete the /msg word, but instead complete empty word with /msg being in linestart. */ - if (*pos > 0 && line[*pos-1] == ' ') { + if (!erase && *pos > 0 && line[*pos-1] == ' ' && + (*linestart == '\0' || wordstart[-1] != ' ')) { char *old; old = linestart; @@ -171,14 +173,38 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos) wordlen = 0; } + } + + if (erase) { + signal_emit("complete erase", 3, window, word, linestart); + + if (!continue_complete) + return NULL; + + /* jump to next completion */ + word = NULL; + linestart = NULL; + startpos = old_startpos; + wordlen = old_wordlen; + } + + if (continue_complete) { + /* complete from old list */ + complist = complist->next != NULL ? complist->next : + g_list_first(complist); + want_space = last_want_space; + } else { + /* get new completion list */ + free_completions(); + want_space = TRUE; signal_emit("complete word", 5, &complist, window, word, linestart, &want_space); last_want_space = want_space; - - g_free(linestart); - g_free(word); } + g_free(linestart); + g_free(word); + if (complist == NULL) return NULL; @@ -207,7 +233,14 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos) return ret; } -GList *list_add_file(GList *list, const char *name) +#define IS_CURRENT_DIR(dir) \ + ((dir)[0] == '.' && ((dir)[1] == '\0' || (dir)[1] == G_DIR_SEPARATOR)) + +#define USE_DEFAULT_PATH(path, default_path) \ + ((!g_path_is_absolute(path) || IS_CURRENT_DIR(path)) && \ + default_path != NULL) + +GList *list_add_file(GList *list, const char *name, const char *default_path) { struct stat statbuf; char *fname; @@ -215,6 +248,11 @@ GList *list_add_file(GList *list, const char *name) g_return_val_if_fail(name != NULL, NULL); fname = convert_home(name); + if (USE_DEFAULT_PATH(fname, default_path)) { + g_free(fname); + fname = g_strconcat(default_path, G_DIR_SEPARATOR_S, + name, NULL); + } if (stat(fname, &statbuf) == 0) { list = g_list_append(list, !S_ISDIR(statbuf.st_mode) ? g_strdup(name) : g_strconcat(name, G_DIR_SEPARATOR_S, NULL)); @@ -224,7 +262,7 @@ GList *list_add_file(GList *list, const char *name) return list; } -GList *filename_complete(const char *path) +GList *filename_complete(const char *path, const char *default_path) { GList *list; DIR *dirp; @@ -238,17 +276,31 @@ GList *filename_complete(const char *path) /* get directory part of the path - expand ~/ */ realpath = convert_home(path); - dir = g_dirname(realpath); - g_free(realpath); + if (USE_DEFAULT_PATH(realpath, default_path)) { + g_free(realpath); + realpath = g_strconcat(default_path, G_DIR_SEPARATOR_S, + path, NULL); + } /* open directory for reading */ + dir = g_dirname(realpath); dirp = opendir(dir); g_free(dir); - if (dirp == NULL) return NULL; + g_free(realpath); + + if (dirp == NULL) + return NULL; dir = g_dirname(path); - if (*dir == G_DIR_SEPARATOR && dir[1] == '\0') - *dir = '\0'; /* completing file in root directory */ + if (*dir == G_DIR_SEPARATOR && dir[1] == '\0') { + /* completing file in root directory */ + *dir = '\0'; + } else if (IS_CURRENT_DIR(dir) && !IS_CURRENT_DIR(path)) { + /* completing file in default_path + (path not set, and leave it that way) */ + g_free_and_null(dir); + } + basename = g_basename(path); len = strlen(basename); @@ -264,14 +316,15 @@ GList *filename_complete(const char *path) } if (len == 0 || strncmp(dp->d_name, basename, len) == 0) { - name = g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name); - list = list_add_file(list, name); + name = dir == NULL ? g_strdup(dp->d_name) : + g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name); + list = list_add_file(list, name, default_path); g_free(name); } } closedir(dirp); - g_free(dir); + g_free_not_null(dir); return list; } @@ -332,11 +385,11 @@ static GList *completion_get_aliases(const char *alias, char cmdchar) /* get list of aliases from mainconfig */ node = iconfig_node_traverse("aliases", FALSE); - tmp = node == NULL ? NULL : node->value; + tmp = node == NULL ? NULL : config_node_first(node->value); len = strlen(alias); complist = NULL; - for (; tmp != NULL; tmp = tmp->next) { + for (; tmp != NULL; tmp = config_node_next(tmp)) { CONFIG_NODE *node = tmp->data; if (node->type != NODE_TYPE_KEY) @@ -461,7 +514,7 @@ static char *line_get_command(const char *line, char **args, int aliases) } else { checkcmd = g_strndup(line, (int) (ptr-line)); - while (isspace(*ptr)) ptr++; + while (i_isspace(*ptr)) ptr++; cmdargs = ptr; } @@ -483,6 +536,8 @@ static char *line_get_command(const char *line, char **args, int aliases) *args = (char *) cmdargs; } while (ptr != NULL); + if (cmd != NULL) + g_strdown(cmd); return cmd; } @@ -502,7 +557,8 @@ static char *expand_aliases(const char *line) } static void sig_complete_word(GList **list, WINDOW_REC *window, - const char *word, const char *linestart, int *want_space) + const char *word, const char *linestart, + int *want_space) { const char *newword, *cmdchars; char *signal, *cmd, *args, *line; @@ -522,7 +578,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window, /* command completion? */ cmdchars = settings_get_str("cmdchars"); - if (strchr(cmdchars, *word) && *linestart == '\0') { + if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) { /* complete /command */ *list = completion_get_commands(word+1, *word); @@ -578,6 +634,39 @@ static void sig_complete_word(GList **list, WINDOW_REC *window, g_free(line); } +static void sig_complete_erase(WINDOW_REC *window, const char *word, + const char *linestart) +{ + const char *cmdchars; + char *line, *cmd, *args, *signal; + + if (*linestart == '\0') + return; + + /* we only want to check for commands */ + cmdchars = settings_get_str("cmdchars"); + cmdchars = strchr(cmdchars, *linestart); + if (cmdchars == NULL) + return; + + /* check if there's aliases */ + line = linestart[1] == *cmdchars ? g_strdup(linestart+2) : + expand_aliases(linestart+1); + + cmd = line_get_command(line, &args, FALSE); + if (cmd == NULL) { + g_free(line); + return; + } + + signal = g_strconcat("complete erase command ", cmd, NULL); + signal_emit(signal, 3, window, word, args); + + g_free(signal); + g_free(cmd); + g_free(line); +} + static void sig_complete_set(GList **list, WINDOW_REC *window, const char *word, const char *line, int *want_space) { @@ -614,7 +703,7 @@ static void sig_complete_filename(GList **list, WINDOW_REC *window, if (*line != '\0') return; - *list = filename_complete(word); + *list = filename_complete(word, NULL); if (*list != NULL) { *want_space = FALSE; signal_stop(); @@ -652,10 +741,10 @@ void completion_init(void) chat_completion_init(); signal_add_first("complete word", (SIGNAL_FUNC) sig_complete_word); + signal_add_first("complete erase", (SIGNAL_FUNC) sig_complete_erase); signal_add("complete command set", (SIGNAL_FUNC) sig_complete_set); signal_add("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle); signal_add("complete command cat", (SIGNAL_FUNC) sig_complete_filename); - signal_add("complete command run", (SIGNAL_FUNC) sig_complete_filename); signal_add("complete command save", (SIGNAL_FUNC) sig_complete_filename); signal_add("complete command reload", (SIGNAL_FUNC) sig_complete_filename); signal_add("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename); @@ -670,10 +759,10 @@ void completion_deinit(void) chat_completion_deinit(); signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word); + signal_remove("complete erase", (SIGNAL_FUNC) sig_complete_erase); signal_remove("complete command set", (SIGNAL_FUNC) sig_complete_set); signal_remove("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle); signal_remove("complete command cat", (SIGNAL_FUNC) sig_complete_filename); - signal_remove("complete command run", (SIGNAL_FUNC) sig_complete_filename); signal_remove("complete command save", (SIGNAL_FUNC) sig_complete_filename); signal_remove("complete command reload", (SIGNAL_FUNC) sig_complete_filename); signal_remove("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename); diff --git a/apps/irssi/src/fe-common/core/completion.h b/apps/irssi/src/fe-common/core/completion.h index ef0fe06f..9a8b32cb 100644 --- a/apps/irssi/src/fe-common/core/completion.h +++ b/apps/irssi/src/fe-common/core/completion.h @@ -5,10 +5,12 @@ /* automatic word completion - called when space/enter is pressed */ char *auto_word_complete(const char *line, int *pos); -/* manual word completion - called when TAB is pressed */ -char *word_complete(WINDOW_REC *window, const char *line, int *pos); +/* manual word completion - called when TAB is pressed. if erase is TRUE, + the word is removed from completion list entirely (if possible) and + next completion is used */ +char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase); -GList *filename_complete(const char *path); +GList *filename_complete(const char *path, const char *default_path); void completion_init(void); void completion_deinit(void); diff --git a/apps/irssi/src/fe-common/core/fe-channels.c b/apps/irssi/src/fe-common/core/fe-channels.c index 67557b91..fe41ea30 100644 --- a/apps/irssi/src/fe-common/core/fe-channels.c +++ b/apps/irssi/src/fe-common/core/fe-channels.c @@ -29,6 +29,7 @@ #include "chat-protocols.h" #include "chatnets.h" +#include "servers.h" #include "channels.h" #include "channels-setup.h" #include "nicklist.h" @@ -74,16 +75,6 @@ static void signal_channel_destroyed(CHANNEL_REC *channel) window_auto_destroy(window); } -static void signal_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item) -{ - CHANNEL_REC *channel; - - g_return_if_fail(window != NULL); - - channel = CHANNEL(item); - if (channel != NULL) channel_destroy(channel); -} - static void sig_disconnected(SERVER_REC *server) { WINDOW_REC *window; @@ -246,8 +237,11 @@ static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { if (*data == '\0') cmd_channel_list_joined(); - else + else if (server != NULL && server_ischannel(server, data)) { + signal_emit("command join", 3, data, server, item); + } else { command_runsub("channel", data, server, item); + } } /* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots ] [-botcmd ] @@ -336,10 +330,10 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) TEXT_DEST_REC dest; GString *str; GSList *tmp; - char *format, *stripped; + char *format, *stripped, *prefix_format; char *linebuf, nickmode[2] = { 0, 0 }; int *columns, cols, rows, last_col_rows, col, row, max_width; - int item_extra, linebuf_size; + int item_extra, linebuf_size, formatnum; window = window_find_closest(channel->server, channel->name, MSGLEVEL_CLIENTCRAP); @@ -355,10 +349,10 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) g_free(format); if (settings_get_int("names_max_width") > 0 && - max_width > settings_get_int("names_max_width")) + settings_get_int("names_max_width") < max_width) max_width = settings_get_int("names_max_width"); - /* remove width of timestamp from max_width */ + /* remove width of the timestamp from max_width */ format_create_dest(&dest, channel->server, channel->name, MSGLEVEL_CLIENTCRAP, NULL); format = format_get_line_start(current_theme, &dest, time(NULL)); @@ -369,6 +363,22 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) g_free(format); } + /* remove width of the prefix from max_width */ + prefix_format = format_get_text(MODULE_NAME, NULL, + channel->server, channel->name, + TXT_NAMES_PREFIX, channel->name); + if (prefix_format != NULL) { + stripped = strip_codes(prefix_format); + max_width -= strlen(stripped); + g_free(stripped); + } + + if (max_width <= 0) { + /* we should always have at least some space .. if we + really don't, it won't show properly anyway. */ + max_width = 10; + } + /* calculate columns */ cols = get_max_column_count(nicklist, get_nick_length, max_width, settings_get_int("names_max_columns"), @@ -380,15 +390,22 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) if (last_col_rows == 0) last_col_rows = rows; - str = g_string_new(NULL); + str = g_string_new(prefix_format); linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size); col = 0; row = 0; for (tmp = nicklist; tmp != NULL; tmp = tmp->next) { NICK_REC *rec = tmp->data; - nickmode[0] = rec->op ? '@' : rec->voice ? '+' : ' '; - + if (rec->op) + nickmode[0] = '@'; + else if (rec->halfop) + nickmode[0] = '%'; + else if (rec->voice) + nickmode[0] = '+'; + else + nickmode[0] = ' '; + if (linebuf_size < columns[col]-item_extra+1) { linebuf_size = (columns[col]-item_extra+1)*2; linebuf = g_realloc(linebuf, linebuf_size); @@ -397,9 +414,13 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) linebuf[columns[col]-item_extra] = '\0'; memcpy(linebuf, rec->nick, strlen(rec->nick)); + formatnum = rec->op ? TXT_NAMES_NICK_OP : + rec->halfop ? TXT_NAMES_NICK_HALFOP : + rec->voice ? TXT_NAMES_NICK_VOICE : + TXT_NAMES_NICK; format = format_get_text(MODULE_NAME, NULL, channel->server, channel->name, - TXT_NAMES_NICK, nickmode, linebuf); + formatnum, nickmode, linebuf); g_string_append(str, format); g_free(format); @@ -407,6 +428,8 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) printtext(channel->server, channel->name, MSGLEVEL_CLIENTCRAP, "%s", str->str); g_string_truncate(str, 0); + if (prefix_format != NULL) + g_string_assign(str, prefix_format); col = 0; row++; if (row == last_col_rows) @@ -422,6 +445,7 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) g_slist_free(nicklist); g_string_free(str, TRUE); g_free_not_null(columns); + g_free_not_null(prefix_format); g_free(linebuf); } @@ -429,9 +453,9 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags) { NICK_REC *nick; GSList *tmp, *nicklist, *sorted; - int nicks, normal, voices, ops; + int nicks, normal, voices, halfops, ops; - nicks = normal = voices = ops = 0; + nicks = normal = voices = halfops = ops = 0; nicklist = nicklist_getnicks(channel); sorted = NULL; @@ -445,6 +469,7 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags) if ((flags & CHANNEL_NICKLIST_FLAG_OPS) == 0) continue; } else if (nick->halfop) { + halfops++; if ((flags & CHANNEL_NICKLIST_FLAG_HALFOPS) == 0) continue; } else if (nick->voice) { @@ -463,17 +488,19 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags) g_slist_free(nicklist); /* display the nicks */ - printformat(channel->server, channel->name, - MSGLEVEL_CRAP, TXT_NAMES, channel->name, ""); - display_sorted_nicks(channel, sorted); + if ((flags & CHANNEL_NICKLIST_FLAG_COUNT) == 0) { + printformat(channel->server, channel->name, + MSGLEVEL_CRAP, TXT_NAMES, channel->name, ""); + display_sorted_nicks(channel, sorted); + } g_slist_free(sorted); printformat(channel->server, channel->name, MSGLEVEL_CRAP, TXT_ENDOFNAMES, - channel->name, nicks, ops, voices, normal); + channel->name, nicks, ops, halfops, voices, normal); } -/* SYNTAX: NAMES [-ops -halfops -voices -normal] [ | **] */ +/* SYNTAX: NAMES [-count | -ops -halfops -voices -normal] [ | **] */ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { CHANNEL_REC *chanrec; @@ -507,6 +534,8 @@ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item) flags |= CHANNEL_NICKLIST_FLAG_VOICES; if (g_hash_table_lookup(optlist, "normal") != NULL) flags |= CHANNEL_NICKLIST_FLAG_NORMAL; + if (g_hash_table_lookup(optlist, "count") != NULL) + flags |= CHANNEL_NICKLIST_FLAG_COUNT; if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL; @@ -572,7 +601,6 @@ void fe_channels_init(void) signal_add("channel created", (SIGNAL_FUNC) signal_channel_created); signal_add("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed); - signal_add_last("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy); signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed); signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected); @@ -587,7 +615,7 @@ void fe_channels_init(void) command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle); command_set_options("channel add", "auto noauto -bots -botcmd"); - command_set_options("names", "ops halfops voices normal"); + command_set_options("names", "count ops halfops voices normal"); command_set_options("join", "window"); } @@ -595,7 +623,6 @@ void fe_channels_deinit(void) { signal_remove("channel created", (SIGNAL_FUNC) signal_channel_created); signal_remove("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed); - signal_remove("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy); signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed); signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected); diff --git a/apps/irssi/src/fe-common/core/fe-channels.h b/apps/irssi/src/fe-common/core/fe-channels.h index a8aa697e..e9a03a12 100644 --- a/apps/irssi/src/fe-common/core/fe-channels.h +++ b/apps/irssi/src/fe-common/core/fe-channels.h @@ -6,6 +6,7 @@ #define CHANNEL_NICKLIST_FLAG_VOICES 0x04 #define CHANNEL_NICKLIST_FLAG_NORMAL 0x08 #define CHANNEL_NICKLIST_FLAG_ALL 0x0f +#define CHANNEL_NICKLIST_FLAG_COUNT 0x10 void fe_channels_nicklist(CHANNEL_REC *channel, int flags); diff --git a/apps/irssi/src/fe-common/core/fe-common-core.c b/apps/irssi/src/fe-common/core/fe-common-core.c index 24836905..6b57098a 100644 --- a/apps/irssi/src/fe-common/core/fe-common-core.c +++ b/apps/irssi/src/fe-common/core/fe-common-core.c @@ -24,10 +24,13 @@ #include "misc.h" #include "levels.h" #include "settings.h" +#include "irssi-version.h" +#include "servers.h" #include "channels.h" #include "servers-setup.h" +#include "autorun.h" #include "fe-queries.h" #include "hilight-text.h" #include "command-history.h" @@ -51,9 +54,6 @@ static int no_autoconnect; static char *cmdline_nick; static char *cmdline_hostname; -void autorun_init(void); -void autorun_deinit(void); - void fe_core_log_init(void); void fe_core_log_deinit(void); @@ -96,6 +96,13 @@ void window_commands_deinit(void); void fe_core_commands_init(void); void fe_core_commands_deinit(void); +static void print_version(void) +{ + printf(PACKAGE" " IRSSI_VERSION" (%d %04d)\n", + IRSSI_VERSION_DATE, IRSSI_VERSION_TIME); + exit(0); +} + static void sig_connected(SERVER_REC *server) { MODULE_DATA_SET(server, g_new0(MODULE_SERVER_REC, 1)); @@ -104,6 +111,7 @@ static void sig_connected(SERVER_REC *server) static void sig_disconnected(SERVER_REC *server) { g_free(MODULE_DATA(server)); + MODULE_DATA_UNSET(server); } static void sig_channel_created(CHANNEL_REC *channel) @@ -114,19 +122,26 @@ static void sig_channel_created(CHANNEL_REC *channel) static void sig_channel_destroyed(CHANNEL_REC *channel) { g_free(MODULE_DATA(channel)); + MODULE_DATA_UNSET(channel); } void fe_common_core_init(void) { + static struct poptOption version_options[] = { + { NULL, '\0', POPT_ARG_CALLBACK, (void *)&print_version, '\0', NULL }, + { "version", 'v', POPT_ARG_NONE, NULL, 0, "Display irssi version" }, + { NULL, '\0', 0, NULL } + }; + static struct poptOption options[] = { + { NULL, '\0', POPT_ARG_INCLUDE_TABLE, version_options, 0, NULL, NULL }, + POPT_AUTOHELP { "connect", 'c', POPT_ARG_STRING, &autocon_server, 0, "Automatically connect to server/ircnet", "SERVER" }, - { "password", 'w', POPT_ARG_STRING, &autocon_password, 0, "Autoconnect password", "SERVER" }, + { "password", 'w', POPT_ARG_STRING, &autocon_password, 0, "Autoconnect password", "PASSWORD" }, { "port", 'p', POPT_ARG_INT, &autocon_port, 0, "Autoconnect port", "PORT" }, { "noconnect", '!', POPT_ARG_NONE, &no_autoconnect, 0, "Disable autoconnecting", NULL }, - /* { "nick", 'n', POPT_ARG_STRING, &cmdline_nick, 0, "Specify nick to use", NULL }, { "hostname", 'h', POPT_ARG_STRING, &cmdline_hostname, 0, "Specify host name to use", NULL }, - */ { NULL, '\0', 0, NULL } }; @@ -139,7 +154,7 @@ void fe_common_core_init(void) args_register(options); settings_add_bool("lookandfeel", "timestamps", TRUE); - settings_add_bool("lookandfeel", "msgs_timestamps", FALSE); + settings_add_str("lookandfeel", "timestamp_level", "ALL"); settings_add_int("lookandfeel", "timestamp_timeout", 0); settings_add_bool("lookandfeel", "bell_beeps", FALSE); @@ -148,6 +163,7 @@ void fe_common_core_init(void) settings_add_bool("lookandfeel", "beep_when_away", TRUE); settings_add_bool("lookandfeel", "hide_text_style", FALSE); + settings_add_bool("lookandfeel", "hide_mirc_colors", FALSE); settings_add_bool("lookandfeel", "hide_server_tags", FALSE); settings_add_bool("lookandfeel", "use_status_window", TRUE); @@ -156,7 +172,6 @@ void fe_common_core_init(void) themes_init(); theme_register(fecommon_core_formats); - autorun_init(); command_history_init(); completion_init(); keyboard_init(); @@ -193,11 +208,12 @@ void fe_common_core_init(void) signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected); signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created); signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + + module_register("core", "fe"); } void fe_common_core_deinit(void) { - autorun_deinit(); hilight_text_deinit(); command_history_deinit(); completion_deinit(); @@ -331,20 +347,25 @@ static void autoconnect_servers(void) void fe_common_core_finish_init(void) { + int setup_changed; + signal_emit("irssi init read settings", 0); #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif + setup_changed = FALSE; if (cmdline_nick != NULL) { /* override nick found from setup */ settings_set_str("nick", cmdline_nick); + setup_changed = TRUE; } if (cmdline_hostname != NULL) { /* override host name found from setup */ settings_set_str("hostname", cmdline_hostname); + setup_changed = TRUE; } create_windows(); @@ -355,5 +376,9 @@ void fe_common_core_finish_init(void) G_LOG_LEVEL_WARNING), (GLogFunc) glog_func, NULL); + if (setup_changed) + signal_emit("setup changed", 0); + + autorun_startup(); autoconnect_servers(); } diff --git a/apps/irssi/src/fe-common/core/fe-core-commands.c b/apps/irssi/src/fe-common/core/fe-core-commands.c index 7b8f7f9b..cf7938ea 100644 --- a/apps/irssi/src/fe-common/core/fe-core-commands.c +++ b/apps/irssi/src/fe-common/core/fe-core-commands.c @@ -27,6 +27,7 @@ #include "line-split.h" #include "settings.h" #include "irssi-version.h" +#include "servers.h" #include "fe-windows.h" #include "printtext.h" @@ -45,6 +46,7 @@ static int ret_texts[] = { TXT_NOT_JOINED, TXT_CHAN_NOT_FOUND, TXT_CHAN_NOT_SYNCED, + TXT_ILLEGAL_PROTO, TXT_NOT_GOOD_IDEA }; @@ -91,11 +93,15 @@ static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item) /* SYNTAX: VERSION */ static void cmd_version(char *data) { + char time[10]; + g_return_if_fail(data != NULL); if (*data == '\0') { + g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME); printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - "Client: "PACKAGE" " IRSSI_VERSION); + "Client: "PACKAGE" " IRSSI_VERSION" (%d %s)", + IRSSI_VERSION_DATE, time); } } @@ -144,6 +150,43 @@ static void cmd_beep(void) signal_emit("beep", 0); } +static void cmd_nick(const char *data, SERVER_REC *server) +{ + g_return_if_fail(data != NULL); + + if (*data != '\0') return; + if (server == NULL || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + /* display current nick */ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick); + signal_stop(); +} + +static void cmd_join(const char *data, SERVER_REC *server) +{ + GHashTable *optlist; + char *channels; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, + "join", &optlist, &channels)) + return; + + server = cmd_options_get_server("join", optlist, server); + if (g_hash_table_lookup(optlist, "invite") && + server != NULL && server->last_invite == NULL) { + /* ..all this trouble just to print this error message */ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED); + signal_stop(); + } + + cmd_params_free(free_arg); +} + static void sig_stop(void) { signal_stop(); @@ -153,6 +196,12 @@ static void event_command(const char *data) { const char *cmdchar; + if (*data == '\0') { + /* empty line, forget it. */ + signal_stop(); + return; + } + /* save current command line */ current_cmdline = data; @@ -170,7 +219,6 @@ static void event_command(const char *data) hide_output = TRUE; signal_add_first("print starting", (SIGNAL_FUNC) sig_stop); signal_add_first("print format", (SIGNAL_FUNC) sig_stop); - signal_add_first("print text stripped", (SIGNAL_FUNC) sig_stop); signal_add_first("print text", (SIGNAL_FUNC) sig_stop); } } @@ -181,7 +229,6 @@ static void event_command_last(const char *data) hide_output = FALSE; signal_remove("print starting", (SIGNAL_FUNC) sig_stop); signal_remove("print format", (SIGNAL_FUNC) sig_stop); - signal_remove("print text stripped", (SIGNAL_FUNC) sig_stop); signal_remove("print text", (SIGNAL_FUNC) sig_stop); } } @@ -278,6 +325,8 @@ void fe_core_commands_init(void) command_bind("version", NULL, (SIGNAL_FUNC) cmd_version); command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat); command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep); + command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick); + command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_join); signal_add("send command", (SIGNAL_FUNC) event_command); signal_add_last("send command", (SIGNAL_FUNC) event_command_last); @@ -294,6 +343,8 @@ void fe_core_commands_deinit(void) command_unbind("version", (SIGNAL_FUNC) cmd_version); command_unbind("cat", (SIGNAL_FUNC) cmd_cat); command_unbind("beep", (SIGNAL_FUNC) cmd_beep); + command_unbind("nick", (SIGNAL_FUNC) cmd_nick); + command_unbind("join", (SIGNAL_FUNC) cmd_join); signal_remove("send command", (SIGNAL_FUNC) event_command); signal_remove("send command", (SIGNAL_FUNC) event_command_last); diff --git a/apps/irssi/src/fe-common/core/fe-exec.c b/apps/irssi/src/fe-common/core/fe-exec.c index bc8f4691..80972634 100644 --- a/apps/irssi/src/fe-common/core/fe-exec.c +++ b/apps/irssi/src/fe-common/core/fe-exec.c @@ -1,7 +1,7 @@ /* fe-exec.c : irssi - Copyright (C) 2000 Timo Sirainen + Copyright (C) 2000-2001 Timo Sirainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ */ #include "module.h" +#include "modules.h" #include "signals.h" #include "commands.h" #include "pidwait.h" @@ -35,9 +36,24 @@ #include #include -static GSList *processes; +GSList *processes; static int signal_exec_input; +static void exec_wi_destroy(EXEC_WI_REC *rec) +{ + g_return_if_fail(rec != NULL); + + if (rec->destroying) return; + rec->destroying = TRUE; + + if (window_item_window((WI_ITEM_REC *) rec) != NULL) + window_item_destroy((WI_ITEM_REC *) rec); + + MODULE_DATA_DEINIT(rec); + g_free(rec->name); + g_free(rec); +} + static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec) { EXEC_WI_REC *item; @@ -47,11 +63,11 @@ static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec) item = g_new0(EXEC_WI_REC, 1); item->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "EXEC"); + item->destroy = (void (*) (WI_ITEM_REC *)) exec_wi_destroy; item->name = rec->name != NULL ? g_strdup_printf("%%%s", rec->name) : g_strdup_printf("%%%d", rec->id); - item->window = window; item->createtime = time(NULL); item->process = rec; @@ -60,21 +76,6 @@ static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec) return item; } -static void exec_wi_destroy(EXEC_WI_REC *rec) -{ - g_return_if_fail(rec != NULL); - - if (rec->destroying) return; - rec->destroying = TRUE; - - if (window_item_window((WI_ITEM_REC *) rec) != NULL) - window_item_destroy((WI_ITEM_REC *) rec); - - MODULE_DATA_DEINIT(rec); - g_free(rec->name); - g_free(rec); -} - static int process_get_new_id(void) { PROCESS_REC *rec; @@ -368,7 +369,7 @@ static void handle_exec(const char *args, GHashTable *optlist, WI_ITEM_REC *item) { PROCESS_REC *rec; - char *target; + char *target, *level; int notice, signum, interactive; /* check that there's no unknown options. we allowed them @@ -491,6 +492,9 @@ static void handle_exec(const char *args, GHashTable *optlist, rec->silent = g_hash_table_lookup(optlist, "-") != NULL; rec->name = g_strdup(g_hash_table_lookup(optlist, "name")); + level = g_hash_table_lookup(optlist, "level"); + rec->level = level == NULL ? MSGLEVEL_CLIENTCRAP : level2bits(level); + rec->read_tag = g_input_add(rec->in, G_INPUT_READ, (GInputFunction) sig_exec_input_reader, rec); @@ -526,12 +530,17 @@ static void cmd_exec(const char *data, SERVER_REC *server, WI_ITEM_REC *item) static void sig_pidwait(void *pid, void *statusp) { PROCESS_REC *rec; + char *str; int status = GPOINTER_TO_INT(statusp); rec = process_find_pid(GPOINTER_TO_INT(pid)); if (rec == NULL) return; - /* process exited */ + /* process exited - print the last line if + there wasn't a newline at end. */ + if (line_split("\n", 1, &str, &rec->databuf) > 0 && *str != '\0') + signal_emit_id(signal_exec_input, 2, rec, str); + if (!rec->silent) { if (WIFSIGNALED(status)) { status = WTERMSIG(status); @@ -568,11 +577,10 @@ static void sig_exec_input(PROCESS_REC *rec, const char *text) 3, str, server, item); g_free(str); } else if (rec->target_item != NULL) { - printtext(NULL, rec->target_item->name, MSGLEVEL_CLIENTCRAP, - "%s", text); + printtext(NULL, rec->target_item->name, + rec->level, "%s", text); } else { - printtext_window(rec->target_win, MSGLEVEL_CLIENTCRAP, - "%s", text); + printtext_window(rec->target_win, rec->level, "%s", text); } } @@ -590,14 +598,6 @@ static void sig_window_destroyed(WINDOW_REC *window) } } -static void sig_window_item_destroyed(WINDOW_REC *window, EXEC_WI_REC *item) -{ - if (IS_EXEC_WI(item)) { - item->process->target_item = NULL; - exec_wi_destroy(item); - } -} - static void event_text(const char *data, SERVER_REC *server, EXEC_WI_REC *item) { if (!IS_EXEC_WI(item)) return; @@ -610,13 +610,12 @@ static void event_text(const char *data, SERVER_REC *server, EXEC_WI_REC *item) void fe_exec_init(void) { command_bind("exec", NULL, (SIGNAL_FUNC) cmd_exec); - command_set_options("exec", "!- interactive nosh +name out +msg +notice +in window close"); + command_set_options("exec", "!- interactive nosh +name out +msg +notice +in window close +level"); signal_exec_input = signal_get_uniq_id("exec input"); signal_add("pidwait", (SIGNAL_FUNC) sig_pidwait); signal_add("exec input", (SIGNAL_FUNC) sig_exec_input); signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); - signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroyed); signal_add_first("send text", (SIGNAL_FUNC) event_text); } @@ -636,6 +635,5 @@ void fe_exec_deinit(void) signal_remove("pidwait", (SIGNAL_FUNC) sig_pidwait); signal_remove("exec input", (SIGNAL_FUNC) sig_exec_input); signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); - signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroyed); signal_remove("send text", (SIGNAL_FUNC) event_text); } diff --git a/apps/irssi/src/fe-common/core/fe-exec.h b/apps/irssi/src/fe-common/core/fe-exec.h index 7f569ece..bebd1f82 100644 --- a/apps/irssi/src/fe-common/core/fe-exec.h +++ b/apps/irssi/src/fe-common/core/fe-exec.h @@ -30,6 +30,7 @@ struct PROCESS_REC { LINEBUF_REC *databuf; int read_tag; + int level; /* what level to use when printing the text */ char *target; /* send text with /msg ... */ WINDOW_REC *target_win; /* print text to this window */ EXEC_WI_REC *target_item; /* print text to this exec window item */ @@ -39,6 +40,8 @@ struct PROCESS_REC { unsigned int silent:1; /* don't print "process exited with level xx" */ }; +extern GSList *processes; + void fe_exec_init(void); void fe_exec_deinit(void); diff --git a/apps/irssi/src/fe-common/core/fe-expandos.c b/apps/irssi/src/fe-common/core/fe-expandos.c index 7eb145db..c14ac7c3 100644 --- a/apps/irssi/src/fe-common/core/fe-expandos.c +++ b/apps/irssi/src/fe-common/core/fe-expandos.c @@ -25,6 +25,9 @@ /* Window ref# */ static char *expando_winref(SERVER_REC *server, void *item, int *free_ret) { + if (active_win == NULL) + return ""; + *free_ret = TRUE; return g_strdup_printf("%d", active_win->refnum); } @@ -32,6 +35,9 @@ static char *expando_winref(SERVER_REC *server, void *item, int *free_ret) /* Window name */ static char *expando_winname(SERVER_REC *server, void *item, int *free_ret) { + if (active_win == NULL) + return ""; + return active_win->name; } @@ -41,6 +47,7 @@ void fe_expandos_init(void) "window changed", EXPANDO_ARG_NONE, "window refnum changed", EXPANDO_ARG_WINDOW, NULL); expando_create("winname", expando_winname, + "window changed", EXPANDO_ARG_NONE, "window name changed", EXPANDO_ARG_WINDOW, NULL); } diff --git a/apps/irssi/src/fe-common/core/fe-ignore.c b/apps/irssi/src/fe-common/core/fe-ignore.c index 3523d527..55a88266 100644 --- a/apps/irssi/src/fe-common/core/fe-ignore.c +++ b/apps/irssi/src/fe-common/core/fe-ignore.c @@ -54,9 +54,18 @@ static void ignore_print(int index, IGNORE_REC *rec) options = g_string_new(NULL); if (rec->exception) g_string_sprintfa(options, "-except "); - if (rec->regexp) g_string_sprintfa(options, "-regexp "); + if (rec->regexp) { + g_string_sprintfa(options, "-regexp "); +#ifdef HAVE_REGEX_H + if (!rec->regexp_compiled) + g_string_sprintfa(options, "[INVALID!] "); +#endif + } if (rec->fullword) g_string_sprintfa(options, "-full "); if (rec->replies) g_string_sprintfa(options, "-replies "); + if (rec->pattern != NULL) + g_string_sprintfa(options, "-pattern %s ", rec->pattern); + if (options->len > 1) g_string_truncate(options, options->len-1); printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, @@ -73,6 +82,12 @@ static void cmd_ignore_show(void) GSList *tmp; int index; + if (ignores == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_IGNORE_NO_IGNORES); + return; + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_IGNORE_HEADER); index = 1; for (tmp = ignores; tmp != NULL; tmp = tmp->next, index++) { @@ -112,7 +127,7 @@ static void cmd_ignore(const char *data) if (*levels == '\0') levels = "ALL"; if (active_win->active_server != NULL && - active_win->active_server->ischannel(mask)) { + server_ischannel(active_win->active_server, mask)) { chanarg = mask; mask = NULL; } @@ -134,6 +149,18 @@ static void cmd_ignore(const char *data) } rec->level = combine_level(rec->level, levels); + + if (new_ignore && rec->level == 0) { + /* tried to unignore levels from nonexisting ignore */ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_IGNORE_NOT_FOUND, rec->mask); + g_free(rec->mask); + g_strfreev(rec->channels); + g_free(rec); + cmd_params_free(free_arg); + return; + } + rec->pattern = (patternarg == NULL || *patternarg == '\0') ? NULL : g_strdup(patternarg); rec->exception = g_hash_table_lookup(optlist, "except") != NULL; @@ -144,11 +171,6 @@ static void cmd_ignore(const char *data) if (timestr != NULL) rec->unignore_time = time(NULL)+atoi(timestr); - if (rec->level == 0) { - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_UNIGNORED, - rec->mask == NULL ? "*" : rec->mask); - } - if (new_ignore) ignore_add_rec(rec); else @@ -162,24 +184,29 @@ static void cmd_unignore(const char *data) { IGNORE_REC *rec; GSList *tmp; + char *mask; + void *free_arg; - if (*data == '\0') + if (!cmd_get_params(data, &free_arg, 1, &mask)) + return; + + if (*mask == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); - if (is_numeric(data, ' ')) { + if (is_numeric(mask, ' ')) { /* with index number */ - tmp = g_slist_nth(ignores, atoi(data)-1); + tmp = g_slist_nth(ignores, atoi(mask)-1); rec = tmp == NULL ? NULL : tmp->data; } else { /* with mask */ const char *chans[2] = { "*", NULL }; if (active_win->active_server != NULL && - active_win->active_server->ischannel(data)) { - chans[0] = data; - data = NULL; + server_ischannel(active_win->active_server, mask)) { + chans[0] = mask; + mask = NULL; } - rec = ignore_find("*", data, (char **) chans); + rec = ignore_find("*", mask, (char **) chans); } if (rec != NULL) { @@ -187,8 +214,9 @@ static void cmd_unignore(const char *data) ignore_update_rec(rec); } else { printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_IGNORE_NOT_FOUND, data); + TXT_IGNORE_NOT_FOUND, mask); } + cmd_params_free(free_arg); } static void sig_ignore_created(IGNORE_REC *rec) @@ -196,7 +224,7 @@ static void sig_ignore_created(IGNORE_REC *rec) char *key, *levels; key = ignore_get_key(rec); - levels = bits2level(rec->level); + levels = bits2level(rec->level); printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_IGNORED, key, levels); g_free(key); diff --git a/apps/irssi/src/fe-common/core/fe-log.c b/apps/irssi/src/fe-common/core/fe-log.c index 60d96327..a0967e1a 100644 --- a/apps/irssi/src/fe-common/core/fe-log.c +++ b/apps/irssi/src/fe-common/core/fe-log.c @@ -22,12 +22,14 @@ #include "module-formats.h" #include "signals.h" #include "commands.h" +#include "chat-protocols.h" #include "servers.h" #include "levels.h" #include "misc.h" #include "log.h" #include "special-vars.h" #include "settings.h" +#include "lib-config/iconfig.h" #include "fe-windows.h" #include "window-items.h" @@ -38,8 +40,6 @@ /* close autologs after 5 minutes of inactivity */ #define AUTOLOG_INACTIVITY_CLOSE (60*5) -#define LOG_DIR_CREATE_MODE 0770 - static int autolog_level; static int autoremove_tag; static const char *autolog_path; @@ -48,6 +48,11 @@ static THEME_REC *log_theme; static int skip_next_printtext; static const char *log_theme_name; +static char *log_colorizer_strip(const char *str) +{ + return strip_codes(str); +} + static void log_add_targets(LOG_REC *log, const char *targets, const char *tag) { char **tmp, **items; @@ -64,7 +69,8 @@ static void log_add_targets(LOG_REC *log, const char *targets, const char *tag) } /* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-] - [-targets ] [] */ + [-targets ] [-colors] + [] */ static void cmd_log_open(const char *data) { SERVER_REC *server; @@ -90,8 +96,12 @@ static void cmd_log_open(const char *data) if (g_hash_table_lookup(optlist, "window")) { /* log by window ref# */ - ltoa(window, active_win->refnum); - log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, + targetarg = g_hash_table_lookup(optlist, "targets"); + if (targetarg == NULL || !is_numeric(targetarg, '\0')) { + ltoa(window, active_win->refnum); + targetarg = window; + } + log_item_add(log, LOG_ITEM_WINDOW_REFNUM, targetarg, servertag); } else { targetarg = g_hash_table_lookup(optlist, "targets"); @@ -102,6 +112,9 @@ static void cmd_log_open(const char *data) if (g_hash_table_lookup(optlist, "autoopen")) log->autoopen = TRUE; + if (g_hash_table_lookup(optlist, "colors") == NULL) + log->colorizer = log_colorizer_strip; + log_update(log); if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) { @@ -277,6 +290,7 @@ static void cmd_window_log(const char *data) active_win->name != NULL ? active_win->name : "Window", active_win->name != NULL ? "" : window); log = log_create_rec(fname, MSGLEVEL_ALL); + log->colorizer = log_colorizer_strip; log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL); log_update(log); g_free(fname); @@ -309,6 +323,7 @@ static void cmd_window_logfile(const char *data) } log = log_create_rec(data, MSGLEVEL_ALL); + log->colorizer = log_colorizer_strip; log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL); log_update(log); @@ -347,7 +362,9 @@ static void sig_server_disconnected(SERVER_REC *server) logitem = log->items->data; if (logitem->type == LOG_ITEM_TARGET && - g_strcasecmp(logitem->servertag, server->tag) == 0) + logitem->servertag != NULL && + g_strcasecmp(logitem->servertag, server->tag) == 0 && + server_ischannel(server, logitem->name)) /* kludge again.. so we won't close dcc chats */ log_close(log); } } @@ -364,29 +381,57 @@ static void autologs_close_all(void) } } -static void autolog_open(SERVER_REC *server, const char *target) +/* '%' -> '%%', '/' -> '_' */ +static char *escape_target(const char *target) +{ + char *str, *p; + + p = str = g_malloc(strlen(target)*2+1); + while (*target != '\0') { + if (*target == '/') + *p++ = '_'; + else { + if (*target == '%') + *p++ = '%'; + *p++ = *target; + } + + target++; + } + *p = '\0'; + + return str; +} + +static void autolog_open(SERVER_REC *server, const char *server_tag, + const char *target) { LOG_REC *log; - char *fname, *dir, *fixed_target, *tag; + char *fname, *dir, *fixed_target; - tag = server == NULL ? NULL : server->tag; - log = logs_find_item(LOG_ITEM_TARGET, target, tag, NULL); + log = logs_find_item(LOG_ITEM_TARGET, target, server_tag, NULL); if (log != NULL && !log->failed) { log_start_logging(log); return; } /* '/' -> '_' - don't even accidentally try to log to - #../../../file if you happen to join to such channel.. */ - fixed_target = g_strdup(target); - replace_chars(fixed_target, '/', '_'); + #../../../file if you happen to join to such channel.. + + '%' -> '%%' - so strftime() won't mess with them */ + fixed_target = escape_target(target); + if (CHAT_PROTOCOL(server)->case_insensitive) + g_strdown(fixed_target); + fname = parse_special_string(autolog_path, server, NULL, fixed_target, NULL, 0); g_free(fixed_target); if (log_find(fname) == NULL) { log = log_create_rec(fname, autolog_level); - log_item_add(log, LOG_ITEM_TARGET, target, tag); + if (!settings_get_bool("autolog_colors")) + log->colorizer = log_colorizer_strip; + log_item_add(log, LOG_ITEM_TARGET, target, server_tag); dir = g_dirname(log->real_fname); mkpath(dir, LOG_DIR_CREATE_MODE); @@ -399,23 +444,27 @@ static void autolog_open(SERVER_REC *server, const char *target) g_free(fname); } -static void autolog_open_check(SERVER_REC *server, const char *target, - int level) +static void autolog_open_check(SERVER_REC *server, const char *server_tag, + const char *target, int level) { char **targets, **tmp; - if (level == MSGLEVEL_PARTS || /* FIXME: kind of a kludge, but we don't want to reopen logs when we're parting the channel with /WINDOW CLOSE.. */ + /* FIXME: kind of a kludge, but we don't want to reopen logs when + we're parting the channel with /WINDOW CLOSE.. Maybe a small + timeout would be nice instead of immediately closing the log file + after "window item destroyed" */ + if (level == MSGLEVEL_PARTS || (autolog_level & level) == 0 || target == NULL || *target == '\0') return; /* there can be multiple targets separated with comma */ targets = g_strsplit(target, ",", -1); for (tmp = targets; *tmp != NULL; tmp++) - autolog_open(server, *tmp); + autolog_open(server, server_tag, *tmp); g_strfreev(targets); } -static void log_single_line(WINDOW_REC *window, void *server, +static void log_single_line(WINDOW_REC *window, const char *server_tag, const char *target, int level, const char *text) { char windownum[MAX_INT_STRLEN]; @@ -426,29 +475,31 @@ static void log_single_line(WINDOW_REC *window, void *server, ltoa(windownum, window->refnum); log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, windownum, NULL, NULL); - if (log != NULL) log_write_rec(log, text, level); + if (log != NULL) { + log_write_rec(log, text, level); + } if (target == NULL) - log_file_write(server, NULL, level, text, FALSE); + log_file_write(server_tag, NULL, level, text, FALSE); else { /* there can be multiple items separated with comma */ targets = g_strsplit(target, ",", -1); for (tmp = targets; *tmp != NULL; tmp++) - log_file_write(server, *tmp, level, text, FALSE); + log_file_write(server_tag, *tmp, level, text, FALSE); g_strfreev(targets); } } -static void log_line(WINDOW_REC *window, SERVER_REC *server, - const char *target, int level, const char *text) +static void log_line(TEXT_DEST_REC *dest, const char *text) { char **lines, **tmp; - if (level == MSGLEVEL_NEVER) + if (dest->level == MSGLEVEL_NEVER) return; /* let autolog open the log records */ - autolog_open_check(server, target, level); + autolog_open_check(dest->server, dest->server_tag, + dest->target, dest->level); if (logs == NULL) return; @@ -457,25 +508,26 @@ static void log_line(WINDOW_REC *window, SERVER_REC *server, line at a time */ lines = g_strsplit(text, "\n", -1); for (tmp = lines; *tmp != NULL; tmp++) - log_single_line(window, server, target, level, *tmp); + log_single_line(dest->window, dest->server_tag, + dest->target, dest->level, *tmp); g_strfreev(lines); } -static void sig_printtext_stripped(TEXT_DEST_REC *dest, const char *text) +static void sig_printtext(TEXT_DEST_REC *dest, const char *text, + const char *stripped) { if (skip_next_printtext) { skip_next_printtext = FALSE; return; } - log_line(dest->window, dest->server, dest->target, - dest->level, text); + log_line(dest, text); } static void sig_print_format(THEME_REC *theme, const char *module, TEXT_DEST_REC *dest, void *formatnum, char **args) { - char *str, *stripped, *linestart, *tmp; + char *str, *linestart, *tmp; if (log_theme == NULL) { /* theme isn't loaded for some reason (/reload destroys it), @@ -500,10 +552,7 @@ static void sig_print_format(THEME_REC *theme, const char *module, g_free(tmp); /* strip colors from text, log it. */ - stripped = strip_codes(str); - log_line(dest->window, dest->server, dest->target, - dest->level, stripped); - g_free(stripped); + log_line(dest, str); } g_free(str); @@ -532,7 +581,7 @@ static int sig_autoremove(void) server = server_find_tag(logitem->servertag); if (logitem->type == LOG_ITEM_TARGET && - server != NULL && !server->ischannel(logitem->name)) + server != NULL && !server_ischannel(server, logitem->name)) log_close(log); } return 1; @@ -558,7 +607,29 @@ static void sig_log_locked(LOG_REC *log) static void sig_log_create_failed(LOG_REC *log) { printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_LOG_CREATE_FAILED, log->fname, g_strerror(errno)); + TXT_LOG_CREATE_FAILED, + log->real_fname, g_strerror(errno)); +} + +static void sig_log_new(LOG_REC *log) +{ + if (!settings_get_bool("awaylog_colors") && + strcmp(log->fname, settings_get_str("awaylog_file")) == 0) + log->colorizer = log_colorizer_strip; +} + +static void sig_log_config_read(LOG_REC *log, CONFIG_NODE *node) +{ + if (!config_node_get_bool(node, "colors", FALSE)) + log->colorizer = log_colorizer_strip; +} + +static void sig_log_config_save(LOG_REC *log, CONFIG_NODE *node) +{ + if (log->colorizer == NULL) + iconfig_node_set_bool(node, "colors", TRUE); + else + iconfig_node_set_str(node, "colors", NULL); } static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos) @@ -615,9 +686,11 @@ void fe_log_init(void) autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL); skip_next_printtext = FALSE; + settings_add_bool("log", "awaylog_colors", TRUE); + settings_add_bool("log", "autolog", FALSE); + settings_add_bool("log", "autolog_colors", FALSE); settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log"); settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps"); - settings_add_bool("log", "autolog", FALSE); settings_add_str("log", "log_theme", ""); autolog_level = 0; @@ -631,17 +704,20 @@ void fe_log_init(void) command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop); command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log); command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile); - signal_add_first("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped); + signal_add_first("print text", (SIGNAL_FUNC) sig_printtext); signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy); signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed); signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); signal_add("log locked", (SIGNAL_FUNC) sig_log_locked); signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed); + signal_add("log new", (SIGNAL_FUNC) sig_log_new); + signal_add("log config read", (SIGNAL_FUNC) sig_log_config_read); + signal_add("log config save", (SIGNAL_FUNC) sig_log_config_save); signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show); signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed); signal_add("setup changed", (SIGNAL_FUNC) read_settings); - command_set_options("log open", "noopen autoopen -targets window"); + command_set_options("log open", "noopen autoopen -targets window colors"); } void fe_log_deinit(void) @@ -657,12 +733,15 @@ void fe_log_deinit(void) command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop); command_unbind("window log", (SIGNAL_FUNC) cmd_window_log); command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile); - signal_remove("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped); + signal_remove("print text", (SIGNAL_FUNC) sig_printtext); signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy); signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed); signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked); signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed); + signal_remove("log new", (SIGNAL_FUNC) sig_log_new); + signal_remove("log config read", (SIGNAL_FUNC) sig_log_config_read); + signal_remove("log config save", (SIGNAL_FUNC) sig_log_config_save); signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show); signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); diff --git a/apps/irssi/src/fe-common/core/fe-messages.c b/apps/irssi/src/fe-common/core/fe-messages.c index fa5e88af..4b4e4db4 100644 --- a/apps/irssi/src/fe-common/core/fe-messages.c +++ b/apps/irssi/src/fe-common/core/fe-messages.c @@ -27,15 +27,17 @@ #include "special-vars.h" #include "settings.h" -#include "window-items.h" -#include "fe-queries.h" +#include "servers.h" #include "channels.h" #include "nicklist.h" -#include "hilight-text.h" #include "ignore.h" + +#include "window-items.h" +#include "fe-queries.h" +#include "hilight-text.h" #include "printtext.h" -#define ishighalnum(c) ((unsigned char) (c) >= 128 || isalnum(c)) +#define ishighalnum(c) ((unsigned char) (c) >= 128 || i_isalnum(c)) static GHashTable *printnicks; @@ -65,7 +67,7 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text) /* check that the beginning marker starts a word, and that the matching end marker ends a word */ - if ((pos > 0 && !isspace(bgn[-1])) || !ishighalnum(bgn[1])) + if ((pos > 0 && !i_isspace(bgn[-1])) || !ishighalnum(bgn[1])) continue; if ((end = strchr(bgn+1, *bgn)) == NULL) continue; @@ -111,35 +113,28 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text) return ret; } -char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick) +static char *channel_get_nickmode_rec(NICK_REC *nickrec) { - NICK_REC *nickrec; char *emptystr; - g_return_val_if_fail(nick != NULL, NULL); - if (!settings_get_bool("show_nickmode")) return ""; emptystr = settings_get_bool("show_nickmode_empty") ? " " : ""; - nickrec = channel == NULL ? NULL : - nicklist_find(channel, nick); return nickrec == NULL ? emptystr : - (nickrec->op ? "@" : (nickrec->voice ? "+" : emptystr)); + nickrec->op ? "@" : + nickrec->halfop ? "%" : + nickrec->voice ? "+" : + emptystr; } -static char *channel_get_nickmode_rec(NICK_REC *nickrec) +char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick) { - char *emptystr; - - if (!settings_get_bool("show_nickmode")) - return ""; - - emptystr = settings_get_bool("show_nickmode_empty") ? " " : ""; + g_return_val_if_fail(nick != NULL, NULL); - return nickrec == NULL ? emptystr : - (nickrec->op ? "@" : (nickrec->voice ? "+" : emptystr)); + return channel_get_nickmode_rec(channel == NULL ? NULL : + nicklist_find(channel, nick)); } static void sig_message_public(SERVER_REC *server, const char *msg, @@ -167,8 +162,9 @@ static void sig_message_public(SERVER_REC *server, const char *msg, window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL) print_channel = TRUE; - level = MSGLEVEL_PUBLIC | (for_me || color != NULL ? - MSGLEVEL_HILIGHT : MSGLEVEL_NOHILIGHT); + level = MSGLEVEL_PUBLIC; + if (for_me || color != NULL) + level |= MSGLEVEL_HILIGHT; if (settings_get_bool("emphasis")) msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg); @@ -408,11 +404,16 @@ static void print_nick_change_channel(SERVER_REC *server, const char *channel, const char *address, int ownnick) { + int level; + if (ignore_check(server, oldnick, address, channel, newnick, MSGLEVEL_NICKS)) return; - printformat(server, channel, MSGLEVEL_NICKS, + level = MSGLEVEL_NICKS; + if (ownnick) level |= MSGLEVEL_NO_ACT; + + printformat(server, channel, level, ownnick ? TXT_YOUR_NICK_CHANGED : TXT_NICK_CHANGED, oldnick, newnick, channel); } @@ -444,20 +445,6 @@ static void print_nick_change(SERVER_REC *server, const char *newnick, msgprint = TRUE; } - for (tmp = server->queries; tmp != NULL; tmp = tmp->next) { - QUERY_REC *query = tmp->data; - WINDOW_REC *window = - window_item_window((WI_ITEM_REC *) query); - - if (g_strcasecmp(query->name, oldnick) != 0 || - g_slist_find(windows, window) != NULL) - continue; - - windows = g_slist_append(windows, window); - print_nick_change_channel(server, query->name, newnick, - oldnick, address, ownnick); - msgprint = TRUE; - } g_slist_free(windows); if (!msgprint && ownnick) { @@ -475,7 +462,12 @@ static void sig_message_nick(SERVER_REC *server, const char *newnick, static void sig_message_own_nick(SERVER_REC *server, const char *newnick, const char *oldnick, const char *address) { - print_nick_change(server, newnick, oldnick, address, TRUE); + if (!settings_get_bool("show_own_nickchange_once")) + print_nick_change(server, newnick, oldnick, address, TRUE); + else { + printformat(server, NULL, MSGLEVEL_NICKS, + TXT_YOUR_NICK_CHANGED, oldnick, newnick, ""); + } } static void sig_message_invite(SERVER_REC *server, const char *channel, @@ -637,19 +629,20 @@ void fe_messages_init(void) settings_add_bool("lookandfeel", "show_nickmode_empty", TRUE); settings_add_bool("lookandfeel", "print_active_channel", FALSE); settings_add_bool("lookandfeel", "show_quit_once", FALSE); - - signal_add("message public", (SIGNAL_FUNC) sig_message_public); - signal_add("message private", (SIGNAL_FUNC) sig_message_private); - signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public); - signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private); - signal_add("message join", (SIGNAL_FUNC) sig_message_join); - signal_add("message part", (SIGNAL_FUNC) sig_message_part); - signal_add("message quit", (SIGNAL_FUNC) sig_message_quit); - signal_add("message kick", (SIGNAL_FUNC) sig_message_kick); - signal_add("message nick", (SIGNAL_FUNC) sig_message_nick); - signal_add("message own_nick", (SIGNAL_FUNC) sig_message_own_nick); - signal_add("message invite", (SIGNAL_FUNC) sig_message_invite); - signal_add("message topic", (SIGNAL_FUNC) sig_message_topic); + settings_add_bool("lookandfeel", "show_own_nickchange_once", FALSE); + + signal_add_last("message public", (SIGNAL_FUNC) sig_message_public); + signal_add_last("message private", (SIGNAL_FUNC) sig_message_private); + signal_add_last("message own_public", (SIGNAL_FUNC) sig_message_own_public); + signal_add_last("message own_private", (SIGNAL_FUNC) sig_message_own_private); + signal_add_last("message join", (SIGNAL_FUNC) sig_message_join); + signal_add_last("message part", (SIGNAL_FUNC) sig_message_part); + signal_add_last("message quit", (SIGNAL_FUNC) sig_message_quit); + signal_add_last("message kick", (SIGNAL_FUNC) sig_message_kick); + signal_add_last("message nick", (SIGNAL_FUNC) sig_message_nick); + signal_add_last("message own_nick", (SIGNAL_FUNC) sig_message_own_nick); + signal_add_last("message invite", (SIGNAL_FUNC) sig_message_invite); + signal_add_last("message topic", (SIGNAL_FUNC) sig_message_topic); signal_add("nicklist new", (SIGNAL_FUNC) sig_nicklist_new); signal_add("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove); diff --git a/apps/irssi/src/fe-common/core/fe-modules.c b/apps/irssi/src/fe-common/core/fe-modules.c index e351dc71..5bbcf0b5 100644 --- a/apps/irssi/src/fe-common/core/fe-modules.c +++ b/apps/irssi/src/fe-common/core/fe-modules.c @@ -20,6 +20,7 @@ #include "module.h" #include "modules.h" +#include "modules-load.h" #include "module-formats.h" #include "signals.h" #include "commands.h" @@ -28,49 +29,99 @@ #include "printtext.h" -static void sig_module_error(void *number, const char *module, - const char *data) +#ifdef HAVE_GMODULE + +static void sig_module_error(void *number, const char *data, + const char *rootmodule, const char *submodule) { switch (GPOINTER_TO_INT(number)) { case MODULE_ERROR_ALREADY_LOADED: printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, - TXT_MODULE_ALREADY_LOADED, module); + TXT_MODULE_ALREADY_LOADED, rootmodule, submodule); break; case MODULE_ERROR_LOAD: printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, - TXT_MODULE_LOAD_ERROR, module, data); + TXT_MODULE_LOAD_ERROR, rootmodule, submodule, data); break; case MODULE_ERROR_INVALID: printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, - TXT_MODULE_INVALID, module); + TXT_MODULE_INVALID, rootmodule, submodule); break; } } -static void sig_module_loaded(MODULE_REC *rec) +static void sig_module_loaded(MODULE_REC *module, MODULE_FILE_REC *file) { printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_MODULE_LOADED, rec->name); + TXT_MODULE_LOADED, module->name, file->name); } -static void sig_module_unloaded(MODULE_REC *rec) +static void sig_module_unloaded(MODULE_REC *module, MODULE_FILE_REC *file) { - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_MODULE_UNLOADED, rec->name); + if (file != NULL && file->gmodule != NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_MODULE_UNLOADED, module->name, file->name); + } +} + +static int module_list_sub(MODULE_REC *module, int mark_type, + GString *submodules) +{ + GSList *tmp; + int all_dynamic, dynamic; + + g_string_truncate(submodules, 0); + + all_dynamic = -1; + for (tmp = module->files; tmp != NULL; tmp = tmp->next) { + MODULE_FILE_REC *file = tmp->data; + + /* if there's dynamic and static modules mixed, we'll need + to specify them separately */ + if (!mark_type) { + dynamic = file->gmodule != NULL; + if (all_dynamic != -1 && all_dynamic != dynamic) { + return module_list_sub(module, TRUE, + submodules); + } + all_dynamic = dynamic; + } + + if (submodules->len > 0) + g_string_append_c(submodules, ' '); + g_string_append(submodules, file->name); + if (mark_type) { + g_string_append(submodules, file->gmodule == NULL ? + " (static)" : " (dynamic)"); + } + } + + return all_dynamic; } static void cmd_load_list(void) { GSList *tmp; + GString *submodules; + const char *type; + int dynamic; + + submodules = g_string_new(NULL); - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_MODULE_HEADER); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_HEADER); for (tmp = modules; tmp != NULL; tmp = tmp->next) { MODULE_REC *rec = tmp->data; - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_MODULE_LINE, rec->name); + dynamic = module_list_sub(rec, FALSE, submodules); + type = dynamic == -1 ? "mixed" : + dynamic ? "dynamic" : "static"; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_MODULE_LINE, rec->name, type, submodules->str); } - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_MODULE_FOOTER); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_FOOTER); + + g_string_free(submodules, TRUE); } static char **module_prefixes_get(void) @@ -108,36 +159,67 @@ static void module_prefixes_free(char **list) g_free(list); } -/* SYNTAX: LOAD */ +/* SYNTAX: LOAD [] */ 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 */ +/* SYNTAX: UNLOAD [] */ static void cmd_unload(const char *data) { - MODULE_REC *rec; + MODULE_REC *module; + MODULE_FILE_REC *file; + char *rootmodule, *submodule; + void *free_arg; g_return_if_fail(data != NULL); - if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); - rec = module_find(data); - if (rec != NULL) module_unload(rec); + if (!cmd_get_params(data, &free_arg, 2 , &rootmodule, &submodule)) + return; + if (*rootmodule == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + module = module_find(rootmodule); + if (module != NULL) { + if (*submodule == '\0') + module_unload(module); + else { + file = module_file_find(module, submodule); + if (file != NULL) + module_file_unload(file); + else + module = NULL; + } + } + + if (module == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_MODULE_NOT_LOADED, rootmodule, submodule); + } + + cmd_params_free(free_arg); } void fe_modules_init(void) @@ -159,3 +241,22 @@ void fe_modules_deinit(void) command_unbind("load", (SIGNAL_FUNC) cmd_load); command_unbind("unload", (SIGNAL_FUNC) cmd_unload); } + +#else /* !HAVE_GMODULE */ + +static void cmd_load(const char *data) +{ + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Dynamic modules loading not supported"); +} + +void fe_modules_init(void) +{ + command_bind("load", NULL, (SIGNAL_FUNC) cmd_load); +} + +void fe_modules_deinit(void) +{ + command_unbind("load", (SIGNAL_FUNC) cmd_load); +} +#endif diff --git a/apps/irssi/src/fe-common/core/fe-queries.c b/apps/irssi/src/fe-common/core/fe-queries.c index 1924464a..44d7ef01 100644 --- a/apps/irssi/src/fe-common/core/fe-queries.c +++ b/apps/irssi/src/fe-common/core/fe-queries.c @@ -27,6 +27,7 @@ #include "settings.h" #include "chat-protocols.h" +#include "servers.h" #include "queries.h" #include "fe-windows.h" @@ -56,14 +57,19 @@ QUERY_REC *privmsg_get_query(SERVER_REC *server, const char *nick, static void signal_query_created(QUERY_REC *query, gpointer automatic) { + TEXT_DEST_REC dest; + g_return_if_fail(IS_QUERY(query)); if (window_item_window(query) == NULL) { window_item_create((WI_ITEM_REC *) query, GPOINTER_TO_INT(automatic)); - printformat(query->server, query->name, MSGLEVEL_CLIENTNOTICE, - TXT_QUERY_STARTED, query->name); } + + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_CLIENTNOTICE, NULL); + printformat_dest(&dest, TXT_QUERY_START, + query->name, query->server_tag); } static void signal_query_created_curwin(QUERY_REC *query) @@ -76,16 +82,22 @@ static void signal_query_created_curwin(QUERY_REC *query) static void signal_query_destroyed(QUERY_REC *query) { WINDOW_REC *window; + TEXT_DEST_REC dest; g_return_if_fail(IS_QUERY(query)); window = window_item_window((WI_ITEM_REC *) query); - if (window != NULL) { - window_item_destroy((WI_ITEM_REC *) query); + if (window == NULL) + return; - if (!query->unwanted) - window_auto_destroy(window); - } + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_CLIENTNOTICE, NULL); + printformat_dest(&dest, TXT_QUERY_STOP, query->name); + + window_item_destroy((WI_ITEM_REC *) query); + + if (!query->unwanted) + window_auto_destroy(window); } static void signal_query_server_changed(QUERY_REC *query) @@ -101,8 +113,15 @@ static void signal_query_server_changed(QUERY_REC *query) static void signal_query_nick_changed(QUERY_REC *query, const char *oldnick) { + TEXT_DEST_REC dest; + g_return_if_fail(query != NULL); + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_CLIENTNOTICE, NULL); + printformat_dest(&dest, TXT_NICK_CHANGED, oldnick, + query->name, query->name); + signal_emit("window item changed", 2, window_item_window((WI_ITEM_REC *) query), query); } @@ -117,16 +136,6 @@ static void signal_window_item_server_changed(WINDOW_REC *window, } } -static void signal_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item) -{ - QUERY_REC *query; - - g_return_if_fail(window != NULL); - - query = QUERY(item); - if (query != NULL) query_destroy(query); -} - static void sig_server_connected(SERVER_REC *server) { GSList *tmp; @@ -151,6 +160,7 @@ static void cmd_window_server(const char *data) { SERVER_REC *server; QUERY_REC *query; + TEXT_DEST_REC dest; g_return_if_fail(data != NULL); @@ -160,10 +170,12 @@ static void cmd_window_server(const char *data) return; /* /WINDOW SERVER used in a query window */ + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_CLIENTNOTICE, NULL); + printformat_dest(&dest, TXT_QUERY_SERVER_CHANGED, + query->name, server->tag); + query_change_server(query, server); - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_QUERY_SERVER_CHANGED, - query->name, server->tag); signal_stop(); } @@ -200,17 +212,17 @@ static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item) g_return_if_fail(data != NULL); - if (*data == '\0') { - /* remove current query */ - cmd_unquery("", server, item); - return; - } - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS, "query", &optlist, &nick, &msg)) return; - if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + if (*nick == '\0') { + /* remove current query */ + cmd_unquery("", server, item); + cmd_params_free(free_arg); + return; + } server = cmd_options_get_server("query", optlist, server); if (server == NULL) { @@ -228,7 +240,8 @@ static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item) query = query_find(server, nick); if (query == NULL) - CHAT_PROTOCOL(server)->query_create(server->tag, nick, FALSE); + query = CHAT_PROTOCOL(server)-> + query_create(server->tag, nick, FALSE); else { /* query already exists */ WINDOW_REC *window = window_item_window(query); @@ -254,13 +267,9 @@ static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item) } if (*msg != '\0') { - /* FIXME: we'll need some function that does both - of these. and separate the , and . target handling - from own_private messagge.. */ - server->send_message(server, nick, msg); - - signal_emit("message own_private", 4, - server, msg, nick, nick); + msg = g_strdup_printf("-nick %s %s", nick, msg); + signal_emit("command msg", 3, msg, server, query); + g_free(msg); } cmd_params_free(free_arg); @@ -346,7 +355,6 @@ void fe_queries_init(void) signal_add("query server changed", (SIGNAL_FUNC) signal_query_server_changed); signal_add("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed); signal_add("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed); - signal_add_last("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy); signal_add("server connected", (SIGNAL_FUNC) sig_server_connected); signal_add("window changed", (SIGNAL_FUNC) sig_window_changed); signal_add_first("message private", (SIGNAL_FUNC) sig_message_private); @@ -368,7 +376,6 @@ void fe_queries_deinit(void) signal_remove("query server changed", (SIGNAL_FUNC) signal_query_server_changed); signal_remove("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed); signal_remove("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed); - signal_remove("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy); signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected); signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed); signal_remove("message private", (SIGNAL_FUNC) sig_message_private); diff --git a/apps/irssi/src/fe-common/core/fe-server.c b/apps/irssi/src/fe-common/core/fe-server.c index f2327c59..d8f9d137 100644 --- a/apps/irssi/src/fe-common/core/fe-server.c +++ b/apps/irssi/src/fe-common/core/fe-server.c @@ -146,6 +146,8 @@ static void cmd_server_add(const char *data) if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE; if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE; + if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE; + if (g_hash_table_lookup(optlist, "noproxy")) rec->no_proxy = TRUE; if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password); value = g_hash_table_lookup(optlist, "host"); @@ -175,7 +177,7 @@ static void cmd_server_remove(const char *data) if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); if (*port == '\0') - rec = server_setup_find(addr, -1); + rec = server_setup_find(addr, -1, NULL); else rec = server_setup_find_port(addr, atoi(port)); @@ -189,36 +191,30 @@ static void cmd_server_remove(const char *data) cmd_params_free(free_arg); } -static void cmd_server(const char *data, SERVER_REC *server, void *item) +static void cmd_server(const char *data) { - GHashTable *optlist; - char *addr; - void *free_arg; - - if (*data == '\0') { - if (servers == NULL && lookup_servers == NULL && - reconnects == NULL) { - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_NO_CONNECTED_SERVERS); - } else { - print_servers(); - print_lookup_servers(); - print_reconnects(); - } - - signal_stop(); + if (*data != '\0') return; - } - if (g_strncasecmp(data, "add ", 4) == 0 || - g_strncasecmp(data, "remove ", 7) == 0 || - g_strcasecmp(data, "list") == 0 || - g_strncasecmp(data, "list ", 5) == 0) { - command_runsub("server", data, server, item); - signal_stop(); - return; + if (servers == NULL && lookup_servers == NULL && + reconnects == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_NO_CONNECTED_SERVERS); + } else { + print_servers(); + print_lookup_servers(); + print_reconnects(); } + signal_stop(); +} + +static void cmd_server_connect(const char *data) +{ + GHashTable *optlist; + char *addr; + void *free_arg; + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, "connect", &optlist, &addr)) return; @@ -264,10 +260,10 @@ static void sig_connect_failed(SERVER_REC *server, gchar *msg) if (msg == NULL) { /* no message so this wasn't unexpected fail - send connection_lost message instead */ - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONNECTION_LOST, server->connrec->address); } else { - printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + printformat(server, NULL, MSGLEVEL_CLIENTERROR, TXT_CANT_CONNECT, server->connrec->address, server->connrec->port, msg); } } @@ -276,7 +272,7 @@ static void sig_server_disconnected(SERVER_REC *server) { g_return_if_fail(server != NULL); - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONNECTION_LOST, server->connrec->address); } @@ -284,7 +280,7 @@ static void sig_server_quit(SERVER_REC *server, const char *msg) { g_return_if_fail(server != NULL); - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_SERVER_QUIT, server->connrec->address, msg); } @@ -292,8 +288,9 @@ static void sig_server_lag_disconnected(SERVER_REC *server) { g_return_if_fail(server != NULL); - printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, - TXT_LAG_DISCONNECTED, server->connrec->address, time(NULL)-server->lag_sent); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_LAG_DISCONNECTED, server->connrec->address, + time(NULL)-server->lag_sent.tv_sec); } static void sig_server_reconnect_removed(RECONNECT_REC *reconnect) @@ -324,9 +321,10 @@ static void sig_chat_protocol_unknown(const char *protocol) void fe_server_init(void) { command_bind("server", NULL, (SIGNAL_FUNC) cmd_server); + command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect); command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add); command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove); - command_set_options("server add", "4 6 auto noauto -host -port"); + command_set_options("server add", "4 6 auto noauto proxy noproxy -host -port"); signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting); @@ -345,6 +343,7 @@ void fe_server_init(void) void fe_server_deinit(void) { command_unbind("server", (SIGNAL_FUNC) cmd_server); + command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect); command_unbind("server add", (SIGNAL_FUNC) cmd_server_add); command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove); diff --git a/apps/irssi/src/fe-common/core/fe-settings.c b/apps/irssi/src/fe-common/core/fe-settings.c index 0d8aaa93..382a09f7 100644 --- a/apps/irssi/src/fe-common/core/fe-settings.c +++ b/apps/irssi/src/fe-common/core/fe-settings.c @@ -50,7 +50,8 @@ static void set_print(SETTINGS_REC *rec) default: value = ""; } - printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s = %s", rec->key, value); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_SET_ITEM, + rec->key, value); } static void set_boolean(const char *key, const char *value) @@ -92,7 +93,8 @@ static void cmd_set(char *data) if (strcmp(last_section, rec->section) != 0) { /* print section */ - printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%_[ %s ]", rec->section); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_SET_TITLE, rec->section); last_section = rec->section; } @@ -123,8 +125,10 @@ static void cmd_set(char *data) } g_slist_free(sets); - if (!found) - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %s", key); + if (!found) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_SET_UNKNOWN, key); + } cmd_params_free(free_arg); } @@ -143,12 +147,13 @@ static void cmd_toggle(const char *data) type = settings_get_type(key); if (type == -1) - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %_%s", key); + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key); else if (type != SETTING_TYPE_BOOLEAN) - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Setting %_%s%_ isn't boolean, use /SET", key); + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_NOT_BOOLEAN, key); else { set_boolean(key, *value != '\0' ? value : "TOGGLE"); set_print(settings_get_record(key)); + signal_emit("setup changed", 0); } cmd_params_free(free_arg); @@ -168,12 +173,12 @@ static void show_aliases(const char *alias) printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_HEADER); node = iconfig_node_traverse("aliases", FALSE); - tmp = node == NULL ? NULL : node->value; + tmp = node == NULL ? NULL : config_node_first(node->value); /* first get the list of aliases sorted */ list = NULL; aliaslen = strlen(alias); - for (; tmp != NULL; tmp = tmp->next) { + for (; tmp != NULL; tmp = config_node_next(tmp)) { CONFIG_NODE *node = tmp->data; if (node->type != NODE_TYPE_KEY) @@ -241,20 +246,19 @@ static void cmd_unalias(const char *data) /* SYNTAX: RELOAD [] */ static void cmd_reload(const char *data) { - char *fname; + const char *fname; + + fname = *data == '\0' ? get_irssi_config() : data; - fname = *data != '\0' ? g_strdup(data) : - g_strdup_printf("%s/.silc/config", g_get_home_dir()); if (settings_reread(fname)) { printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONFIG_RELOADED, fname); } - g_free(fname); } static void settings_save_fe(const char *fname) { - if (settings_save(fname)) { + if (settings_save(fname, FALSE /* not autosaved */)) { printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONFIG_SAVED, fname); } @@ -262,7 +266,7 @@ static void settings_save_fe(const char *fname) static void settings_save_confirm(const char *line, char *fname) { - if (line[0] == 'Y') + if (i_toupper(line[0]) == 'Y') settings_save_fe(fname); g_free(fname); } @@ -270,29 +274,37 @@ static void settings_save_confirm(const char *line, char *fname) /* SYNTAX: SAVE [] */ 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(); } diff --git a/apps/irssi/src/fe-common/core/fe-windows.c b/apps/irssi/src/fe-common/core/fe-windows.c index 3c48261a..fc4766d9 100644 --- a/apps/irssi/src/fe-common/core/fe-windows.c +++ b/apps/irssi/src/fe-common/core/fe-windows.c @@ -134,7 +134,8 @@ void window_destroy(WINDOW_REC *window) void window_auto_destroy(WINDOW_REC *window) { if (settings_get_bool("autoclose_windows") && windows->next != NULL && - window->items == NULL && window->level == 0) + window->items == NULL && window->bound_items == NULL && + window->level == 0) window_destroy(window); } @@ -194,6 +195,21 @@ void window_set_name(WINDOW_REC *window, const char *name) signal_emit("window name changed", 1, window); } +void window_set_history(WINDOW_REC *window, const char *name) +{ + char *oldname; + oldname = window->history_name; + + if (name == NULL || *name == '\0') + window->history_name = NULL; + else + window->history_name = g_strdup(name); + + signal_emit("window history changed", 1, window, oldname); + + g_free_not_null(oldname); +} + void window_set_level(WINDOW_REC *window, int level) { g_return_if_fail(window != NULL); @@ -375,7 +391,7 @@ int windows_refnum_last(void) return max; } -static int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2) +int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2) { return w1->refnum < w2->refnum ? -1 : 1; } @@ -395,6 +411,7 @@ GSList *windows_get_sorted(void) return sorted; } +/* Add a new bind to window - if duplicate is found it's returned */ WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag, const char *name) { @@ -402,7 +419,11 @@ WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag, g_return_val_if_fail(window != NULL, NULL); g_return_val_if_fail(servertag != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + rec = window_bind_find(window, servertag, name); + if (rec != NULL) + return rec; rec = g_new0(WINDOW_BIND_REC, 1); rec->name = g_strdup(name); @@ -494,18 +515,31 @@ static void sig_server_disconnected(SERVER_REC *server) } } +static void window_print_daychange(WINDOW_REC *window, struct tm *tm) +{ + THEME_REC *theme; + TEXT_DEST_REC dest; + char *format, str[256]; + + theme = active_win->theme != NULL ? active_win->theme : current_theme; + format_create_dest(&dest, NULL, NULL, MSGLEVEL_NEVER, window); + format = format_get_text_theme(theme, MODULE_NAME, &dest, + TXT_DAYCHANGE); + if (strftime(str, sizeof(str), format, tm) <= 0) + str[0] = '\0'; + g_free(format); + + printtext_string_window(window, MSGLEVEL_NEVER, str); +} + static void sig_print_text(void) { GSList *tmp; - char month[100]; time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); - if (strftime(month, sizeof(month), "%b", tm) <= 0) - month[0] = '\0'; - if (tm->tm_hour != 0 || tm->tm_min != 0) return; @@ -513,11 +547,8 @@ static void sig_print_text(void) signal_remove("print text", (SIGNAL_FUNC) sig_print_text); /* day changed, print notice about it to every window */ - for (tmp = windows; tmp != NULL; tmp = tmp->next) { - printformat_window(tmp->data, MSGLEVEL_NEVER, TXT_DAYCHANGE, - tm->tm_mday, tm->tm_mon+1, - 1900+tm->tm_year, month); - } + for (tmp = windows; tmp != NULL; tmp = tmp->next) + window_print_daychange(tmp->data, tm); } static int sig_check_daychange(void) diff --git a/apps/irssi/src/fe-common/core/fe-windows.h b/apps/irssi/src/fe-common/core/fe-windows.h index c9ffe69a..32004459 100644 --- a/apps/irssi/src/fe-common/core/fe-windows.h +++ b/apps/irssi/src/fe-common/core/fe-windows.h @@ -1,10 +1,8 @@ #ifndef __WINDOWS_H #define __WINDOWS_H -#include "servers.h" - -#define STRUCT_SERVER_REC SERVER_REC #include "window-item-def.h" +#include "command-history.h" enum { DATA_LEVEL_NONE = 0, @@ -19,7 +17,7 @@ typedef struct { unsigned int sticky:1; } WINDOW_BIND_REC; -typedef struct { +struct _WINDOW_REC { int refnum; char *name; @@ -37,8 +35,8 @@ typedef struct { unsigned int destroying:1; /* window-specific command line history */ - GList *history, *history_pos; - int history_lines, history_over_counter; + HISTORY_REC *history; + char *history_name; int data_level; /* current data level */ char *hilight_color; /* current hilight color in %format */ @@ -50,7 +48,7 @@ typedef struct { void *theme; /* THEME_REC */ void *gui_data; -} WINDOW_REC; +}; extern GSList *windows; extern WINDOW_REC *active_win; @@ -65,6 +63,7 @@ void window_change_server(WINDOW_REC *window, void *server); void window_set_refnum(WINDOW_REC *window, int refnum); void window_set_name(WINDOW_REC *window, const char *name); +void window_set_history(WINDOW_REC *window, const char *name); void window_set_level(WINDOW_REC *window, int level); /* return active item's name, or if none is active, window's name */ @@ -80,8 +79,10 @@ int window_refnum_prev(int refnum, int wrap); int window_refnum_next(int refnum, int wrap); int windows_refnum_last(void); +int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2); GSList *windows_get_sorted(void); +/* Add a new bind to window - if duplicate is found it's returned */ WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag, const char *name); void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec); diff --git a/apps/irssi/src/fe-common/core/formats.c b/apps/irssi/src/fe-common/core/formats.c index b933c068..b4d2e7ed 100644 --- a/apps/irssi/src/fe-common/core/formats.c +++ b/apps/irssi/src/fe-common/core/formats.c @@ -25,8 +25,10 @@ #include "settings.h" #include "levels.h" +#include "servers.h" #include "fe-windows.h" +#include "window-items.h" #include "formats.h" #include "themes.h" #include "translation.h" @@ -36,9 +38,9 @@ static const char *format_fores = "kbgcrmyw"; static const char *format_boldfores = "KBGCRMYW"; static int signal_gui_print_text; -static int hide_text_style, hide_server_tags; +static int hide_text_style, hide_server_tags, hide_mirc_colors; -static int timestamps, msgs_timestamps; +static int timestamp_level; static int timestamp_timeout; int format_find_tag(const char *module, const char *tag) @@ -59,11 +61,73 @@ int format_find_tag(const char *module, const char *tag) return -1; } -int format_expand_styles(GString *out, char format) +static void format_expand_code(const char **format, GString *out, int *flags) { - char *p; + int set; - switch (format) { + if (flags == NULL) { + /* flags are being ignored - skip the code */ + while (**format != ']') + (*format)++; + return; + } + + set = TRUE; + (*format)++; + while (**format != ']' && **format != '\0') { + if (**format == '+') + set = TRUE; + else if (**format == '-') + set = FALSE; + else switch (**format) { + case 'i': + /* indent function */ + (*format)++; + if (**format == '=') + (*format)++; + + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_INDENT_FUNC); + while (**format != ']' && **format != '\0' && + **format != ',') { + g_string_append_c(out, **format); + (*format)++; + } + g_string_append_c(out, ','); + (*format)--; + break; + case 's': + case 'S': + *flags |= !set ? PRINT_FLAG_UNSET_LINE_START : + **format == 's' ? PRINT_FLAG_SET_LINE_START : + PRINT_FLAG_SET_LINE_START_IRSSI; + break; + case 't': + *flags |= set ? PRINT_FLAG_SET_TIMESTAMP : + PRINT_FLAG_UNSET_TIMESTAMP; + break; + case 'T': + *flags |= set ? PRINT_FLAG_SET_SERVERTAG : + PRINT_FLAG_UNSET_SERVERTAG; + break; + } + + (*format)++; + } +} + +int format_expand_styles(GString *out, const char **format, int *flags) +{ + char *p, fmt; + + fmt = **format; + switch (fmt) { + case '{': + case '}': + case '%': + /* escaped char */ + g_string_append_c(out, fmt); + break; case 'U': /* Underline on/off */ g_string_append_c(out, 4); @@ -80,9 +144,6 @@ int format_expand_styles(GString *out, char format) g_string_append_c(out, 4); g_string_append_c(out, FORMAT_STYLE_REVERSE); break; - case '%': - g_string_append_c(out, '%'); - break; case ':': /* Newline */ g_string_append_c(out, '\n'); @@ -103,9 +164,18 @@ int format_expand_styles(GString *out, char format) g_string_append_c(out, 4); g_string_append_c(out, FORMAT_STYLE_DEFAULTS); break; + case '>': + /* clear to end of line */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_CLRTOEOL); + break; + case '[': + /* code */ + format_expand_code(format, out, flags); + break; default: /* check if it's a background color */ - p = strchr(format_backs, format); + p = strchr(format_backs, fmt); if (p != NULL) { g_string_append_c(out, 4); g_string_append_c(out, FORMAT_COLOR_NOCHANGE); @@ -114,8 +184,8 @@ int format_expand_styles(GString *out, char format) } /* check if it's a foreground color */ - if (format == 'p') format = 'm'; - p = strchr(format_fores, format); + if (fmt == 'p') fmt = 'm'; + p = strchr(format_fores, fmt); if (p != NULL) { g_string_append_c(out, 4); g_string_append_c(out, (char) ((int) (p-format_fores)+'0')); @@ -124,8 +194,8 @@ int format_expand_styles(GString *out, char format) } /* check if it's a bold foreground color */ - if (format == 'P') format = 'M'; - p = strchr(format_boldfores, format); + if (fmt == 'P') fmt = 'M'; + p = strchr(format_boldfores, fmt); if (p != NULL) { g_string_append_c(out, 4); g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0')); @@ -208,14 +278,21 @@ void format_create_dest(TEXT_DEST_REC *dest, void *server, const char *target, int level, WINDOW_REC *window) { + format_create_dest_tag(dest, server, NULL, target, level, window); +} + +void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, + const char *server_tag, const char *target, + int level, WINDOW_REC *window) +{ + memset(dest, 0, sizeof(TEXT_DEST_REC)); + dest->server = server; + dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag; dest->target = target; dest->level = level; dest->window = window != NULL ? window : window_find_closest(server, target, level); - - dest->hilight_priority = 0; - dest->hilight_color = NULL; } /* Return length of text part in string (ie. without % codes) */ @@ -231,7 +308,8 @@ int format_get_length(const char *str) while (*str != '\0') { if (*str == '%' && str[1] != '\0') { str++; - if (*str != '%' && format_expand_styles(tmp, *str)) { + if (*str != '%' && + format_expand_styles(tmp, &str, NULL)) { str++; continue; } @@ -265,7 +343,8 @@ int format_real_length(const char *str, int len) while (*str != '\0' && len > 0) { if (*str == '%' && str[1] != '\0') { str++; - if (*str != '%' && format_expand_styles(tmp, *str)) { + if (*str != '%' && + format_expand_styles(tmp, &str, NULL)) { str++; continue; } @@ -285,7 +364,7 @@ int format_real_length(const char *str, int len) return (int) (str-start); } -char *format_string_expand(const char *text) +char *format_string_expand(const char *text, int *flags) { GString *out; char code, *ret; @@ -294,11 +373,12 @@ char *format_string_expand(const char *text) out = g_string_new(NULL); + if (flags != NULL) *flags = 0; code = 0; while (*text != '\0') { if (code == '%') { /* color code */ - if (!format_expand_styles(out, *text)) { + if (!format_expand_styles(out, &text, flags)) { g_string_append_c(out, '%'); g_string_append_c(out, '%'); g_string_append_c(out, *text); @@ -332,7 +412,7 @@ static char *format_get_text_args(TEXT_DEST_REC *dest, while (*text != '\0') { if (code == '%') { /* color code */ - if (!format_expand_styles(out, *text)) { + if (!format_expand_styles(out, &text, &dest->flags)) { g_string_append_c(out, '%'); g_string_append_c(out, '%'); g_string_append_c(out, *text); @@ -342,12 +422,10 @@ static char *format_get_text_args(TEXT_DEST_REC *dest, /* argument */ char *ret; - ret = parse_special((char **) &text, - active_win == NULL ? NULL : - active_win->active_server, - active_win == NULL ? NULL : - active_win->active, arglist, - &need_free, NULL, 0); + ret = parse_special((char **) &text, dest->server, + dest->target == NULL ? NULL : + window_item_find(dest->server, dest->target), + arglist, &need_free, NULL, 0); if (ret != NULL) { /* string shouldn't end with \003 or it could @@ -384,10 +462,8 @@ char *format_get_text_theme(THEME_REC *theme, const char *module, va_list va; char *str; - if (theme == NULL) { - theme = dest->window->theme == NULL ? current_theme : - dest->window->theme; - } + if (theme == NULL) + theme = window_get_theme(dest->window); va_start(va, formatnum); str = format_get_text_theme_args(theme, module, dest, formatnum, va); @@ -438,8 +514,7 @@ char *format_get_text(const char *module, WINDOW_REC *window, char *str; format_create_dest(&dest, server, target, 0, window); - theme = dest.window->theme == NULL ? current_theme : - dest.window->theme; + theme = window_get_theme(dest.window); va_start(va, formatnum); str = format_get_text_theme_args(theme, module, &dest, formatnum, va); @@ -487,28 +562,45 @@ char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest) { int format; - if (dest->level & LINE_START_IRSSI_LEVEL) - format = TXT_LINE_START_IRSSI; - else if ((dest->level & NOT_LINE_START_LEVEL) == 0) - format = TXT_LINE_START; - else + /* check for flags if we want to override defaults */ + if (dest->flags & PRINT_FLAG_UNSET_LINE_START) return NULL; + if (dest->flags & PRINT_FLAG_SET_LINE_START) + format = TXT_LINE_START; + else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI) + format = TXT_LINE_START_IRSSI; + else { + /* use defaults */ + if (dest->level & LINE_START_IRSSI_LEVEL) + format = TXT_LINE_START_IRSSI; + else if ((dest->level & NOT_LINE_START_LEVEL) == 0) + format = TXT_LINE_START; + else + return NULL; + } + return format_get_text_theme(theme, MODULE_NAME, dest, format); } -#define show_timestamp(level) \ - ((level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) == 0 && \ - (timestamps || (msgs_timestamps && ((level) & MSGLEVEL_MSGS)))) - static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t) { + char *format, str[256]; struct tm *tm; int diff; - if (!show_timestamp(dest->level)) + if ((timestamp_level & dest->level) == 0) + return NULL; + + /* check for flags if we want to override defaults */ + if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP) return NULL; + if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 && + (dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0) + return NULL; + + if (timestamp_timeout > 0) { diff = t - dest->window->last_timestamp; dest->window->last_timestamp = t; @@ -517,38 +609,47 @@ static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t) } tm = localtime(&t); - return format_get_text_theme(theme, MODULE_NAME, dest, TXT_TIMESTAMP, - tm->tm_year+1900, - tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + format = format_get_text_theme(theme, MODULE_NAME, dest, + TXT_TIMESTAMP); + if (strftime(str, sizeof(str), format, tm) <= 0) + str[0] = '\0'; + g_free(format); + return g_strdup(str); } static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest) { - SERVER_REC *server; int count = 0; - server = dest->server; + if (dest->server_tag == NULL || hide_server_tags) + return NULL; - if (server == NULL || hide_server_tags || - (dest->window->active != NULL && - dest->window->active->server == server)) + /* check for flags if we want to override defaults */ + if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG) return NULL; - if (servers != NULL) { - count++; - if (servers->next != NULL) + if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) { + if (dest->window->active != NULL && + dest->window->active->server == dest->server) + return NULL; + + if (servers != NULL) { count++; - } - if (count < 2 && lookup_servers != NULL) { - count++; - if (lookup_servers->next != NULL) + if (servers->next != NULL) + count++; + } + if (count < 2 && lookup_servers != NULL) { count++; + if (lookup_servers->next != NULL) + count++; + } + + if (count < 2) + return NULL; } - return count < 2 ? NULL : - format_get_text_theme(theme, MODULE_NAME, dest, - TXT_SERVERTAG, server->tag); + return format_get_text_theme(theme, MODULE_NAME, dest, + TXT_SERVERTAG, dest->server_tag); } char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t) @@ -576,16 +677,16 @@ void format_newline(WINDOW_REC *window) signal_emit_id(signal_gui_print_text, 6, window, GINT_TO_POINTER(-1), GINT_TO_POINTER(-1), - GINT_TO_POINTER(PRINTFLAG_NEWLINE), + GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE), "", GINT_TO_POINTER(-1)); } /* parse ANSI color string */ -static char *get_ansi_color(THEME_REC *theme, char *str, - int *fg_ret, int *bg_ret, int *flags_ret) +static const char *get_ansi_color(THEME_REC *theme, const char *str, + int *fg_ret, int *bg_ret, int *flags_ret) { static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - char *start; + const char *start; int fg, bg, flags, num; if (*str != '[') @@ -600,7 +701,7 @@ static char *get_ansi_color(THEME_REC *theme, char *str, for (;; str++) { if (*str == '\0') return start; - if (isdigit((int) *str)) { + if (i_isdigit(*str)) { num = num*10 + (*str-'0'); continue; } @@ -613,19 +714,19 @@ static char *get_ansi_color(THEME_REC *theme, char *str, /* reset colors back to default */ fg = theme->default_color; bg = -1; - flags &= ~PRINTFLAG_INDENT; + flags &= ~GUI_PRINT_FLAG_INDENT; break; case 1: /* hilight */ - flags |= PRINTFLAG_BOLD; + flags |= GUI_PRINT_FLAG_BOLD; break; case 5: /* blink */ - flags |= PRINTFLAG_BLINK; + flags |= GUI_PRINT_FLAG_BLINK; break; case 7: /* reverse */ - flags |= PRINTFLAG_REVERSE; + flags |= GUI_PRINT_FLAG_REVERSE; break; default: if (num >= 30 && num <= 37) @@ -659,7 +760,7 @@ static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret) fg = fg_ret == NULL ? -1 : *fg_ret; bg = bg_ret == NULL ? -1 : *bg_ret; - if (!isdigit((int) **str) && **str != ',') { + if (!i_isdigit(**str) && **str != ',') { fg = -1; bg = -1; } else { @@ -667,7 +768,7 @@ static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret) if (**str != ',') { fg = **str-'0'; (*str)++; - if (isdigit((int) **str)) { + if (i_isdigit(**str)) { fg = fg*10 + (**str-'0'); (*str)++; } @@ -675,12 +776,12 @@ static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret) if (**str == ',') { /* background color */ (*str)++; - if (!isdigit((int) **str)) + if (!i_isdigit(**str)) bg = -1; else { bg = **str-'0'; (*str)++; - if (isdigit((int) **str)) { + if (i_isdigit(**str)) { bg = bg*10 + (**str-'0'); (*str)++; } @@ -772,7 +873,10 @@ char *strip_codes(const char *input) p += 2; continue; } - } + } + + if (*p == 27 && p[1] != '\0') + p = get_ansi_color(current_theme, p, NULL, NULL, NULL); if (!IS_COLOR_CODE(*p)) *out++ = *p; @@ -808,16 +912,19 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) /* bell */ if (settings_get_bool("bell_beeps")) signal_emit("beep", 0); + } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) { + /* clear to end of line */ + flags |= GUI_PRINT_FLAG_CLRTOEOL; } - if (*str != '\0') { + if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) { /* send the text to gui handler */ signal_emit_id(signal_gui_print_text, 6, dest->window, GINT_TO_POINTER(fgcolor), GINT_TO_POINTER(bgcolor), GINT_TO_POINTER(flags), str, dest->level); - flags &= ~PRINTFLAG_INDENT; + flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL); } if (type == '\n') @@ -831,84 +938,106 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) case 2: /* bold */ if (!hide_text_style) - flags ^= PRINTFLAG_BOLD; + flags ^= GUI_PRINT_FLAG_BOLD; break; case 3: /* MIRC color */ get_mirc_color((const char **) &ptr, - hide_text_style ? NULL : &fgcolor, - hide_text_style ? NULL : &bgcolor); - if (!hide_text_style) - flags |= PRINTFLAG_MIRC_COLOR; + hide_mirc_colors || hide_text_style ? NULL : &fgcolor, + hide_mirc_colors || hide_text_style ? NULL : &bgcolor); + if (!hide_mirc_colors && !hide_text_style) + flags |= GUI_PRINT_FLAG_MIRC_COLOR; break; case 4: /* user specific colors */ - flags &= ~PRINTFLAG_MIRC_COLOR; + flags &= ~GUI_PRINT_FLAG_MIRC_COLOR; switch (*ptr) { case FORMAT_STYLE_BLINK: - flags ^= PRINTFLAG_BLINK; + flags ^= GUI_PRINT_FLAG_BLINK; break; case FORMAT_STYLE_UNDERLINE: - flags ^= PRINTFLAG_UNDERLINE; + flags ^= GUI_PRINT_FLAG_UNDERLINE; break; case FORMAT_STYLE_BOLD: - flags ^= PRINTFLAG_BOLD; + flags ^= GUI_PRINT_FLAG_BOLD; break; case FORMAT_STYLE_REVERSE: - flags ^= PRINTFLAG_REVERSE; + flags ^= GUI_PRINT_FLAG_REVERSE; break; case FORMAT_STYLE_INDENT: - flags |= PRINTFLAG_INDENT; + flags |= GUI_PRINT_FLAG_INDENT; + break; + case FORMAT_STYLE_INDENT_FUNC: { + const char *start = ptr; + while (*ptr != ',' && *ptr != '\0') + ptr++; + if (*ptr != '\0') *ptr++ = '\0'; + signal_emit_id(signal_gui_print_text, 6, + dest->window, NULL, NULL, + GINT_TO_POINTER(GUI_PRINT_FLAG_INDENT_FUNC), + str, start, dest->level); break; + } case FORMAT_STYLE_DEFAULTS: fgcolor = bgcolor = -1; - flags &= PRINTFLAG_INDENT; + flags &= GUI_PRINT_FLAG_INDENT; break; + case FORMAT_STYLE_CLRTOEOL: + break; default: if (*ptr != FORMAT_COLOR_NOCHANGE) { fgcolor = (unsigned char) *ptr-'0'; if (fgcolor <= 7) - flags &= ~PRINTFLAG_BOLD; + flags &= ~GUI_PRINT_FLAG_BOLD; else { /* bold */ if (fgcolor != 8) fgcolor -= 8; - flags |= PRINTFLAG_BOLD; + flags |= GUI_PRINT_FLAG_BOLD; } } ptr++; - if (*ptr != FORMAT_COLOR_NOCHANGE) + if (*ptr != FORMAT_COLOR_NOCHANGE) { bgcolor = *ptr-'0'; + if (bgcolor <= 7) + flags &= ~GUI_PRINT_FLAG_BLINK; + else { + /* blink */ + bgcolor -= 8; + flags |= GUI_PRINT_FLAG_BLINK; + } + } } ptr++; break; case 6: /* blink */ if (!hide_text_style) - flags ^= PRINTFLAG_BLINK; + flags ^= GUI_PRINT_FLAG_BLINK; break; case 15: /* remove all styling */ fgcolor = bgcolor = -1; - flags &= PRINTFLAG_INDENT; + flags &= GUI_PRINT_FLAG_INDENT; break; case 22: /* reverse */ if (!hide_text_style) - flags ^= PRINTFLAG_REVERSE; + flags ^= GUI_PRINT_FLAG_REVERSE; break; case 31: /* underline */ if (!hide_text_style) - flags ^= PRINTFLAG_UNDERLINE; + flags ^= GUI_PRINT_FLAG_UNDERLINE; break; case 27: /* ansi color code */ - ptr = get_ansi_color(dest->window == NULL || dest->window->theme == NULL ? - current_theme : dest->window->theme, - ptr, - hide_text_style ? NULL : &fgcolor, - hide_text_style ? NULL : &bgcolor, - hide_text_style ? NULL : &flags); + ptr = (char *) + get_ansi_color(dest->window == NULL || dest->window->theme == NULL ? + current_theme : dest->window->theme, + ptr, + hide_text_style ? NULL : &fgcolor, + hide_text_style ? NULL : &bgcolor, + hide_text_style ? NULL : &flags); break; } @@ -920,11 +1049,16 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) static void read_settings(void) { + timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0; + if (timestamp_level > 0) { + timestamp_level = + level2bits(settings_get_str("timestamp_level")); + } + timestamp_timeout = settings_get_int("timestamp_timeout"); + hide_server_tags = settings_get_bool("hide_server_tags"); hide_text_style = settings_get_bool("hide_text_style"); - timestamps = settings_get_bool("timestamps"); - timestamp_timeout = settings_get_int("timestamp_timeout"); - msgs_timestamps = settings_get_bool("msgs_timestamps"); + hide_mirc_colors = settings_get_bool("hide_mirc_colors"); } void formats_init(void) diff --git a/apps/irssi/src/fe-common/core/formats.h b/apps/irssi/src/fe-common/core/formats.h index 86c53e69..b98d7be7 100644 --- a/apps/irssi/src/fe-common/core/formats.h +++ b/apps/irssi/src/fe-common/core/formats.h @@ -4,13 +4,15 @@ #include "themes.h" #include "fe-windows.h" -#define PRINTFLAG_BOLD 0x01 -#define PRINTFLAG_REVERSE 0x02 -#define PRINTFLAG_UNDERLINE 0x04 -#define PRINTFLAG_BLINK 0x08 -#define PRINTFLAG_MIRC_COLOR 0x10 -#define PRINTFLAG_INDENT 0x20 -#define PRINTFLAG_NEWLINE 0x40 +#define GUI_PRINT_FLAG_BOLD 0x0001 +#define GUI_PRINT_FLAG_REVERSE 0x0002 +#define GUI_PRINT_FLAG_UNDERLINE 0x0004 +#define GUI_PRINT_FLAG_BLINK 0x0008 +#define GUI_PRINT_FLAG_MIRC_COLOR 0x0010 +#define GUI_PRINT_FLAG_INDENT 0x0020 +#define GUI_PRINT_FLAG_INDENT_FUNC 0x0040 +#define GUI_PRINT_FLAG_NEWLINE 0x0080 +#define GUI_PRINT_FLAG_CLRTOEOL 0x0100 #define MAX_FORMAT_PARAMS 10 #define DEFAULT_FORMAT_ARGLIST_SIZE 200 @@ -30,16 +32,32 @@ struct _FORMAT_REC { int paramtypes[MAX_FORMAT_PARAMS]; }; +#define PRINT_FLAG_SET_LINE_START 0x0001 +#define PRINT_FLAG_SET_LINE_START_IRSSI 0x0002 +#define PRINT_FLAG_UNSET_LINE_START 0x0003 + +#define PRINT_FLAG_SET_TIMESTAMP 0x0004 +#define PRINT_FLAG_UNSET_TIMESTAMP 0x0008 + +#define PRINT_FLAG_SET_SERVERTAG 0x0010 +#define PRINT_FLAG_UNSET_SERVERTAG 0x0020 + typedef struct { WINDOW_REC *window; SERVER_REC *server; + const char *server_tag; /* if server is non-NULL, must be server->tag */ const char *target; int level; int hilight_priority; char *hilight_color; + int flags; } TEXT_DEST_REC; +#define window_get_theme(window) \ + (window != NULL && (window)->theme != NULL ? \ + (window)->theme : current_theme) + int format_find_tag(const char *module, const char *tag); /* Return length of text part in string (ie. without % codes) */ @@ -49,7 +67,7 @@ int format_get_length(const char *str); handles %codes. */ int format_real_length(const char *str, int len); -char *format_string_expand(const char *text); +char *format_string_expand(const char *text, int *flags); char *format_get_text(const char *module, WINDOW_REC *window, void *server, const char *target, @@ -83,6 +101,9 @@ char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t); void format_create_dest(TEXT_DEST_REC *dest, void *server, const char *target, int level, WINDOW_REC *window); +void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, + const char *server_tag, const char *target, + int level, WINDOW_REC *window); void format_newline(WINDOW_REC *window); @@ -106,8 +127,10 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text); #define FORMAT_STYLE_BOLD (0x03 + FORMAT_STYLE_SPECIAL) #define FORMAT_STYLE_REVERSE (0x04 + FORMAT_STYLE_SPECIAL) #define FORMAT_STYLE_INDENT (0x05 + FORMAT_STYLE_SPECIAL) -#define FORMAT_STYLE_DEFAULTS (0x06 + FORMAT_STYLE_SPECIAL) -int format_expand_styles(GString *out, char format); +#define FORMAT_STYLE_INDENT_FUNC (0x06 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_DEFAULTS (0x07 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_CLRTOEOL (0x08 + FORMAT_STYLE_SPECIAL) +int format_expand_styles(GString *out, const char **format, int *flags); void formats_init(void); void formats_deinit(void); diff --git a/apps/irssi/src/fe-common/core/hilight-text.c b/apps/irssi/src/fe-common/core/hilight-text.c index 197957f6..0bdda55f 100644 --- a/apps/irssi/src/fe-common/core/hilight-text.c +++ b/apps/irssi/src/fe-common/core/hilight-text.c @@ -37,8 +37,6 @@ #include "formats.h" static NICKMATCH_REC *nickmatch; -static HILIGHT_REC *next_nick_hilight, *next_line_hilight; -static int next_hilight_start, next_hilight_end; static int never_hilight_level, default_hilight_level; GSList *hilights; @@ -264,7 +262,7 @@ static char *hilight_get_color(HILIGHT_REC *rec) color = rec->color != NULL ? rec->color : settings_get_str("hilight_color"); - return format_string_expand(color); + return format_string_expand(color, NULL); } static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec) @@ -274,64 +272,51 @@ static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec) if (rec->priority > 0) dest->hilight_priority = rec->priority; + g_free_not_null(dest->hilight_color); dest->hilight_color = hilight_get_act_color(rec); } -static void sig_print_text_stripped(TEXT_DEST_REC *dest, const char *str) +static void sig_print_text(TEXT_DEST_REC *dest, const char *text, + const char *stripped) { - HILIGHT_REC *hilight; - - g_return_if_fail(str != NULL); - - if (next_nick_hilight != NULL) { - if (!next_nick_hilight->nick) { - /* non-nick hilight wanted */ - hilight = next_nick_hilight; - next_nick_hilight = NULL; - if (!hilight_match_text(hilight, str, - &next_hilight_start, - &next_hilight_end)) { - next_hilight_start = 0; - next_hilight_end = strlen(str); - } - } else { - /* nick is highlighted, just set priority */ - hilight_update_text_dest(dest, next_nick_hilight); - next_nick_hilight = NULL; - return; - } - } else { - if (dest->level & (MSGLEVEL_NOHILIGHT|MSGLEVEL_HILIGHT)) - return; + HILIGHT_REC *hilight; + char *color, *newstr; + int old_level, hilight_start, hilight_end, hilight_len; + int nick_match; - hilight = hilight_match(dest->server, dest->target, - NULL, NULL, dest->level, str, - &next_hilight_start, - &next_hilight_end); - } + if (dest->level & MSGLEVEL_NOHILIGHT) + return; + + hilight_start = hilight_end = 0; + hilight = hilight_match(dest->server, dest->target, + NULL, NULL, dest->level, stripped, + &hilight_start, + &hilight_end); + if (hilight == NULL) + return; - if (hilight != NULL) { + nick_match = hilight->nick && (dest->level & (MSGLEVEL_PUBLIC|MSGLEVEL_ACTIONS)) == MSGLEVEL_PUBLIC; + + old_level = dest->level; + if (!nick_match || (dest->level & MSGLEVEL_HILIGHT)) { /* update the level / hilight info */ hilight_update_text_dest(dest, hilight); - - next_line_hilight = hilight; } -} -static void sig_print_text(TEXT_DEST_REC *dest, const char *str) -{ - char *color, *newstr; - int next_hilight_len; + if (nick_match) + return; /* fe-messages.c should have taken care of this */ - if (next_line_hilight == NULL) - return; + if (old_level & MSGLEVEL_HILIGHT) { + /* nick is highlighted, just set priority */ + return; + } - color = hilight_get_color(next_line_hilight); - next_hilight_len = next_hilight_end-next_hilight_start; + color = hilight_get_color(hilight); + hilight_len = hilight_end-hilight_start; - if (!next_line_hilight->word) { + if (!hilight->word) { /* hilight whole line */ - char *tmp = strip_codes(str); + char *tmp = strip_codes(text); newstr = g_strconcat(color, tmp, NULL); g_free(tmp); } else { @@ -343,25 +328,25 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *str) tmp = g_string_new(NULL); /* start of the line */ - pos = strip_real_length(str, next_hilight_start, NULL, NULL); - g_string_append(tmp, str); + pos = strip_real_length(text, hilight_start, NULL, NULL); + g_string_append(tmp, text); g_string_truncate(tmp, pos); /* color */ g_string_append(tmp, color); /* middle of the line, stripped */ - middle = strip_codes(str+pos); + middle = strip_codes(text+pos); pos = tmp->len; g_string_append(tmp, middle); - g_string_truncate(tmp, pos+next_hilight_len); + g_string_truncate(tmp, pos+hilight_len); g_free(middle); /* end of the line */ - pos = strip_real_length(str, next_hilight_end, + pos = strip_real_length(text, hilight_end, &color_pos, &color_len); if (color_pos > 0) - lastcolor = g_strndup(str+color_pos, color_len); + lastcolor = g_strndup(text+color_pos, color_len); else { /* no colors in line, change back to default */ lastcolor = g_malloc0(3); @@ -369,15 +354,14 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *str) lastcolor[1] = FORMAT_STYLE_DEFAULTS; } g_string_append(tmp, lastcolor); - g_string_append(tmp, str+pos); + g_string_append(tmp, text+pos); g_free(lastcolor); newstr = tmp->str; g_string_free(tmp, FALSE); } - next_line_hilight = NULL; - signal_emit("print text", 2, dest, newstr); + signal_emit("print text", 3, dest, newstr, stripped); g_free(color); g_free(newstr); @@ -397,7 +381,6 @@ char *hilight_match_nick(SERVER_REC *server, const char *channel, color = rec == NULL || !rec->nick ? NULL : hilight_get_color(rec); - next_nick_hilight = rec; return color; } @@ -416,7 +399,8 @@ static void read_hilight_config(void) return; } - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { node = tmp->data; if (node->type != NODE_TYPE_BLOCK) @@ -567,16 +551,14 @@ static void cmd_hilight(const char *data) rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL; if (colorarg != NULL) { + g_free_and_null(rec->color); if (*colorarg != '\0') rec->color = g_strdup(colorarg); - else - g_free_and_null(rec->color); } if (actcolorarg != NULL) { + g_free_and_null(rec->act_color); if (*actcolorarg != '\0') rec->act_color = g_strdup(actcolorarg); - else - g_free_and_null(rec->act_color); } #ifdef HAVE_REGEX_H @@ -664,15 +646,11 @@ void hilight_text_init(void) settings_add_str("lookandfeel", "hilight_act_color", "%M"); settings_add_str("lookandfeel", "hilight_level", "PUBLIC DCCMSGS"); - next_nick_hilight = NULL; - next_line_hilight = NULL; - read_settings(); nickmatch = nickmatch_init(hilight_nick_cache); read_hilight_config(); - signal_add_first("print text stripped", (SIGNAL_FUNC) sig_print_text_stripped); signal_add_first("print text", (SIGNAL_FUNC) sig_print_text); signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config); signal_add("setup changed", (SIGNAL_FUNC) read_settings); @@ -687,7 +665,6 @@ void hilight_text_deinit(void) hilights_destroy_all(); nickmatch_deinit(nickmatch); - signal_remove("print text stripped", (SIGNAL_FUNC) sig_print_text_stripped); signal_remove("print text", (SIGNAL_FUNC) sig_print_text); signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); diff --git a/apps/irssi/src/fe-common/core/keyboard.c b/apps/irssi/src/fe-common/core/keyboard.c index 0b7ac714..ac682001 100644 --- a/apps/irssi/src/fe-common/core/keyboard.c +++ b/apps/irssi/src/fe-common/core/keyboard.c @@ -38,44 +38,15 @@ static GHashTable *keys, *default_keys; If the key isn't used, used_keys[key] is zero. */ static char used_keys[256]; -/* contains list of all key bindings of which command is "key" - - this can be used to check fast if some command queue exists or not. - Format is _always_ in key1-key2-key3 format (like ^W-^N, - not ^W^N) */ +/* Contains a list of all possible executable key bindings (not "key" keys). + Format is _always_ in key1-key2-key3 format and fully extracted, like + ^[-[-A, not meta-A */ static GTree *key_states; -/* List of all key combo names */ -static GSList *key_combos; static int key_config_frozen; struct KEYBOARD_REC { - /* example: - /BIND ^[ key meta - /BIND meta-O key meta2 - /BIND meta-[ key meta2 - - /BIND meta2-C key right - /BIND ^W-meta-right /echo ^W Meta-right key pressed - - When ^W Meta-Right is pressed, the full char combination - is "^W^[^[[C". - - We'll get there with key states: - ^W - key_prev_state = NULL, key_state = NULL -> ^W - ^[ - key_prev_state = NULL, key_state = ^W -> meta - ^[ - key_prev_state = ^W, key_state = meta -> meta - [ - key_prev_state = ^W-meta, key_state = meta -> meta2 - C - key_prev_state = ^W-meta, key_state = meta2 -> right - key_prev_state = ^W-meta, key_state = right -> ^W-meta-right - - key_state is moved to key_prev_state if there's nothing else in - /BINDs matching for key_state-newkey. - - ^X^Y equals to ^X-^Y, ABC equals to A-B-C unless there's ABC - named key. ^ can be used with ^^ and - with -- */ - char *key_state, *key_prev_state; - - /* GUI specific data sent in "key pressed" signal */ - void *gui_data; + char *key_state; /* the ongoing key combo */ + void *gui_data; /* GUI specific data sent in "key pressed" signal */ }; /* Creates a new "keyboard" - this is used only for keeping track of @@ -98,7 +69,6 @@ void keyboard_destroy(KEYBOARD_REC *keyboard) signal_emit("keyboard destroyed", 1, keyboard); g_free_not_null(keyboard->key_state); - g_free_not_null(keyboard->key_prev_state); g_free(keyboard); } @@ -144,7 +114,8 @@ static CONFIG_NODE *key_config_find(const char *key) /* remove old keyboard settings */ node = iconfig_node_traverse("(keyboard", TRUE); - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { node = tmp->data; if (strcmp(config_node_get_str(node, "key", ""), key) == 0) @@ -180,8 +151,10 @@ static void keyconfig_clear(const char *key) /* remove old keyboard settings */ node = key_config_find(key); - if (node != NULL) - iconfig_node_clear(node); + if (node != NULL) { + iconfig_node_remove(iconfig_node_traverse("(keyboard", FALSE), + node); + } } KEYINFO_REC *key_info_find(const char *id) @@ -198,69 +171,170 @@ KEYINFO_REC *key_info_find(const char *id) return NULL; } -static KEY_REC *key_combo_find(const char *key) +static int expand_key(const char *key, GSList **out); + +#define expand_out_char(out, c) \ + { \ + GSList *tmp; \ + for (tmp = out; tmp != NULL; tmp = tmp->next) \ + g_string_append_c(tmp->data, c); \ + } + +#define expand_out_free(out) \ + { \ + GSList *tmp; \ + for (tmp = out; tmp != NULL; tmp = tmp->next) \ + g_string_free(tmp->data, TRUE); \ + g_slist_free(out); out = NULL; \ + } + +static int expand_combo(const char *start, const char *end, GSList **out) { + KEY_REC *rec; KEYINFO_REC *info; - GSList *tmp; + GSList *tmp, *tmp2, *list, *copy, *newout; + char *str; + + if (start == end) { + /* single key */ + expand_out_char(*out, *start); + return TRUE; + } info = key_info_find("key"); if (info == NULL) - return NULL; + return FALSE; + /* get list of all key combos that generate the named combo.. */ + list = NULL; + str = g_strndup(start, (int) (end-start)+1); for (tmp = info->keys; tmp != NULL; tmp = tmp->next) { KEY_REC *rec = tmp->data; - if (strcmp(rec->data, key) == 0) - return rec; + if (strcmp(rec->data, str) == 0) + list = g_slist_append(list, rec); } + g_free(str); - return NULL; -} + if (list == NULL) + return FALSE; -static void key_states_scan_key(const char *key, KEY_REC *rec, GString *temp) -{ - char **keys, **tmp, *p; + if (list->next == NULL) { + /* only one way to generate the combo, good */ + rec = list->data; + return expand_key(rec->key, out); + } - g_string_truncate(temp, 0); + /* multiple ways to generate the combo - + we'll need to include all of them in output */ + newout = NULL; + for (tmp = list->next; tmp != NULL; tmp = tmp->next) { + KEY_REC *rec = tmp->data; - /* meta-^W^Gfoo -> meta-^W-^G-f-o-o */ - keys = g_strsplit(key, "-", -1); - for (tmp = keys; *tmp != NULL; tmp++) { - if (key_combo_find(*tmp)) { - /* key combo */ - g_string_append(temp, *tmp); - g_string_append_c(temp, '-'); - continue; + copy = NULL; + for (tmp2 = *out; tmp2 != NULL; tmp2 = tmp2->next) { + GString *str = tmp2->data; + copy = g_slist_append(copy, g_string_new(str->str)); } - if (**tmp == '\0') { - /* '-' */ - g_string_append(temp, "--"); - continue; + if (!expand_key(rec->key, ©)) { + /* illegal key combo, remove from list */ + expand_out_free(copy); + } else { + newout = g_slist_concat(newout, copy); } + } - for (p = *tmp; *p != '\0'; p++) { - g_string_append_c(temp, *p); + rec = list->data; + if (!expand_key(rec->key, out)) { + /* illegal key combo, remove from list */ + expand_out_free(*out); + } - if (*p == '^') { - /* ctrl-code */ - if (p[1] != '\0') - p++; - g_string_append_c(temp, *p); + *out = g_slist_concat(*out, newout); + return *out != NULL; +} + +/* Expand key code - returns TRUE if successful. */ +static int expand_key(const char *key, GSList **out) +{ + GSList *tmp; + const char *start; + int last_hyphen; + + /* meta-^W^Gf -> ^[-^W-^G-f */ + start = NULL; last_hyphen = TRUE; + for (; *key != '\0'; key++) { + if (start != NULL) { + if (i_isalnum(*key) || *key == '_') { + /* key combo continues */ + continue; } - g_string_append_c(temp, '-'); + if (!expand_combo(start, key-1, out)) + return FALSE; + expand_out_char(*out, '-'); + start = NULL; + } + + if (*key == '-') { + if (last_hyphen) { + expand_out_char(*out, '-'); + expand_out_char(*out, '-'); + } + last_hyphen = !last_hyphen; + } else if (*key == '^') { + /* ctrl-code */ + if (key[1] != '\0') + key++; + + expand_out_char(*out, '^'); + expand_out_char(*out, *key); + expand_out_char(*out, '-'); + last_hyphen = FALSE; /* optional */ + } else if (last_hyphen && i_isalnum(*key) && !i_isdigit(*key)) { + /* possibly beginning of keycombo */ + start = key; + last_hyphen = FALSE; + } else { + expand_out_char(*out, *key); + expand_out_char(*out, '-'); + last_hyphen = FALSE; /* optional */ } } - g_strfreev(keys); - if (temp->len > 0) { - g_string_truncate(temp, temp->len-1); + if (start != NULL) + return expand_combo(start, key-1, out); - if (temp->str[1] == '-' || temp->str[1] == '\0') - used_keys[(int) (unsigned char) temp->str[0]] = 1; - g_tree_insert(key_states, g_strdup(temp->str), rec); + for (tmp = *out; tmp != NULL; tmp = tmp->next) { + GString *str = tmp->data; + + g_string_truncate(str, str->len-1); } + + return TRUE; +} + +static void key_states_scan_key(const char *key, KEY_REC *rec) +{ + GSList *tmp, *out; + + if (strcmp(rec->info->id, "key") == 0) + return; + + out = g_slist_append(NULL, g_string_new(NULL)); + if (expand_key(key, &out)) { + for (tmp = out; tmp != NULL; tmp = tmp->next) { + GString *str = tmp->data; + + if (str->str[1] == '-' || str->str[1] == '\0') + used_keys[(int)(unsigned char)str->str[0]] = 1; + + g_tree_insert(key_states, g_strdup(str->str), rec); + } + } + + expand_out_free(out); } static int key_state_destroy(char *key) @@ -455,7 +529,8 @@ static int key_emit_signal(KEYBOARD_REC *keyboard, KEY_REC *key) return consumed; } -int key_states_search(const char *combo, const char *search) +static int key_states_search(const unsigned char *combo, + const unsigned char *search) { while (*search != '\0') { if (*combo != *search) @@ -463,7 +538,7 @@ int key_states_search(const char *combo, const char *search) search++; combo++; } - return *combo == '\0' || *combo == '-' ? 0 : -1; + return 0; } /* Returns TRUE if key press was consumed. Control characters should be sent @@ -471,96 +546,45 @@ int key_states_search(const char *combo, const char *search) int key_pressed(KEYBOARD_REC *keyboard, const char *key) { KEY_REC *rec; - char *str; - int consumed; + char *combo; + int first_key, consumed; g_return_val_if_fail(keyboard != NULL, FALSE); g_return_val_if_fail(key != NULL && *key != '\0', FALSE); - if (keyboard->key_state == NULL) { - if (key[1] == '\0' && - !used_keys[(int) (unsigned char) key[0]]) { - /* fast check - key not used */ - return FALSE; - } - - rec = g_tree_search(key_states, - (GSearchFunc) key_states_search, - (void *) key); - if (rec == NULL || - (g_tree_lookup(key_states, (void *) key) != NULL && - strcmp(rec->info->id, "key") != 0)) { - /* a single non-combo key was pressed */ - rec = g_hash_table_lookup(keys, key); - if (rec == NULL) - return FALSE; - consumed = key_emit_signal(keyboard, rec); - - /* never consume non-control characters */ - return consumed && key[1] != '\0'; - } - } - - if (keyboard->key_state == NULL) { - /* first key in combo */ - rec = g_tree_lookup(key_states, (void *) key); - } else { - /* continuing key combination */ - str = g_strconcat(keyboard->key_state, "-", key, NULL); - rec = g_tree_lookup(key_states, str); - g_free(str); + if (keyboard->key_state == NULL && key[1] == '\0' && + !used_keys[(int) (unsigned char) key[0]]) { + /* fast check - key not used */ + return FALSE; } - if (rec != NULL && strcmp(rec->info->id, "key") == 0) { - /* combo has a specified name, use it */ - g_free_not_null(keyboard->key_state); - keyboard->key_state = g_strdup(rec->data); - } else { - /* some unnamed key - move key_state after key_prev_state - and replace key_state with this new key */ - if (keyboard->key_prev_state == NULL) - keyboard->key_prev_state = keyboard->key_state; - else { - str = g_strconcat(keyboard->key_prev_state, "-", - keyboard->key_state, NULL); - g_free(keyboard->key_prev_state); - g_free(keyboard->key_state); - keyboard->key_prev_state = str; - } + first_key = keyboard->key_state == NULL; + combo = keyboard->key_state == NULL ? g_strdup(key) : + g_strconcat(keyboard->key_state, "-", key, NULL); + g_free_and_null(keyboard->key_state); - keyboard->key_state = g_strdup(key); + rec = g_tree_search(key_states, + (GSearchFunc) key_states_search, + combo); + if (rec == NULL) { + /* unknown key combo, eat the invalid key + unless it was the first key pressed */ + g_free(combo); + return !first_key; } - /* what to do with the key combo? */ - str = keyboard->key_prev_state == NULL ? - g_strdup(keyboard->key_state) : - g_strconcat(keyboard->key_prev_state, "-", - keyboard->key_state, NULL); - - rec = g_tree_lookup(key_states, str); - if (rec != NULL) { - if (strcmp(rec->info->id, "key") == 0) - rec = g_tree_lookup(key_states, rec->data); - - if (rec != NULL) { - /* full key combo */ - key_emit_signal(keyboard, rec); - rec = NULL; - } - } else { - /* check that combo is possible */ - rec = g_tree_search(key_states, - (GSearchFunc) key_states_search, str); + if (g_tree_lookup(key_states, combo) != rec) { + /* key combo continues.. */ + keyboard->key_state = combo; + return TRUE; } - if (rec == NULL) { - /* a) key combo finished, b) unknown key combo, abort */ - g_free_and_null(keyboard->key_prev_state); - g_free_and_null(keyboard->key_state); - } + /* finished key combo, execute */ + g_free(combo); + consumed = key_emit_signal(keyboard, rec); - g_free(str); - return TRUE; + /* never consume non-control characters */ + return consumed; } void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry, @@ -771,7 +795,8 @@ static void read_keyboard_config(void) return; } - for (tmp = node->value; tmp != NULL; tmp = tmp->next) + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) key_config_read(tmp->data); key_configure_thaw(); @@ -785,7 +810,6 @@ void keyboard_init(void) (GCompareFunc) g_str_equal); keyinfos = NULL; key_states = g_tree_new((GCompareFunc) strcmp); - key_combos = NULL; key_config_frozen = 0; memset(used_keys, 0, sizeof(used_keys)); diff --git a/apps/irssi/src/fe-common/core/module-formats.c b/apps/irssi/src/fe-common/core/module-formats.c index 0f37ccfd..2a5c8f93 100644 --- a/apps/irssi/src/fe-common/core/module-formats.c +++ b/apps/irssi/src/fe-common/core/module-formats.c @@ -28,21 +28,39 @@ FORMAT_REC fecommon_core_formats[] = { { NULL, "Windows", 0 }, { "line_start", "{line_start}", 0 }, - { "line_start_irssi", "{line_start}{hilight Irssi:} ", 0 }, - { "timestamp", "{timestamp $Z} ", 6, { 1, 1, 1, 1, 1, 1 } }, + { "line_start_irssi", "{line_start}{hilight Irssi:} ", 0 }, + { "timestamp", "{timestamp $Z} ", 0 }, { "servertag", "[$0] ", 1, { 0 } }, - { "daychange", "Day changed to $[-2.0]{0} $3 $2", 4, { 1, 1, 1, 0 } }, + { "daychange", "Day changed to %%d %%b %%Y", 0 }, { "talking_with", "You are now talking with {nick $0}", 1, { 0 } }, { "refnum_too_low", "Window number must be greater than 1", 0 }, { "error_server_sticky", "Window's server is sticky and it cannot be changed without -unsticky option", 0 }, { "set_server_sticky", "Window's server set sticky", 1, { 0 } }, - { "unset_server_sticky", "Window's server isn't sticky anymore", 1, { 0 } }, + { "unset_server_sticky", "Window's server isn't sticky anymore", 0 }, + { "window_name_not_unique", "Window names must be unique", 1, { 0 } }, { "window_level", "Window level is now $0", 1, { 0 } }, { "windowlist_header", "Ref Name Active item Server Level", 0 }, { "windowlist_line", "$[3]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } }, { "windowlist_footer", "", 0 }, - { "windows_layout_saved", "Layout of windows is now remembered next time you start silc", 0 }, + { "windows_layout_saved", "Layout of windows is now remembered next time you start irssi", 0 }, { "windows_layout_reset", "Layout of windows reset to defaults", 0 }, + { "window_info_header", "", 0 }, + { "window_info_footer", "", 0 }, + { "window_info_refnum", "Window : {hilight #$0}", 1, { 1 } }, + { "window_info_refnum_sticky", "Window : {hilight #$0 (sticky)}", 1, { 1 } }, + { "window_info_name", "Name : $0", 1, { 0 } }, + { "window_info_history", "History : $0", 1, { 0 } }, + { "window_info_size", "Size : $0x$1", 2, { 1, 1 } }, + { "window_info_level", "Level : $0", 1, { 0 } }, + { "window_info_server", "Server : $0", 1, { 0 } }, + { "window_info_server_sticky", "Server : $0 (sticky)", 1, { 0 } }, + { "window_info_theme", "Theme : $0$1", 2, { 0, 0 } }, + { "window_info_bound_items_header", "Bounds : {hilight Name Server tag}", 0 }, + { "window_info_bound_item", " : $[!30]0 $[!15]1 $2", 3, { 0, 0, 0 } }, + { "window_info_bound_items_footer", "", 0 }, + { "window_info_items_header", "Items : {hilight Name Server tag}", 0 }, + { "window_info_item", " $[7]0: $[!30]1 $2", 3, { 0, 0, 0 } }, + { "window_info_items_footer", "", 0 }, /* ---- */ { NULL, "Server", 0 }, @@ -66,6 +84,7 @@ FORMAT_REC fecommon_core_formats[] = { { "setupserver_added", "Server {server $0} saved", 2, { 0, 1 } }, { "setupserver_removed", "Server {server $0} removed", 2, { 0, 1 } }, { "setupserver_not_found", "Server {server $0} not found", 2, { 0, 1 } }, + { "your_nick", "Your nickname is {nick $0}", 1, { 0 } }, /* ---- */ { NULL, "Channels", 0 }, @@ -76,6 +95,7 @@ FORMAT_REC fecommon_core_formats[] = { { "quit", "{channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } }, { "quit_once", "{channel $3} {channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } }, { "invite", "{nick $0} invites you to {channel $1}", 2, { 0, 0 } }, + { "not_invited", "You have not been invited to a channel!", 0 }, { "new_topic", "{nick $0} changed the topic of {channel $1} to: $2", 3, { 0, 0, 0 } }, { "topic_unset", "Topic unset by {nick $0} on {channel $1}", 2, { 0, 0 } }, { "your_nick_changed", "You're now known as {nick $1}", 3, { 0, 0, 0 } }, @@ -84,14 +104,18 @@ FORMAT_REC fecommon_core_formats[] = { { "not_in_channels", "You are not on any channels", 0 }, { "current_channel", "Current channel {channel $0}", 1, { 0 } }, { "names", "{names_users Users {names_channel $0}} $1", 2, { 0, 0 } }, + { "names_prefix", "{names_prefix $0}", 1, { 0 } }, + { "names_nick_op", "{names_nick_op $0 $1}", 2, { 0, 0 } }, + { "names_nick_halfop", "{names_nick_halfop $0 $1}", 2, { 0, 0 } }, + { "names_nick_voice", "{names_nick_voice $0 $1}", 2, { 0, 0 } }, { "names_nick", "{names_nick $0 $1}", 2, { 0, 0 } }, - { "endofnames", "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} voices, {hilight $4} normal}", 5, { 0, 1, 1, 1, 1 } }, + { "endofnames", "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal}", 6, { 0, 1, 1, 1, 1, 1 } }, { "chanlist_header", "You are on the following channels:", 0 }, { "chanlist_line", "{channel $[-10]0} %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } }, { "chansetup_not_found", "Channel {channel $0} not found", 2, { 0, 0 } }, { "chansetup_added", "Channel {channel $0} saved", 2, { 0, 0 } }, { "chansetup_removed", "Channel {channel $0} removed", 2, { 0, 0 } }, - { "chansetup_header", "Channel IRC net Password Settings", 0 }, + { "chansetup_header", "Channel Network Password Settings", 0 }, { "chansetup_line", "{channel $[15]0} %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } }, { "chansetup_footer", "", 0 }, { "channel_move_notify", "{channel $0} is already joined in window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } }, @@ -117,7 +141,8 @@ FORMAT_REC fecommon_core_formats[] = { /* ---- */ { NULL, "Queries", 0 }, - { "query_start", "Starting query with {nick $0}", 1, { 0 } }, + { "query_start", "Starting query in {server $1} with {nick $0}", 2, { 0, 0 } }, + { "query_stop", "Closing query with {nick $0}", 1, { 0 } }, { "no_query", "No query with {nick $0}", 1, { 0 } }, { "query_server_changed", "Query with {nick $0} changed to server {server $1}", 2, { 0, 0 } }, { "query_move_notify", "Query with {nick $0} is already created to window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } }, @@ -147,7 +172,7 @@ FORMAT_REC fecommon_core_formats[] = { { "log_opened", "Log file {hilight $0} opened", 1, { 0 } }, { "log_closed", "Log file {hilight $0} closed", 1, { 0 } }, { "log_create_failed", "Couldn't create log file {hilight $0}: $1", 2, { 0, 0 } }, - { "log_locked", "Log file {hilight $0} is locked, probably by another running Irssi-SILC", 1, { 0 } }, + { "log_locked", "Log file {hilight $0} is locked, probably by another running Irssi", 1, { 0 } }, { "log_not_open", "Log file {hilight $0} not open", 1, { 0 } }, { "log_started", "Started logging to file {hilight $0}", 1, { 0 } }, { "log_stopped", "Stopped logging to file {hilight $0}", 1, { 0 } }, @@ -162,14 +187,15 @@ FORMAT_REC fecommon_core_formats[] = { /* ---- */ { NULL, "Modules", 0 }, - { "module_header", "Loaded modules:", 0, }, - { "module_line", " $0", 1, { 0 } }, + { "module_header", "Module Type Submodules", 0, }, + { "module_line", "$[!20]0 $[7]1 $2", 3, { 0, 0, 0 } }, { "module_footer", "", 0, }, - { "module_already_loaded", "Module {hilight $0} already loaded", 1, { 0 } }, - { "module_load_error", "Error loading module {hilight $0}: $1", 2, { 0, 0 } }, - { "module_invalid", "{hilight $0} isn't Irssi-SILC module", 1, { 0 } }, - { "module_loaded", "Loaded module {hilight $0}", 1, { 0 } }, - { "module_unloaded", "Unloaded module {hilight $0}", 1, { 0 } }, + { "module_already_loaded", "Module {hilight $0/$1} already loaded", 2, { 0, 0 } }, + { "module_not_loaded", "Module {hilight $0/$1} is not loaded", 2, { 0, 0 } }, + { "module_load_error", "Error loading module {hilight $0/$1}: $2", 3, { 0, 0, 0 } }, + { "module_invalid", "{hilight $0/$1} isn't Irssi module", 2, { 0, 0 } }, + { "module_loaded", "Loaded module {hilight $0/$1}", 2, { 0, 0 } }, + { "module_unloaded", "Unloaded module {hilight $0/$1}", 2, { 0, 0 } }, /* ---- */ { NULL, "Commands", 0 }, @@ -184,6 +210,7 @@ FORMAT_REC fecommon_core_formats[] = { { "not_joined", "Not joined to any channel", 0 }, { "chan_not_found", "Not joined to such channel", 0 }, { "chan_not_synced", "Channel not fully synchronized yet, try again after a while", 0 }, + { "illegal_proto", "Command isn't designed for the chat protocol of the active server", 0 }, { "not_good_idea", "Doing this is not a good idea. Add -YES if you really mean it", 0 }, /* ---- */ @@ -193,7 +220,10 @@ FORMAT_REC fecommon_core_formats[] = { { "theme_save_failed", "Error saving theme to $0: $1", 2, { 0, 0 } }, { "theme_not_found", "Theme {hilight $0} not found", 1, { 0 } }, { "theme_changed", "Using now theme {hilight $0} ($1)", 2, { 0, 0 } }, - { "window_theme_changed", "Using theme {hilight $0} ($1) in this window", 2, { 0, 0 } }, + { "window_theme", "Using theme {hilight $0} in this window", 2, { 0, 0 } }, + { "window_theme_default", "No theme is set for this window", 0 }, + { "window_theme_changed", "Using now theme {hilight $0} ($1) in this window", 2, { 0, 0 } }, + { "window_theme_removed", "Removed theme from this window", 0 }, { "format_title", "%:[{hilight $0}] - [{hilight $1}]%:", 2, { 0, 0 } }, { "format_subtitle", "[{hilight $0}]", 1, { 0 } }, { "format_item", "$0 = $1", 2, { 0, 0 } }, @@ -213,16 +243,22 @@ FORMAT_REC fecommon_core_formats[] = { { NULL, "Misc", 0 }, { "unknown_chat_protocol", "Unknown chat protocol: $0", 1, { 0 } }, - { "unknown_chatnet", "Unknown chat network: $0", 1, { 0 } }, + { "unknown_chatnet", "Unknown chat network: $0 (create it with /IRCNET ADD)", 1, { 0 } }, { "not_toggle", "Value must be either ON, OFF or TOGGLE", 0 }, { "perl_error", "Perl error: $0", 1, { 0 } }, - { "bind_key", "$[10]0 $1 $2", 3, { 0, 0, 0 } }, + { "bind_key", "$[!20]0 $1 $2", 3, { 0, 0, 0 } }, { "bind_unknown_id", "Unknown bind action: $0", 1, { 0 } }, { "config_saved", "Saved configuration to file $0", 1, { 0 } }, { "config_reloaded", "Reloaded configuration", 1, { 0 } }, - { "config_modified", "Configuration file was modified since Irssi-SILC was last started - do you want to overwrite the possible changes?", 1, { 0 } }, + { "config_modified", "Configuration file was modified since irssi was last started - do you want to overwrite the possible changes?", 1, { 0 } }, { "glib_error", "{error GLib $0} $1", 2, { 0, 0 } }, { "overwrite_config", "Overwrite config (y/N)?", 0 }, + { "set_title", "[{hilight $0}]", 1, { 0 } }, + { "set_item", "$0 = $1", 2, { 0, 0 } }, + { "set_unknown", "Unknown setting $0", 1, { 0 } }, + { "set_not_boolean", "Setting {hilight $0} isn't boolean, use /SET", 1, { 0 } }, + { "translation_not_found", "Error opening translation table file $0: $1", 2, { 0, 0 } }, + { "translation_file_error", "Error parsing translation table file $0", 1, { 0 } }, { NULL, NULL, 0 } }; diff --git a/apps/irssi/src/fe-common/core/module-formats.h b/apps/irssi/src/fe-common/core/module-formats.h index e0f31f9a..fcfd87d5 100644 --- a/apps/irssi/src/fe-common/core/module-formats.h +++ b/apps/irssi/src/fe-common/core/module-formats.h @@ -14,13 +14,31 @@ enum { TXT_REFNUM_TOO_LOW, TXT_ERROR_SERVER_STICKY, TXT_SET_SERVER_STICKY, - TXT_UNSET_SERVER_STICKY, + TXT_UNSET_SERVER_STICKY, + TXT_WINDOW_NAME_NOT_UNIQUE, TXT_WINDOW_LEVEL, TXT_WINDOWLIST_HEADER, TXT_WINDOWLIST_LINE, TXT_WINDOWLIST_FOOTER, TXT_WINDOWS_LAYOUT_SAVED, TXT_WINDOWS_LAYOUT_RESET, + TXT_WINDOW_INFO_HEADER, + TXT_WINDOW_INFO_FOOTER, + TXT_WINDOW_INFO_REFNUM, + TXT_WINDOW_INFO_REFNUM_STICKY, + TXT_WINDOW_INFO_NAME, + TXT_WINDOW_INFO_HISTORY, + TXT_WINDOW_INFO_SIZE, + TXT_WINDOW_INFO_LEVEL, + TXT_WINDOW_INFO_SERVER, + TXT_WINDOW_INFO_SERVER_STICKY, + TXT_WINDOW_INFO_THEME, + TXT_WINDOW_INFO_BOUND_ITEMS_HEADER, + TXT_WINDOW_INFO_BOUND_ITEM, + TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER, + TXT_WINDOW_INFO_ITEMS_HEADER, + TXT_WINDOW_INFO_ITEM, + TXT_WINDOW_INFO_ITEMS_FOOTER, TXT_FILL_2, @@ -43,6 +61,7 @@ enum { TXT_SETUPSERVER_ADDED, TXT_SETUPSERVER_REMOVED, TXT_SETUPSERVER_NOT_FOUND, + TXT_YOUR_NICK, TXT_FILL_3, @@ -52,6 +71,7 @@ enum { TXT_QUIT, TXT_QUIT_ONCE, TXT_INVITE, + TXT_NOT_INVITED, TXT_NEW_TOPIC, TXT_TOPIC_UNSET, TXT_YOUR_NICK_CHANGED, @@ -60,6 +80,10 @@ enum { TXT_NOT_IN_CHANNELS, TXT_CURRENT_CHANNEL, TXT_NAMES, + TXT_NAMES_PREFIX, + TXT_NAMES_NICK_OP, + TXT_NAMES_NICK_HALFOP, + TXT_NAMES_NICK_VOICE, TXT_NAMES_NICK, TXT_ENDOFNAMES, TXT_CHANLIST_HEADER, @@ -91,7 +115,8 @@ enum { TXT_FILL_5, - TXT_QUERY_STARTED, + TXT_QUERY_START, + TXT_QUERY_STOP, TXT_NO_QUERY, TXT_QUERY_SERVER_CHANGED, TXT_QUERY_MOVE_NOTIFY, @@ -136,6 +161,7 @@ enum { TXT_MODULE_LINE, TXT_MODULE_FOOTER, TXT_MODULE_ALREADY_LOADED, + TXT_MODULE_NOT_LOADED, TXT_MODULE_LOAD_ERROR, TXT_MODULE_INVALID, TXT_MODULE_LOADED, @@ -153,6 +179,7 @@ enum { TXT_NOT_JOINED, TXT_CHAN_NOT_FOUND, TXT_CHAN_NOT_SYNCED, + TXT_ILLEGAL_PROTO, TXT_NOT_GOOD_IDEA, TXT_FILL_11, @@ -161,7 +188,10 @@ enum { TXT_THEME_SAVE_FAILED, TXT_THEME_NOT_FOUND, TXT_THEME_CHANGED, + TXT_WINDOW_THEME, + TXT_WINDOW_THEME_DEFAULT, TXT_WINDOW_THEME_CHANGED, + TXT_WINDOW_THEME_REMOVED, TXT_FORMAT_TITLE, TXT_FORMAT_SUBTITLE, TXT_FORMAT_ITEM, @@ -188,7 +218,13 @@ enum { TXT_CONFIG_RELOADED, TXT_CONFIG_MODIFIED, TXT_GLIB_ERROR, - TXT_OVERWRITE_CONFIG + TXT_OVERWRITE_CONFIG, + TXT_SET_TITLE, + TXT_SET_ITEM, + TXT_SET_UNKNOWN, + TXT_SET_NOT_BOOLEAN, + TXT_TRANSLATION_NOT_FOUND, + TXT_TRANSLATION_FILE_ERROR }; extern FORMAT_REC fecommon_core_formats[]; diff --git a/apps/irssi/src/fe-common/core/printtext.c b/apps/irssi/src/fe-common/core/printtext.c index 38de158d..1900adec 100644 --- a/apps/irssi/src/fe-common/core/printtext.c +++ b/apps/irssi/src/fe-common/core/printtext.c @@ -34,19 +34,17 @@ static int beep_msg_level, beep_when_away, beep_when_window_active; -static int signal_gui_print_text; -static int signal_print_text_stripped; +static int signal_gui_print_text_finished; +static int signal_print_starting; static int signal_print_text; -static int signal_print_text_finished; static int signal_print_format; -static int signal_print_starting; static int sending_print_starting; static void print_line(TEXT_DEST_REC *dest, const char *text); -static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, - int formatnum, va_list va) +void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest, + int formatnum, va_list va) { char *arglist[MAX_FORMAT_PARAMS]; char buffer[DEFAULT_FORMAT_ARGLIST_SIZE]; @@ -54,8 +52,7 @@ static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, THEME_REC *theme; char *str; - theme = dest->window->theme == NULL ? current_theme : - dest->window->theme; + theme = window_get_theme(dest->window); formats = g_hash_table_lookup(default_formats, module); format_read_arglist(va, &formats[formatnum], @@ -77,6 +74,16 @@ static void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, g_free(str); } +void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, + int formatnum, ...) +{ + va_list va; + + va_start(va, formatnum); + printformat_module_dest_args(module, dest, formatnum, va); + va_end(va); +} + void printformat_module_args(const char *module, void *server, const char *target, int level, int formatnum, va_list va) @@ -84,7 +91,7 @@ void printformat_module_args(const char *module, void *server, TEXT_DEST_REC dest; format_create_dest(&dest, server, target, level, NULL); - printformat_module_dest(module, &dest, formatnum, va); + printformat_module_dest_args(module, &dest, formatnum, va); } void printformat_module(const char *module, void *server, const char *target, @@ -103,7 +110,7 @@ void printformat_module_window_args(const char *module, WINDOW_REC *window, TEXT_DEST_REC dest; format_create_dest(&dest, NULL, NULL, level, window); - printformat_module_dest(module, &dest, formatnum, va); + printformat_module_dest_args(module, &dest, formatnum, va); } void printformat_module_window(const char *module, WINDOW_REC *window, @@ -133,7 +140,8 @@ void printformat_module_gui_args(const char *module, int formatnum, va_list va) arglist, sizeof(arglist)/sizeof(char *), buffer, sizeof(buffer)); - str = format_get_text_theme_charargs(current_theme, module, &dest, + str = format_get_text_theme_charargs(window_get_theme(dest.window), + module, &dest, formatnum, arglist); if (*str != '\0') format_send_to_gui(&dest, str); g_free(str); @@ -150,22 +158,22 @@ void printformat_module_gui(const char *module, int formatnum, ...) static void print_line(TEXT_DEST_REC *dest, const char *text) { - char *str, *tmp; + char *str, *tmp, *stripped; g_return_if_fail(dest != NULL); g_return_if_fail(text != NULL); - tmp = format_get_level_tag(current_theme, dest); + tmp = format_get_level_tag(window_get_theme(dest->window), dest); str = format_add_linestart(text, tmp); g_free_not_null(tmp); - /* send the plain text version for logging etc.. */ - tmp = strip_codes(str); - signal_emit_id(signal_print_text_stripped, 2, dest, tmp); - g_free(tmp); + /* send both the formatted + stripped (for logging etc.) */ + stripped = strip_codes(str); + signal_emit_id(signal_print_text, 3, dest, str, stripped); + g_free_and_null(dest->hilight_color); - signal_emit_id(signal_print_text, 2, dest, str); g_free(str); + g_free(stripped); } /* append string to `out', expand newlines. */ @@ -237,7 +245,7 @@ static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str, break; } default: - if (!format_expand_styles(out, *str)) { + if (!format_expand_styles(out, &str, &dest->flags)) { g_string_append_c(out, '%'); g_string_append_c(out, *str); } @@ -250,7 +258,7 @@ static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str, return ret; } -static char *printtext_expand_formats(const char *str) +static char *printtext_expand_formats(const char *str, int *flags) { GString *out; char *ret; @@ -265,7 +273,7 @@ static char *printtext_expand_formats(const char *str) if (*++str == '\0') break; - if (!format_expand_styles(out, *str)) { + if (!format_expand_styles(out, &str, flags)) { g_string_append_c(out, '%'); g_string_append_c(out, *str); } @@ -276,7 +284,7 @@ static char *printtext_expand_formats(const char *str) return ret; } -void printtext_dest(TEXT_DEST_REC *dest, const char *text, va_list va) +static void printtext_dest_args(TEXT_DEST_REC *dest, const char *text, va_list va) { char *str; @@ -291,6 +299,15 @@ void printtext_dest(TEXT_DEST_REC *dest, const char *text, va_list va) g_free(str); } +void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...) +{ + va_list va; + + va_start(va, text); + printtext_dest_args(dest, text, va); + va_end(va); +} + /* Write text to target - convert color codes */ void printtext(void *server, const char *target, int level, const char *text, ...) { @@ -302,7 +319,7 @@ void printtext(void *server, const char *target, int level, const char *text, .. format_create_dest(&dest, server, target, level, NULL); va_start(va, text); - printtext_dest(&dest, text, va); + printtext_dest_args(&dest, text, va); va_end(va); } @@ -322,7 +339,29 @@ void printtext_string(void *server, const char *target, int level, const char *t sending_print_starting = FALSE; } - str = printtext_expand_formats(text); + str = printtext_expand_formats(text, &dest.flags); + print_line(&dest, str); + g_free(str); +} + +/* Like printtext_window(), but don't handle %s etc. */ +void printtext_string_window(WINDOW_REC *window, int level, const char *text) +{ + TEXT_DEST_REC dest; + char *str; + + g_return_if_fail(text != NULL); + + format_create_dest(&dest, NULL, NULL, level, + window != NULL ? window : active_win); + + if (!sending_print_starting) { + sending_print_starting = TRUE; + signal_emit_id(signal_print_starting, 1, dest); + sending_print_starting = FALSE; + } + + str = printtext_expand_formats(text, &dest.flags); print_line(&dest, str); g_free(str); } @@ -338,7 +377,7 @@ void printtext_window(WINDOW_REC *window, int level, const char *text, ...) window != NULL ? window : active_win); va_start(va, text); - printtext_dest(&dest, text, va); + printtext_dest_args(&dest, text, va); va_end(va); } @@ -351,14 +390,14 @@ void printtext_gui(const char *text) memset(&dest, 0, sizeof(dest)); - str = printtext_expand_formats(text); + str = printtext_expand_formats(text, &dest.flags); format_send_to_gui(&dest, str); g_free(str); } static void msg_beep_check(TEXT_DEST_REC *dest) { - if (dest->level != 0 && (dest->level & MSGLEVEL_NOHILIGHT) == 0 && + if (dest->level != 0 && (dest->level & MSGLEVEL_NO_ACT) == 0 && (beep_msg_level & dest->level) && (beep_when_away || (dest->server != NULL && !dest->server->usermode_away)) && @@ -372,6 +411,7 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text) char *str, *tmp; g_return_if_fail(dest != NULL); + g_return_if_fail(dest->window != NULL); g_return_if_fail(text != NULL); msg_beep_check(dest); @@ -380,19 +420,15 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text) /* add timestamp/server tag here - if it's done in print_line() it would be written to log files too */ - tmp = format_get_line_start(current_theme, dest, time(NULL)); + tmp = format_get_line_start(window_get_theme(dest->window), + dest, time(NULL)); str = format_add_linestart(text, tmp); g_free_not_null(tmp); format_send_to_gui(dest, str); g_free(str); - signal_emit_id(signal_print_text_finished, 1, dest->window); -} - -static void sig_print_text_free(TEXT_DEST_REC *dest, const char *text) -{ - g_free_and_null(dest->hilight_color); + signal_emit_id(signal_gui_print_text_finished, 1, dest->window); } void printtext_multiline(void *server, const char *target, int level, @@ -433,16 +469,13 @@ static void read_settings(void) void printtext_init(void) { sending_print_starting = FALSE; - signal_gui_print_text = signal_get_uniq_id("gui print text"); - signal_print_text_stripped = signal_get_uniq_id("print text stripped"); + signal_gui_print_text_finished = signal_get_uniq_id("gui print text finished"); + signal_print_starting = signal_get_uniq_id("print starting"); signal_print_text = signal_get_uniq_id("print text"); - signal_print_text_finished = signal_get_uniq_id("print text finished"); signal_print_format = signal_get_uniq_id("print format"); - signal_print_starting = signal_get_uniq_id("print starting"); read_settings(); signal_add("print text", (SIGNAL_FUNC) sig_print_text); - signal_add_last("print text", (SIGNAL_FUNC) sig_print_text_free); signal_add("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); signal_add("setup changed", (SIGNAL_FUNC) read_settings); } @@ -450,7 +483,6 @@ void printtext_init(void) void printtext_deinit(void) { signal_remove("print text", (SIGNAL_FUNC) sig_print_text); - signal_remove("print text", (SIGNAL_FUNC) sig_print_text_free); signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); } diff --git a/apps/irssi/src/fe-common/core/printtext.h b/apps/irssi/src/fe-common/core/printtext.h index 14f46564..463f3572 100644 --- a/apps/irssi/src/fe-common/core/printtext.h +++ b/apps/irssi/src/fe-common/core/printtext.h @@ -2,17 +2,22 @@ #define __PRINTTEXT_H #include "fe-windows.h" +#include "formats.h" void printformat_module(const char *module, void *server, const char *target, int level, int formatnum, ...); void printformat_module_window(const char *module, WINDOW_REC *window, int level, int formatnum, ...); +void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, int formatnum, ...); void printformat_module_args(const char *module, void *server, const char *target, int level, int formatnum, va_list va); void printformat_module_window_args(const char *module, WINDOW_REC *window, int level, int formatnum, va_list va); +void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest, int formatnum, va_list va); void printtext(void *server, const char *target, int level, const char *text, ...); void printtext_string(void *server, const char *target, int level, const char *text); +void printtext_string_window(WINDOW_REC *window, int level, const char *text); void printtext_window(WINDOW_REC *window, int level, const char *text, ...); void printtext_multiline(void *server, const char *target, int level, const char *format, const char *text); +void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...); /* only GUI should call these - used for printing text to somewhere else than windows */ @@ -35,6 +40,8 @@ void printtext_deinit(void); printformat_module(MODULE_NAME, server, target, level, ##formatnum) # define printformat_window(window, level, formatnum...) \ printformat_module_window(MODULE_NAME, window, level, ##formatnum) +# define printformat_dest(dest, formatnum...) \ + printformat_module_dest(MODULE_NAME, dest, ##formatnum) # define printformat_gui(formatnum...) \ printformat_module_gui(MODULE_NAME, ##formatnum) #elif defined (_ISOC99_SOURCE) @@ -43,6 +50,8 @@ void printtext_deinit(void); printformat_module(MODULE_NAME, server, target, level, formatnum, __VA_ARGS__) # define printformat_window(window, level, formatnum, ...) \ printformat_module_window(MODULE_NAME, window, level, formatnum, __VA_ARGS__) +# define printformat_dest(dest, formatnum, ...) \ + printformat_module_dest(MODULE_NAME, dest, formatnum, __VA_ARGS__) # define printformat_gui(formatnum, ...) \ printformat_module_gui(MODULE_NAME, formatnum, __VA_ARGS__) #else @@ -75,6 +84,20 @@ void printformat_window(WINDOW_REC *window, int level, int formatnum, ...) va_end(va); } +#ifdef G_CAN_INLINE +G_INLINE_FUNC +#else +static +#endif +void printformat_dest(TEXT_DEST_REC *dest, int formatnum, ...) +{ + va_list va; + + va_start(va, formatnum); + printformat_module_dest_args(MODULE_NAME, dest, formatnum, va); + va_end(va); +} + #ifdef G_CAN_INLINE G_INLINE_FUNC #else diff --git a/apps/irssi/src/fe-common/core/themes.c b/apps/irssi/src/fe-common/core/themes.c index ab768ccb..f009f0ba 100644 --- a/apps/irssi/src/fe-common/core/themes.c +++ b/apps/irssi/src/fe-common/core/themes.c @@ -119,7 +119,8 @@ static char *theme_replace_expand(THEME_REC *theme, int index, abstract = theme_format_expand_data(theme, (const char **) &abstract, default_fg, default_bg, last_fg, last_bg, flags); - ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0); + ret = parse_special_string(abstract, NULL, NULL, data, NULL, + PARSE_FLAG_ONLY_ARGS); g_free(abstract); return ret; } @@ -128,9 +129,9 @@ static const char *fgcolorformats = "nkrgybmpcwKRGYBMPCW"; static const char *bgcolorformats = "n01234567"; #define IS_FGCOLOR_FORMAT(c) \ - ((c) != '\0' && strchr(fgcolorformats, (c)) != NULL) + ((c) != '\0' && strchr(fgcolorformats, c) != NULL) #define IS_BGCOLOR_FORMAT(c) \ - ((c) != '\0' && strchr(bgcolorformats, (c)) != NULL) + ((c) != '\0' && strchr(bgcolorformats, c) != NULL) /* append "variable" part in $variable, ie. not the contents of the variable */ static void theme_format_append_variable(GString *str, const char **format) @@ -143,7 +144,7 @@ static void theme_format_append_variable(GString *str, const char **format) (*format)++; value = parse_special((char **) format, NULL, NULL, - args, &free_ret, NULL, 0); + args, &free_ret, NULL, PARSE_FLAG_ONLY_ARGS); if (free_ret) g_free(value); (*format)++; @@ -211,7 +212,9 @@ static void theme_format_append_next(THEME_REC *theme, GString *str, return; } - /* %{ or %} gives us { or } char */ + /* %{ or %} gives us { or } char - keep the % char + though to make sure {} isn't treated as abstract */ + g_string_append_c(str, '%'); chr = **format; } @@ -232,6 +235,54 @@ static void theme_format_append_next(THEME_REC *theme, GString *str, (*format)++; } +/* returns TRUE if data is empty, or the data is a $variable which is empty */ +static int data_is_empty(const char **data) +{ + /* since we don't know the real argument list, assume there's always + an argument in them */ + char *arglist[] = { + "x", "x", "x", "x", "x", "x","x", "x", "x", "x", + NULL + }; + const char *p; + char *ret; + int free_ret, empty; + + p = *data; + while (*p == ' ') p++; + + if (*p == '}') { + /* empty */ + *data = p+1; + return TRUE; + } + + if (*p != '$') { + /* not empty */ + return FALSE; + } + + /* variable - check if it's empty */ + p++; + ret = parse_special((char **) &p, + active_win == NULL ? NULL : active_win->active_server, + active_win == NULL ? NULL : active_win->active, + arglist, &free_ret, NULL, 0); + p++; + + while (*p == ' ') p++; + empty = *p == '}' && (ret == NULL || *ret == '\0'); + if (free_ret) g_free(ret); + + if (empty) { + /* empty */ + *data = p+1; + return TRUE; + } + + return FALSE; +} + /* expand a single {abstract ...data... } */ static char *theme_format_expand_abstract(THEME_REC *theme, const char **formatp, @@ -258,17 +309,11 @@ static char *theme_format_expand_abstract(THEME_REC *theme, treated as arguments */ if (*p == ' ') { len++; - if ((flags & EXPAND_FLAG_IGNORE_EMPTY)) { - /* if the data is empty, ignore the abstract */ - p = format+len; - while (*p == ' ') p++; - if (*p == '}') { - *formatp = p+1; - g_free(abstract); - return NULL; - } + if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) { + *formatp = p; + g_free(abstract); + return NULL; } - } *formatp = format+len; @@ -287,7 +332,7 @@ static char *theme_format_expand_abstract(THEME_REC *theme, NULL, NULL, flags); len = strlen(data); - if (len > 1 && isdigit(data[len-1]) && data[len-2] == '$') { + if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') { /* ends with $ .. this breaks things if next character is digit or '-' */ char digit, *tmp; @@ -300,7 +345,8 @@ static char *theme_format_expand_abstract(THEME_REC *theme, g_free(tmp); } - ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0); + ret = parse_special_string(abstract, NULL, NULL, data, NULL, + PARSE_FLAG_ONLY_ARGS); g_free(abstract); g_free(data); abstract = ret; @@ -701,11 +747,11 @@ THEME_REC *theme_load(const char *setname) theme = theme_find(name); /* check home dir */ - fname = g_strdup_printf("%s/.silc/%s.theme", g_get_home_dir(), name); + fname = g_strdup_printf("%s/%s.theme", get_irssi_dir(), name); if (stat(fname, &statbuf) != 0) { /* check global config dir */ g_free(fname); - fname = g_strdup_printf(SYSCONFDIR"/irssi/%s.theme", name); + fname = g_strdup_printf(THEMESDIR"/%s.theme", name); if (stat(fname, &statbuf) != 0) { /* theme not found */ g_free(fname); @@ -795,9 +841,11 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data) } theme->default_color = - config_get_int(config, NULL, "default_color", 0); - theme->default_real_color = - config_get_int(config, NULL, "default_real_color", 7); + config_get_int(config, NULL, "default_color", -1); + /* FIXME: remove after 0.7.99 */ + if (theme->default_color == 0 && + config_get_int(config, NULL, "default_real_color", -1) != -1) + theme->default_color = -1; theme_read_replaces(config, theme); if (data == NULL) { @@ -952,8 +1000,13 @@ static void cmd_format(const char *data) cmd_params_free(free_arg); } +typedef struct { + CONFIG_REC *config; + int save_all; +} THEME_SAVE_REC; + static void module_save(const char *module, MODULE_THEME_REC *rec, - CONFIG_REC *config) + THEME_SAVE_REC *data) { CONFIG_NODE *fnode, *node; FORMAT_REC *formats; @@ -962,27 +1015,33 @@ static void module_save(const char *module, MODULE_THEME_REC *rec, formats = g_hash_table_lookup(default_formats, rec->name); if (formats == NULL) return; - fnode = config_node_traverse(config, "formats", TRUE); + fnode = config_node_traverse(data->config, "formats", TRUE); node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK); - for (n = 0; formats[n].def != NULL; n++) { + for (n = 1; formats[n].def != NULL; n++) { if (rec->formats[n] != NULL) { - config_node_set_str(config, node, formats[n].tag, + config_node_set_str(data->config, node, formats[n].tag, rec->formats[n]); - } + } else if (data->save_all && formats[n].tag != NULL) { + config_node_set_str(data->config, node, formats[n].tag, + formats[n].def); + } } if (node->value == NULL) { /* not modified, don't keep the empty section */ - config_node_remove(config, fnode, node); - if (fnode->value == NULL) - config_node_remove(config, config->mainnode, fnode); + config_node_remove(data->config, fnode, node); + if (fnode->value == NULL) { + config_node_remove(data->config, + data->config->mainnode, fnode); + } } } -static void theme_save(THEME_REC *theme) +static void theme_save(THEME_REC *theme, int save_all) { CONFIG_REC *config; + THEME_SAVE_REC data; char *path; int ok; @@ -1002,10 +1061,12 @@ static void theme_save(THEME_REC *theme) } } - g_hash_table_foreach(theme->modules, (GHFunc) module_save, config); + data.config = config; + data.save_all = save_all; + g_hash_table_foreach(theme->modules, (GHFunc) module_save, &data); - /* always save the theme to ~/.silc/ */ - path = g_strdup_printf("%s/.silc/%s", g_get_home_dir(), + /* always save the theme to ~/.irssi/ */ + path = g_strdup_printf("%s/%s", get_irssi_dir(), g_basename(theme->path)); ok = config_write(config, path, 0660) == 0; @@ -1017,16 +1078,27 @@ static void theme_save(THEME_REC *theme) config_close(config); } -/* save changed formats */ -static void cmd_save(void) +/* save changed formats, -format saves all */ +static void cmd_save(const char *data) { GSList *tmp; + GHashTable *optlist; + void *free_arg; + char *fname; + int saveall; + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "save", &optlist, &fname)) + return; + + saveall = g_hash_table_lookup(optlist, "formats") != NULL; for (tmp = themes; tmp != NULL; tmp = tmp->next) { THEME_REC *theme = tmp->data; - theme_save(theme); + theme_save(theme, saveall); } + + cmd_params_free(free_arg); } static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list) @@ -1102,9 +1174,9 @@ static void change_theme(const char *name, int verbose) if (rec != NULL) { current_theme = rec; if (verbose) { - printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, - TXT_THEME_CHANGED, - rec->name, rec->path); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_THEME_CHANGED, + rec->name, rec->path); } } else if (verbose) { printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, @@ -1115,9 +1187,13 @@ static void change_theme(const char *name, int verbose) static void read_settings(void) { const char *theme; + int len; theme = settings_get_str("theme"); - if (strcmp(current_theme->name, theme) != 0) + len = strlen(current_theme->name); + if (strcmp(current_theme->name, theme) != 0 && + (strncmp(current_theme->name, theme, len) != 0 || + strcmp(theme+len, ".theme") != 0)) change_theme(theme, TRUE); } @@ -1131,11 +1207,9 @@ static void themes_read(void) /* first there's default theme.. */ current_theme = theme_load("default"); if (current_theme == NULL) { - fname = g_strdup_printf("%s/.silc/default.theme", - g_get_home_dir()); + fname = g_strdup_printf("%s/default.theme", get_irssi_dir()); current_theme = theme_create(fname, "default"); - current_theme->default_color = 0; - current_theme->default_real_color = 7; + current_theme->default_color = -1; theme_read(current_theme, NULL, default_theme); g_free(fname); } @@ -1165,6 +1239,7 @@ void themes_init(void) signal_add("setup reread", (SIGNAL_FUNC) themes_read); command_set_options("format", "delete reset"); + command_set_options("save", "formats"); } void themes_deinit(void) diff --git a/apps/irssi/src/fe-common/core/themes.h b/apps/irssi/src/fe-common/core/themes.h index 96f23400..59d2810a 100644 --- a/apps/irssi/src/fe-common/core/themes.h +++ b/apps/irssi/src/fe-common/core/themes.h @@ -16,11 +16,8 @@ typedef struct { time_t last_modify; int default_color; /* default color to use with text with default - background. default is 0 which means the default - color set by terminal */ - int default_real_color; /* default color to use with background set. - this shouldn't be 0, unless black is really - wanted. default is 7 (white). */ + background. default is -1 which means the + default color set by terminal */ GHashTable *modules; int replace_keys[256]; /* index to replace_values for each char */ @@ -47,7 +44,7 @@ void theme_register_module(const char *module, FORMAT_REC *formats); void theme_unregister_module(const char *module); #define EXPAND_FLAG_IGNORE_REPLACES 0x01 /* don't use the character replaces when expanding */ -#define EXPAND_FLAG_IGNORE_EMPTY 0x02 /* if abstract's argument is empty, don't try to expand it (ie. {xx }, but not {xx}) */ +#define EXPAND_FLAG_IGNORE_EMPTY 0x02 /* if abstract's argument is empty, or the argument is a $variable that is empty, don't try to expand it (ie. {xx }, but not {xx}) */ #define EXPAND_FLAG_RECURSIVE_MASK 0x0f /* private */ #define EXPAND_FLAG_ROOT 0x10 diff --git a/apps/irssi/src/fe-common/core/translation.c b/apps/irssi/src/fe-common/core/translation.c index 2713cc73..d573fc17 100644 --- a/apps/irssi/src/fe-common/core/translation.c +++ b/apps/irssi/src/fe-common/core/translation.c @@ -19,12 +19,17 @@ */ #include "module.h" +#include "module-formats.h" #include "signals.h" #include "line-split.h" #include "misc.h" +#include "levels.h" #include "settings.h" +#include "printtext.h" + unsigned char translation_in[256], translation_out[256]; +static char *current_translation; void translation_reset(void) { @@ -45,7 +50,7 @@ void translate_output(char *text) } #define gethex(a) \ - (isdigit(a) ? ((a)-'0') : (toupper(a)-'A'+10)) + (i_isdigit(a) ? ((a)-'0') : (i_toupper(a)-'A'+10)) void translation_parse_line(const char *str, int *pos) { @@ -81,7 +86,12 @@ int translation_read(const char *file) f = open(file, O_RDONLY); g_free(path); - if (f == -1) return FALSE; + if (f == -1) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_TRANSLATION_NOT_FOUND, file, + g_strerror(errno)); + return FALSE; + } pos = 0; buffer = NULL; while (pos < 512) { @@ -95,20 +105,40 @@ int translation_read(const char *file) line_split_free(buffer); close(f); - if (pos != 512) + if (pos != 512) { translation_reset(); + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_TRANSLATION_FILE_ERROR, file); + } return pos == 512; } static void read_settings(void) { - translation_read(settings_get_str("translation")); + const char *translation; + + translation = settings_get_str("translation"); + if (*translation == '\0') { + if (current_translation != NULL) { + g_free_and_null(current_translation); + translation_reset(); + } + return; + } + + if (current_translation == NULL || + strcmp(translation, current_translation) != 0) { + g_free_not_null(current_translation); + current_translation = g_strdup(translation); + translation_read(translation); + } } void translation_init(void) { translation_reset(); + current_translation = NULL; settings_add_str("misc", "translation", ""); signal_add("setup changed", (SIGNAL_FUNC) read_settings); diff --git a/apps/irssi/src/fe-common/core/window-commands.c b/apps/irssi/src/fe-common/core/window-commands.c index 17cf65e5..e388f23b 100644 --- a/apps/irssi/src/fe-common/core/window-commands.c +++ b/apps/irssi/src/fe-common/core/window-commands.c @@ -33,14 +33,131 @@ #include "windows-layout.h" #include "printtext.h" -static void cmd_window(const char *data, void *server, WI_ITEM_REC *item) +static void window_print_binds(WINDOW_REC *win) { - if (is_numeric(data, 0)) { - signal_emit("command window refnum", 3, data, server, item); - return; + GSList *tmp; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_BOUND_ITEMS_HEADER); + for (tmp = win->bound_items; tmp != NULL; tmp = tmp->next) { + WINDOW_BIND_REC *bind = tmp->data; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_BOUND_ITEM, + bind->name, bind->servertag, + bind->sticky ? "sticky" : ""); + } + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER); +} + +static void window_print_items(WINDOW_REC *win) +{ + GSList *tmp; + const char *type; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_ITEMS_HEADER); + for (tmp = win->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *item = tmp->data; + + type = module_find_id_str("WINDOW ITEM TYPE", item->type); + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_ITEM, + type == NULL ? "??" : type, item->name, + item->server == NULL ? "" : + item->server->tag); + } + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_ITEMS_FOOTER); +} + +static void cmd_window_info(WINDOW_REC *win) +{ + char *levelstr; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_HEADER); + + /* Window reference number + sticky status */ + if (!win->sticky_refnum) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_REFNUM, win->refnum); + } else { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_REFNUM_STICKY, win->refnum); + } + + /* Window name */ + if (win->name != NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_NAME, win->name); + } + + /* Window history name */ + if (win->history_name != NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_HISTORY, win->history_name); + } + + /* Window width / height */ + printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_SIZE, + win->width, win->height); + + /* Window level */ + levelstr = win->level == 0 ? + g_strdup("NONE") : bits2level(win->level); + printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_LEVEL, + levelstr); + g_free(levelstr); + + /* Active window server + sticky status */ + if (win->servertag == NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_SERVER, + win->active_server != NULL ? + win->active_server->tag : "NONE"); + } else { + if (win->active_server != NULL && + strcmp(win->active_server->tag, win->servertag) != 0) + g_warning("Active server isn't the sticky server!"); + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_SERVER_STICKY, + win->servertag); } - command_runsub("window", data, server, item); + /* Window theme + error status */ + if (win->theme_name != NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_THEME, win->theme_name, + win->theme != NULL ? "" : "(not loaded)"); + } + + /* Bound items in window */ + if (win->bound_items != NULL) + window_print_binds(win); + + /* Item */ + if (win->items != NULL) + window_print_items(win); + + signal_emit("window print info", 1, win); + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_FOOTER); +} + +static void cmd_window(const char *data, void *server, WI_ITEM_REC *item) +{ + while (*data == ' ') data++; + + if (*data == '\0') + cmd_window_info(active_win); + else if (is_numeric(data, 0)) + signal_emit("command window refnum", 3, data, server, item); + else + command_runsub("window", data, server, item); } /* SYNTAX: WINDOW NEW [hide] */ @@ -77,7 +194,7 @@ static void cmd_window_close(const char *data) } first_num = *first == '\0' ? active_win->refnum : atoi(first); - last_num = *last == '\0' ? active_win->refnum : atoi(last); + last_num = *last == '\0' ? first_num : atoi(last); /* get list of windows to destroy */ destroys = NULL; @@ -224,7 +341,7 @@ static void cmd_window_server(const char *data) "window server", &optlist, &tag)) return; - if (*tag == '\0' && + if (*tag == '\0' && active_win->active_server != NULL && (g_hash_table_lookup(optlist, "sticky") != NULL || g_hash_table_lookup(optlist, "unsticky") != NULL)) { tag = active_win->active_server->tag; @@ -238,7 +355,7 @@ static void cmd_window_server(const char *data) active_win->servertag != NULL) { g_free_and_null(active_win->servertag); printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, - TXT_UNSET_SERVER_STICKY, server->tag); + TXT_UNSET_SERVER_STICKY); } if (active_win->servertag != NULL && @@ -297,18 +414,25 @@ static void cmd_window_item_goto(const char *data, SERVER_REC *server) static void cmd_window_item_move(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { - WINDOW_REC *window; + WINDOW_REC *window; + void *free_arg; + char *target; - if (is_numeric(data, '\0')) { + if (!cmd_get_params(data, &free_arg, 1, &target)) + return; + + if (is_numeric(target, '\0')) { /* move current window item to specified window */ - window = window_find_refnum(atoi(data)); + window = window_find_refnum(atoi(target)); } else { /* move specified window item to current window */ - item = window_item_find(server, data); + item = window_item_find(server, target); window = active_win; } if (window != NULL && item != NULL) - window_item_set_active(window, item); + window_item_set_active(window, item); + + cmd_params_free(free_arg); } /* SYNTAX: WINDOW NUMBER [-sticky] */ @@ -342,75 +466,101 @@ static void cmd_window_number(const char *data) /* SYNTAX: WINDOW 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 */ +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 |left|right */ +/* SYNTAX: WINDOW MOVE | */ static void cmd_window_move(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { int new_refnum, refnum; @@ -463,23 +613,49 @@ static void cmd_window_list(void) printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_FOOTER); } -/* SYNTAX: WINDOW THEME */ +/* SYNTAX: WINDOW THEME [-delete] [] */ static void cmd_window_theme(const char *data) { THEME_REC *theme; + GHashTable *optlist; + char *name; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "window theme", &optlist, &name)) + return; - g_free_not_null(active_win->theme_name); - active_win->theme_name = g_strdup(data); + if (g_hash_table_lookup(optlist, "delete") != NULL) { + g_free_and_null(active_win->theme_name); - active_win->theme = theme = theme_load(data); - if (theme != NULL) { printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, - TXT_WINDOW_THEME_CHANGED, - theme->name, theme->path); + TXT_WINDOW_THEME_REMOVED); + } else if (*name == '\0') { + if (active_win->theme == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_THEME_DEFAULT); + } else { + theme = active_win->theme; + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_THEME, + theme->name, theme->path); + } } else { - printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, - TXT_THEME_NOT_FOUND, data); + g_free_not_null(active_win->theme_name); + active_win->theme_name = g_strdup(data); + + active_win->theme = theme = theme_load(data); + if (theme != NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_THEME_CHANGED, + theme->name, theme->path); + } else { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_THEME_NOT_FOUND, data); + } } + + cmd_params_free(free_arg); } static void cmd_layout(const char *data, SERVER_REC *server, WI_ITEM_REC *item) @@ -491,16 +667,20 @@ static void cmd_layout(const char *data, SERVER_REC *server, WI_ITEM_REC *item) static void cmd_foreach_window(const char *data) { WINDOW_REC *old; - GSList *tmp; + GSList *list; old = active_win; - for (tmp = windows; tmp != NULL; tmp = tmp->next) { - WINDOW_REC *rec = tmp->data; - active_win = rec; + list = g_slist_copy(windows); + while (list != NULL) { + WINDOW_REC *rec = list->data; + + active_win = rec; signal_emit("send command", 3, data, rec->active_server, rec->active); + list = g_slist_remove(list, list->data); } + active_win = old; } @@ -524,9 +704,10 @@ void window_commands_init(void) command_bind("window item move", NULL, (SIGNAL_FUNC) cmd_window_item_move); command_bind("window number", NULL, (SIGNAL_FUNC) cmd_window_number); command_bind("window name", NULL, (SIGNAL_FUNC) cmd_window_name); + command_bind("window history", NULL, (SIGNAL_FUNC) cmd_window_history); command_bind("window move", NULL, (SIGNAL_FUNC) cmd_window_move); - command_bind("window move left", NULL, (SIGNAL_FUNC) cmd_window_move_left); - command_bind("window move right", NULL, (SIGNAL_FUNC) cmd_window_move_right); + command_bind("window move prev", NULL, (SIGNAL_FUNC) cmd_window_move_prev); + command_bind("window move next", NULL, (SIGNAL_FUNC) cmd_window_move_next); command_bind("window list", NULL, (SIGNAL_FUNC) cmd_window_list); command_bind("window theme", NULL, (SIGNAL_FUNC) cmd_window_theme); command_bind("layout", NULL, (SIGNAL_FUNC) cmd_layout); @@ -538,6 +719,7 @@ void window_commands_init(void) command_set_options("window number", "sticky"); command_set_options("window server", "sticky unsticky"); + command_set_options("window theme", "delete"); } void window_commands_deinit(void) @@ -560,9 +742,10 @@ void window_commands_deinit(void) command_unbind("window item move", (SIGNAL_FUNC) cmd_window_item_move); command_unbind("window number", (SIGNAL_FUNC) cmd_window_number); command_unbind("window name", (SIGNAL_FUNC) cmd_window_name); + command_unbind("window history", (SIGNAL_FUNC) cmd_window_history); command_unbind("window move", (SIGNAL_FUNC) cmd_window_move); - command_unbind("window move left", (SIGNAL_FUNC) cmd_window_move_left); - command_unbind("window move right", (SIGNAL_FUNC) cmd_window_move_right); + command_unbind("window move prev", (SIGNAL_FUNC) cmd_window_move_prev); + command_unbind("window move next", (SIGNAL_FUNC) cmd_window_move_next); command_unbind("window list", (SIGNAL_FUNC) cmd_window_list); command_unbind("window theme", (SIGNAL_FUNC) cmd_window_theme); command_unbind("layout", (SIGNAL_FUNC) cmd_layout); diff --git a/apps/irssi/src/fe-common/core/window-items.c b/apps/irssi/src/fe-common/core/window-items.c index d7726521..e53e5a1d 100644 --- a/apps/irssi/src/fe-common/core/window-items.c +++ b/apps/irssi/src/fe-common/core/window-items.c @@ -35,6 +35,7 @@ void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic) { g_return_if_fail(window != NULL); g_return_if_fail(item != NULL); + g_return_if_fail(item->window == NULL); item->window = window; @@ -52,7 +53,8 @@ void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic) window->items = g_slist_append(window->items, item); signal_emit("window item new", 2, window, item); - if (!automatic || g_slist_length(window->items) == 1) { + if (g_slist_length(window->items) == 1 || + (!automatic && settings_get_bool("autofocus_new_items"))) { window->active = NULL; window_item_set_active(window, item); } @@ -66,7 +68,7 @@ void window_item_remove(WI_ITEM_REC *item) window = window_item_window(item); - if (g_slist_find(window->items, item) == NULL) + if (window == NULL) return; item->window = NULL; @@ -86,8 +88,7 @@ void window_item_destroy(WI_ITEM_REC *item) window = window_item_window(item); window_item_remove(item); - - signal_emit("window item destroy", 2, window, item); + item->destroy(item); } void window_item_change_server(WI_ITEM_REC *item, void *server) @@ -241,14 +242,13 @@ static int window_bind_has_sticky(WINDOW_REC *window) void window_item_create(WI_ITEM_REC *item, int automatic) { WINDOW_REC *window; + WINDOW_BIND_REC *bind; GSList *tmp, *sorted; int clear_waiting, reuse_unused_windows; g_return_if_fail(item != NULL); - reuse_unused_windows = - !settings_get_bool("autoclose_windows") || - settings_get_bool("reuse_unused_windows"); + reuse_unused_windows = settings_get_bool("reuse_unused_windows"); clear_waiting = TRUE; window = NULL; @@ -257,11 +257,16 @@ void window_item_create(WI_ITEM_REC *item, int automatic) WINDOW_REC *rec = tmp->data; /* is item bound to this window? */ - if (item->server != NULL && - window_bind_find(rec, item->server->tag, item->name)) { - window = rec; - clear_waiting = FALSE; - break; + if (item->server != NULL) { + bind = window_bind_find(rec, item->server->tag, + item->name); + if (bind != NULL) { + if (!bind->sticky) + window_bind_destroy(rec, bind); + window = rec; + clear_waiting = FALSE; + break; + } } /* use this window IF: @@ -290,6 +295,10 @@ void window_item_create(WI_ITEM_REC *item, int automatic) if (window == NULL) { /* create new window to use */ + if (settings_get_bool("autocreate_split_windows")) { + signal_emit("gui window create override", 1, + GINT_TO_POINTER(0)); + } window = window_create(item, automatic); } else { /* use existing window */ @@ -316,6 +325,8 @@ void window_items_init(void) { settings_add_bool("lookandfeel", "reuse_unused_windows", FALSE); settings_add_bool("lookandfeel", "autocreate_windows", TRUE); + settings_add_bool("lookandfeel", "autocreate_split_windows", FALSE); + settings_add_bool("lookandfeel", "autofocus_new_items", TRUE); signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed); } diff --git a/apps/irssi/src/fe-common/core/windows-layout.c b/apps/irssi/src/fe-common/core/windows-layout.c index 814127fb..56ebcb89 100644 --- a/apps/irssi/src/fe-common/core/windows-layout.c +++ b/apps/irssi/src/fe-common/core/windows-layout.c @@ -35,7 +35,16 @@ #include "fe-windows.h" #include "window-items.h" -static void sig_window_restore_item(WINDOW_REC *window, const char *type, +static WINDOW_REC *restore_win; + +static void signal_query_created_curwin(QUERY_REC *query) +{ + g_return_if_fail(IS_QUERY(query)); + + window_item_add(restore_win, (WI_ITEM_REC *) query, TRUE); +} + +static void sig_layout_restore_item(WINDOW_REC *window, const char *type, CONFIG_NODE *node) { char *name, *tag, *chat_type; @@ -53,7 +62,14 @@ static void sig_window_restore_item(WINDOW_REC *window, const char *type, rec->sticky = TRUE; } else if (g_strcasecmp(type, "QUERY") == 0 && chat_type != NULL) { /* create query immediately */ + signal_add("query created", + (SIGNAL_FUNC) signal_query_created_curwin); + + restore_win = window; chat_protocol_find(chat_type)->query_create(tag, name, TRUE); + + signal_remove("query created", + (SIGNAL_FUNC) signal_query_created_curwin); } } @@ -65,18 +81,24 @@ static void window_add_items(WINDOW_REC *window, CONFIG_NODE *node) if (node == NULL) return; - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { CONFIG_NODE *node = tmp->data; type = config_node_get_str(node, "type", NULL); if (type != NULL) { - signal_emit("window restore item", 3, + signal_emit("layout restore item", 3, window, type, node); } } } void windows_layout_restore(void) +{ + signal_emit("layout restore", 0); +} + +static void sig_layout_restore(void) { WINDOW_REC *window; CONFIG_NODE *node; @@ -85,13 +107,18 @@ void windows_layout_restore(void) node = iconfig_node_traverse("windows", FALSE); if (node == NULL) return; - for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { CONFIG_NODE *node = tmp->data; - window = window_create(NULL, TRUE); + window = window_find_refnum(atoi(node->key)); + if (window == NULL) + window = window_create(NULL, TRUE); + window_set_refnum(window, atoi(node->key)); window->sticky_refnum = config_node_get_bool(node, "sticky_refnum", FALSE); window_set_name(window, config_node_get_str(node, "name", NULL)); + window_set_history(window, config_node_get_str(node, "history_name", NULL)); window_set_level(window, level2bits(config_node_get_str(node, "level", ""))); window->servertag = g_strdup(config_node_get_str(node, "servertag", NULL)); @@ -100,10 +127,8 @@ void windows_layout_restore(void) window->theme = theme_load(window->theme_name); window_add_items(window, config_node_section(node, "items", -1)); - signal_emit("window restore", 2, window, node); + signal_emit("layout restore window", 2, window, node); } - - signal_emit("windows restored", 0); } static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node) @@ -148,6 +173,10 @@ static void window_save(WINDOW_REC *window, CONFIG_NODE *node) if (window->name != NULL) iconfig_node_set_str(node, "name", window->name); + + if (window->history_name != NULL) + iconfig_node_set_str(node, "history_name", window->history_name); + if (window->servertag != NULL) iconfig_node_set_str(node, "servertag", window->servertag); if (window->level != 0) { @@ -161,7 +190,7 @@ static void window_save(WINDOW_REC *window, CONFIG_NODE *node) if (window->items != NULL) window_save_items(window, node); - signal_emit("window save", 2, window, node); + signal_emit("layout save window", 2, window, node); } void windows_layout_save(void) @@ -172,7 +201,7 @@ void windows_layout_save(void) node = iconfig_node_traverse("windows", TRUE); g_slist_foreach(windows, (GFunc) window_save, node); - signal_emit("windows saved", 0); + signal_emit("layout save", 0); printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWS_LAYOUT_SAVED); @@ -181,16 +210,20 @@ void windows_layout_save(void) void windows_layout_reset(void) { iconfig_set_str(NULL, "windows", NULL); + signal_emit("layout reset", 0); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWS_LAYOUT_RESET); } void windows_layout_init(void) { - signal_add("window restore item", (SIGNAL_FUNC) sig_window_restore_item); + signal_add("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item); + signal_add("layout restore", (SIGNAL_FUNC) sig_layout_restore); } void windows_layout_deinit(void) { - signal_remove("window restore item", (SIGNAL_FUNC) sig_window_restore_item); + signal_remove("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item); + signal_remove("layout restore", (SIGNAL_FUNC) sig_layout_restore); } diff --git a/apps/irssi/src/fe-text/.cvsignore b/apps/irssi/src/fe-text/.cvsignore new file mode 100644 index 00000000..6974ee80 --- /dev/null +++ b/apps/irssi/src/fe-text/.cvsignore @@ -0,0 +1,9 @@ +*.la +*.lo +*.o +.deps +.libs +Makefile +Makefile.in +so_locations +irssi diff --git a/apps/irssi/src/fe-text/Makefile.am b/apps/irssi/src/fe-text/Makefile.am index 326ec232..6407d861 100644 --- a/apps/irssi/src/fe-text/Makefile.am +++ b/apps/irssi/src/fe-text/Makefile.am @@ -2,27 +2,48 @@ bin_PROGRAMS = silc include $(top_srcdir)/Makefile.defines.in -ADD_INCLUDES = \ - $(GLIB_CFLAGS) \ +INCLUDES = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/core/ \ - -I$(top_srcdir)/src/silc/core/ \ -I$(top_srcdir)/src/fe-common/core/ \ -I$(top_srcdir)/src/fe-common/silc/ \ + $(GLIB_CFLAGS) \ $(CURSES_INCLUDEDIR) \ -DLOCALEDIR=\""$(datadir)/locale"\" -silc_DEPENDENCIES = @COMMON_LIBS@ +silc_DEPENDENCIES = \ + @COMMON_LIBS@ \ + @PERL_LINK_LIBS@ \ + @PERL_FE_LINK_LIBS@ LIBS = $(SILC_COMMON_LIBS) silc_LDADD = \ @COMMON_LIBS@ \ @PERL_LINK_LIBS@ \ @PERL_FE_LINK_LIBS@ \ - $(PROG_LIBS) \ - $(CURSES_LIBS) \ + @PERL_LINK_FLAGS@ \ + @PROG_LIBS@ \ -L../../../lib -lsilcclient +tparm_sources = \ + tparm.c + +terminfo_sources = \ + term-terminfo.c \ + terminfo-core.c + +curses_sources = \ + term-curses.c + +if NEED_TPARM +use_tparm_sources = $(tparm_sources) +endif + +if USE_CURSES +use_term_sources = $(curses_sources) +else +use_term_sources = $(terminfo_sources) +endif silc_SOURCES = \ gui-entry.c \ @@ -33,13 +54,19 @@ silc_SOURCES = \ lastlog.c \ mainwindows.c \ mainwindow-activity.c \ - mainwindows-save.c \ - screen.c \ + mainwindows-layout.c \ statusbar.c \ + statusbar-config.c \ statusbar-items.c \ + term.c \ + term-dummy.c \ + $(use_tparm_sources) \ + $(use_term_sources) \ textbuffer.c \ textbuffer-commands.c \ + textbuffer-reformat.c \ textbuffer-view.c \ + utf8.c \ silc.c \ module-formats.c @@ -50,8 +77,17 @@ noinst_HEADERS = \ gui-windows.h \ mainwindows.h \ statusbar.h \ - screen.h \ + statusbar-config.h \ + term.h \ + terminfo-core.h \ textbuffer.h \ textbuffer-view.h \ + textbuffer-reformat.h \ + utf8.h \ module.h \ module-formats.h + +EXTRA_DIST = \ + $(tparm_sources) \ + $(terminfo_sources) \ + $(curses_sources) diff --git a/apps/irssi/src/fe-text/gui-entry.c b/apps/irssi/src/fe-text/gui-entry.c index 7544539a..f70bf0df 100644 --- a/apps/irssi/src/fe-text/gui-entry.c +++ b/apps/irssi/src/fe-text/gui-entry.c @@ -19,280 +19,446 @@ */ #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); } diff --git a/apps/irssi/src/fe-text/gui-entry.h b/apps/irssi/src/fe-text/gui-entry.h index ff8f3ef5..364f92da 100644 --- a/apps/irssi/src/fe-text/gui-entry.h +++ b/apps/irssi/src/fe-text/gui-entry.h @@ -1,31 +1,46 @@ #ifndef __GUI_ENTRY_H #define __GUI_ENTRY_H -void gui_entry_set_prompt(const char *str); +typedef struct { + GString *text; + int xpos, ypos, width; /* entry position in screen */ + int pos, scrstart, scrpos; /* cursor position */ + int hidden; /* print the chars as spaces in input line (useful for passwords) */ -/* permanent prompt can't be overwritten with gui_entry_set_prompt() */ -void gui_entry_set_perm_prompt(const char *str); -void gui_entry_remove_perm_prompt(void); -void gui_entry_set_hidden(int hidden); + int promptlen; + char *prompt; -void gui_entry_set_text(const char *str); -char *gui_entry_get_text(void); + int redraw_needed_from; + unsigned int utf8:1; +} GUI_ENTRY_REC; -void gui_entry_insert_text(const char *str); -void gui_entry_insert_char(char chr); +extern GUI_ENTRY_REC *active_entry; -void gui_entry_erase(int size); -void gui_entry_erase_word(void); -void gui_entry_erase_next_word(void); +GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8); +void gui_entry_destroy(GUI_ENTRY_REC *entry); -int gui_entry_get_pos(void); -void gui_entry_set_pos(int pos); -void gui_entry_move_pos(int pos); -void gui_entry_move_words(int count); +void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width); +void gui_entry_set_active(GUI_ENTRY_REC *entry); -void gui_entry_redraw(void); +void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str); +void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden); +void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8); -void gui_entry_init(void); -void gui_entry_deinit(void); +void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str); +char *gui_entry_get_text(GUI_ENTRY_REC *entry); + +void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str); +void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr); + +void gui_entry_erase(GUI_ENTRY_REC *entry, int size); +void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space); +void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space); + +int gui_entry_get_pos(GUI_ENTRY_REC *entry); +void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos); +void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos); +void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space); + +void gui_entry_redraw(GUI_ENTRY_REC *entry); #endif diff --git a/apps/irssi/src/fe-text/gui-expandos.c b/apps/irssi/src/fe-text/gui-expandos.c index c95ac621..b07bf129 100644 --- a/apps/irssi/src/fe-text/gui-expandos.c +++ b/apps/irssi/src/fe-text/gui-expandos.c @@ -37,7 +37,7 @@ static char *expando_idletime(SERVER_REC *server, void *item, int *free_ret) /* current contents of the input line */ static char *expando_inputline(SERVER_REC *server, void *item, int *free_ret) { - return gui_entry_get_text(); + return gui_entry_get_text(active_entry); } /* value of cutbuffer */ diff --git a/apps/irssi/src/fe-text/gui-printtext.c b/apps/irssi/src/fe-text/gui-printtext.c index 5faae0fa..3bd1de36 100644 --- a/apps/irssi/src/fe-text/gui-printtext.c +++ b/apps/irssi/src/fe-text/gui-printtext.c @@ -25,18 +25,73 @@ #include "formats.h" #include "printtext.h" -#include "screen.h" +#include "term.h" +#include "gui-printtext.h" #include "gui-windows.h" -int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 14, 10, 3, 11, 9, 13, 8, 7 }; +int mirc_colors[] = { 15, 0, 1, 2, 12, 4, 5, 6, 14, 10, 3, 11, 9, 13, 8, 7 }; static int scrollback_lines, scrollback_hours, scrollback_burst_remove; -static int scrollback_save_formats; -static GString *format; - -static int last_color, last_flags; +static int last_fg, last_bg, last_flags; static int next_xpos, next_ypos; +static GHashTable *indent_functions; +static INDENT_FUNC default_indent_func; + +void gui_register_indent_func(const char *name, INDENT_FUNC func) +{ + gpointer key, value; + GSList *list; + + if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) { + list = value; + g_hash_table_remove(indent_functions, key); + } else { + key = g_strdup(name); + list = NULL; + } + + list = g_slist_append(list, (void *) func); + g_hash_table_insert(indent_functions, key, list); +} + +void gui_unregister_indent_func(const char *name, INDENT_FUNC func) +{ + gpointer key, value; + GSList *list; + + if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) { + list = value; + + list = g_slist_remove(list, (void *) func); + g_hash_table_remove(indent_functions, key); + if (list == NULL) + g_free(key); + else + g_hash_table_insert(indent_functions, key, list); + } + + if (default_indent_func == func) + gui_set_default_indent(NULL); + + textbuffer_views_unregister_indent_func(func); +} + +void gui_set_default_indent(const char *name) +{ + GSList *list; + + list = name == NULL ? NULL : + g_hash_table_lookup(indent_functions, name); + default_indent_func = list == NULL ? NULL : list->data; + gui_windows_reset_settings(); +} + +INDENT_FUNC get_default_indent_func(void) +{ + return default_indent_func; +} + void gui_printtext(int xpos, int ypos, const char *str) { next_xpos = xpos; @@ -47,6 +102,18 @@ void gui_printtext(int xpos, int ypos, const char *str) next_xpos = next_ypos = -1; } +void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str) +{ + GUI_WINDOW_REC *gui; + + gui = WINDOW_GUI(dest->window); + + gui->use_insert_after = TRUE; + gui->insert_after = prev; + format_send_to_gui(dest, str); + gui->use_insert_after = FALSE; +} + static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view) { LINE_REC *line; @@ -57,9 +124,12 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view) scrollback_lines+scrollback_burst_remove) { /* remove lines by line count */ while (view->buffer->lines_count > scrollback_lines) { - line = view->buffer->lines->data; - if (line->info.time >= old_time) { - /* too new line, don't remove yet */ + line = view->buffer->first_line; + if (line->info.time >= old_time || + scrollback_lines == 0) { + /* too new line, don't remove yet - also + if scrollback_lines is 0, we want to check + only scrollback_hours setting. */ break; } textbuffer_view_remove_line(view, line); @@ -67,77 +137,85 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view) } } -static void get_colors(int flags, int *fg, int *bg) +static void get_colors(int flags, int *fg, int *bg, int *attr) { - if (flags & PRINTFLAG_MIRC_COLOR) { + if (flags & GUI_PRINT_FLAG_MIRC_COLOR) { /* mirc colors - real range is 0..15, but after 16 colors wrap to 0, 1, ... */ - *bg = *bg < 0 ? 0 : mirc_colors[*bg % 16]; - if (*fg > 0) *fg = mirc_colors[*fg % 16]; - } else { - /* default colors */ - *bg = *bg < 0 || *bg > 15 ? 0 : *bg; - if (*fg > 8) *fg &= ~8; + if (*bg >= 0) *bg = mirc_colors[*bg % 16]; + if (*fg >= 0) *fg = mirc_colors[*fg % 16]; } - if (*fg < 0 || *fg > 15) { - *fg = *bg == 0 ? current_theme->default_color : - current_theme->default_real_color; - } - - if (flags & PRINTFLAG_REVERSE) { - int tmp; - - tmp = *fg; *fg = *bg; *bg = tmp; - } + if (*fg < 0 || *fg > 15) + *fg = current_theme->default_color; + if (*bg < 0 || *bg > 15) + *bg = -1; - if (*fg == 8) *fg |= ATTR_COLOR8; - if (flags & PRINTFLAG_BOLD) { - if (*fg == 0) *fg = current_theme->default_real_color; - *fg |= 8; - } - if (flags & PRINTFLAG_UNDERLINE) *fg |= ATTR_UNDERLINE; - if (flags & PRINTFLAG_BLINK) *bg |= 0x08; + *attr = 0; + if (flags & GUI_PRINT_FLAG_REVERSE) *attr |= ATTR_REVERSE; + if (flags & GUI_PRINT_FLAG_BOLD) *attr |= ATTR_BOLD; + if (flags & GUI_PRINT_FLAG_UNDERLINE) *attr |= ATTR_UNDERLINE; + if (flags & GUI_PRINT_FLAG_BLINK) *attr |= ATTR_BLINK; } static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line, int fg, int bg, int flags) { - unsigned char data[12]; - int color, pos; + unsigned char data[20]; + int pos; - /* color should never have last bit on or it would be treated as a - command! */ - color = (fg & 0x0f) | ((bg & 0x07) << 4); - pos = 0; + /* get the fg & bg command chars */ + fg = fg < 0 ? LINE_COLOR_DEFAULT : fg & 0x0f; + bg = LINE_COLOR_BG | (bg < 0 ? LINE_COLOR_DEFAULT : bg & 0x0f); + if (flags & GUI_PRINT_FLAG_BOLD) + fg |= LINE_COLOR_BOLD; + if (flags & GUI_PRINT_FLAG_BLINK) + bg |= LINE_COLOR_BLINK; - if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != last_color) || - ((fg & ATTR_COLOR8) && (fg & 0xf0) != (last_color & 0xf0))) { + pos = 0; + if (fg != last_fg) { + last_fg = fg; data[pos++] = 0; - data[pos++] = color == 0 ? LINE_CMD_COLOR0 : color; + data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg; } - - if ((flags & PRINTFLAG_UNDERLINE) != (last_flags & PRINTFLAG_UNDERLINE)) { + if (bg != last_bg) { + last_bg = bg; data[pos++] = 0; - data[pos++] = LINE_CMD_UNDERLINE; + data[pos++] = bg; } - if (fg & ATTR_COLOR8) { + + if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (last_flags & GUI_PRINT_FLAG_UNDERLINE)) { data[pos++] = 0; - data[pos++] = LINE_CMD_COLOR8; + data[pos++] = LINE_CMD_UNDERLINE; } - if (bg & 0x08) { + if ((flags & GUI_PRINT_FLAG_REVERSE) != (last_flags & GUI_PRINT_FLAG_REVERSE)) { data[pos++] = 0; - data[pos++] = LINE_CMD_BLINK; + data[pos++] = LINE_CMD_REVERSE; } - if (flags & PRINTFLAG_INDENT) { + if (flags & GUI_PRINT_FLAG_INDENT) { data[pos++] = 0; data[pos++] = LINE_CMD_INDENT; } - *line = textbuffer_insert(buffer, *line, data, pos, NULL); + if (pos > 0) + *line = textbuffer_insert(buffer, *line, data, pos, NULL); last_flags = flags; - last_color = fg | (bg << 4); +} + +static void line_add_indent_func(TEXT_BUFFER_REC *buffer, LINE_REC **line, + const char *function) +{ + GSList *list; + unsigned char data[1+sizeof(INDENT_FUNC)]; + + list = g_hash_table_lookup(indent_functions, function); + if (list != NULL) { + data[0] = LINE_CMD_INDENT_FUNC; + memcpy(data+1, list->data, sizeof(INDENT_FUNC)); + *line = textbuffer_insert(buffer, *line, + data, sizeof(data), NULL); + } } static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line) @@ -152,22 +230,28 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, void *bgcolor, void *pflags, char *str, void *level) { + GUI_WINDOW_REC *gui; TEXT_BUFFER_VIEW_REC *view; LINE_REC *insert_after; LINE_INFO_REC lineinfo; - int fg, bg, flags; + int fg, bg, flags, attr; flags = GPOINTER_TO_INT(pflags); fg = GPOINTER_TO_INT(fgcolor); bg = GPOINTER_TO_INT(bgcolor); - get_colors(flags, &fg, &bg); + get_colors(flags, &fg, &bg, &attr); if (window == NULL) { g_return_if_fail(next_xpos != -1); - wmove(stdscr, next_ypos, next_xpos); - set_color(stdscr, fg | (bg << 4)); - addstr(str); + attr |= fg > 0 ? fg : ATTR_RESETFG; + attr |= bg > 0 ? (bg << 4) : ATTR_RESETBG; + term_set_color(root_window, attr); + + term_move(root_window, next_xpos, next_ypos); + if (flags & GUI_PRINT_FLAG_CLRTOEOL) + term_clrtoeol(root_window); + term_addstr(root_window, str); next_xpos += strlen(str); return; } @@ -175,23 +259,33 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, lineinfo.level = GPOINTER_TO_INT(level); lineinfo.time = time(NULL); - view = WINDOW_GUI(window)->view; - insert_after = WINDOW_GUI(window)->use_insert_after ? - WINDOW_GUI(window)->insert_after : view->buffer->cur_line; + gui = WINDOW_GUI(window); + view = gui->view; + insert_after = gui->use_insert_after ? + gui->insert_after : view->buffer->cur_line; - if (flags & PRINTFLAG_NEWLINE) + if (flags & GUI_PRINT_FLAG_NEWLINE) view_add_eol(view, &insert_after); line_add_colors(view->buffer, &insert_after, fg, bg, flags); - textbuffer_insert(view->buffer, insert_after, - str, strlen(str), &lineinfo); + + if (flags & GUI_PRINT_FLAG_INDENT_FUNC) { + /* specify the indentation function */ + line_add_indent_func(view->buffer, &insert_after, str); + } else { + insert_after = textbuffer_insert(view->buffer, insert_after, + (unsigned char *) str, + strlen(str), &lineinfo); + } + if (gui->use_insert_after) + gui->insert_after = insert_after; } -static void sig_printtext_finished(WINDOW_REC *window) +static void sig_gui_printtext_finished(WINDOW_REC *window) { TEXT_BUFFER_VIEW_REC *view; LINE_REC *insert_after; - last_color = 0; + last_fg = last_bg = -1; last_flags = 0; view = WINDOW_GUI(window)->view; @@ -202,74 +296,36 @@ static void sig_printtext_finished(WINDOW_REC *window) remove_old_lines(view); } -static void sig_print_format(THEME_REC *theme, const char *module, - TEXT_DEST_REC *dest, void *formatnump, - char **args) -{ - FORMAT_REC *formats; - int formatnum, n; - - if (!scrollback_save_formats) - return; - - formatnum = GPOINTER_TO_INT(formatnump); - formats = g_hash_table_lookup(default_formats, module); - - /* */ - 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); } diff --git a/apps/irssi/src/fe-text/gui-printtext.h b/apps/irssi/src/fe-text/gui-printtext.h index 3b2098b7..47cd3c23 100644 --- a/apps/irssi/src/fe-text/gui-printtext.h +++ b/apps/irssi/src/fe-text/gui-printtext.h @@ -2,12 +2,21 @@ #define __GUI_PRINTTEXT_H #include "gui-windows.h" +#include "textbuffer-view.h" +#include "formats.h" extern int mirc_colors[]; void gui_printtext_init(void); void gui_printtext_deinit(void); +void gui_register_indent_func(const char *name, INDENT_FUNC func); +void gui_unregister_indent_func(const char *name, INDENT_FUNC func); + +void gui_set_default_indent(const char *name); +INDENT_FUNC get_default_indent_func(void); + void gui_printtext(int xpos, int ypos, const char *str); +void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str); #endif diff --git a/apps/irssi/src/fe-text/gui-readline.c b/apps/irssi/src/fe-text/gui-readline.c index bc3429d8..8fe17410 100644 --- a/apps/irssi/src/fe-text/gui-readline.c +++ b/apps/irssi/src/fe-text/gui-readline.c @@ -23,13 +23,14 @@ #include "misc.h" #include "settings.h" #include "special-vars.h" +#include "servers.h" #include "completion.h" #include "command-history.h" #include "keyboard.h" #include "translation.h" -#include "screen.h" +#include "term.h" #include "gui-entry.h" #include "gui-windows.h" @@ -51,6 +52,25 @@ char *cutbuffer; static int readtag; static time_t idle_time; +static void sig_input(void); + +void input_listen_init(int handle) +{ + GIOChannel *stdin_channel; + + stdin_channel = g_io_channel_unix_new(handle); + readtag = g_input_add_full(stdin_channel, + G_PRIORITY_HIGH, G_INPUT_READ, + (GInputFunction) sig_input, NULL); + g_io_channel_unref(stdin_channel); +} + +void input_listen_deinit(void) +{ + g_source_remove(readtag); + readtag = -1; +} + static void handle_key_redirect(int key) { ENTRY_REDIRECT_KEY_FUNC func; @@ -63,8 +83,7 @@ static void handle_key_redirect(int key) if (func != NULL) func(key, data, active_win->active_server, active_win->active); - gui_entry_remove_perm_prompt(); - window_update_prompt(); + gui_entry_set_prompt(active_entry, ""); } static void handle_entry_redirect(const char *line) @@ -72,7 +91,7 @@ static void handle_entry_redirect(const char *line) ENTRY_REDIRECT_ENTRY_FUNC func; void *data; - gui_entry_set_hidden(FALSE); + gui_entry_set_hidden(active_entry, FALSE); func = (ENTRY_REDIRECT_ENTRY_FUNC) redir->func; data = redir->data; @@ -83,8 +102,7 @@ static void handle_entry_redirect(const char *line) active_win->active); } - gui_entry_remove_perm_prompt(); - window_update_prompt(); + gui_entry_set_prompt(active_entry, ""); } static int get_scroll_count(void) @@ -99,8 +117,9 @@ static int get_scroll_count(void) else if (count < 1) count = 1.0/count; - if (*str == '/') - count = WINDOW_GUI(active_win)->parent->height/count; + if (*str == '/') { + count = (active_mainwin->height-active_mainwin->statusbar_lines)/count; + } return (int)count; } @@ -141,36 +160,44 @@ void handle_key(int key) if (!key_pressed(keyboard, str)) { /* key wasn't used for anything, print it */ - gui_entry_insert_char((char) key); + gui_entry_insert_char(active_entry, (char) key); } } static void key_send_line(void) { - int add_history; - char *str; + HISTORY_REC *history; + char *str, *add_history; - str = gui_entry_get_text(); + str = gui_entry_get_text(active_entry); if (*str == '\0') return; + /* we can't use gui_entry_get_text() later, since the entry might + have been destroyed after we get back */ + add_history = g_strdup(str); + history = command_history_current(active_win); + translate_output(str); - add_history = TRUE; if (redir == NULL) { signal_emit("send command", 3, str, active_win->active_server, active_win->active); } else { if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN) - add_history = FALSE; + g_free_and_null(add_history); handle_entry_redirect(str); } - if (add_history) { - command_history_add(active_win, gui_entry_get_text(), - FALSE); + if (add_history != NULL) { + history = command_history_find(history); + if (history != NULL) + command_history_add(history, add_history); + g_free(add_history); } - gui_entry_set_text(""); + + if (active_entry != NULL) + gui_entry_set_text(active_entry, ""); command_history_clear_pos(active_win); } @@ -182,83 +209,93 @@ static void key_backward_history(void) { const char *text; - text = command_history_prev(active_win, gui_entry_get_text()); - gui_entry_set_text(text); + text = command_history_prev(active_win, gui_entry_get_text(active_entry)); + gui_entry_set_text(active_entry, text); } static void key_forward_history(void) { const char *text; - text = command_history_next(active_win, gui_entry_get_text()); - gui_entry_set_text(text); + text = command_history_next(active_win, gui_entry_get_text(active_entry)); + gui_entry_set_text(active_entry, text); } static void key_beginning_of_line(void) { - gui_entry_set_pos(0); + gui_entry_set_pos(active_entry, 0); } static void key_end_of_line(void) { - gui_entry_set_pos(strlen(gui_entry_get_text())); + gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry))); } static void key_backward_character(void) { - gui_entry_move_pos(-1); + gui_entry_move_pos(active_entry, -1); } static void key_forward_character(void) { - gui_entry_move_pos(1); + gui_entry_move_pos(active_entry, 1); } static void key_backward_word(void) { - gui_entry_move_words(-1); + gui_entry_move_words(active_entry, -1, FALSE); } static void key_forward_word(void) { - gui_entry_move_words(1); + gui_entry_move_words(active_entry, 1, FALSE); +} + +static void key_backward_to_space(void) +{ + gui_entry_move_words(active_entry, -1, TRUE); +} + +static void key_forward_to_space(void) +{ + gui_entry_move_words(active_entry, 1, TRUE); } static void key_erase_line(void) { g_free_not_null(cutbuffer); - cutbuffer = g_strdup(gui_entry_get_text()); + cutbuffer = g_strdup(gui_entry_get_text(active_entry)); - gui_entry_set_text(""); + gui_entry_set_text(active_entry, ""); } static void key_erase_to_beg_of_line(void) { int pos; - pos = gui_entry_get_pos(); + pos = gui_entry_get_pos(active_entry); g_free_not_null(cutbuffer); - cutbuffer = g_strndup(gui_entry_get_text(), pos); + cutbuffer = g_strndup(gui_entry_get_text(active_entry), pos); - gui_entry_erase(pos); + gui_entry_erase(active_entry, pos); } static void key_erase_to_end_of_line(void) { int pos; - pos = gui_entry_get_pos(); + pos = gui_entry_get_pos(active_entry); g_free_not_null(cutbuffer); - cutbuffer = g_strdup(gui_entry_get_text()+pos); + cutbuffer = g_strdup(gui_entry_get_text(active_entry)+pos); - gui_entry_set_pos(strlen(gui_entry_get_text())); - gui_entry_erase(strlen(gui_entry_get_text()) - pos); + gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry))); + gui_entry_erase(active_entry, strlen(gui_entry_get_text(active_entry)) - pos); } static void key_yank_from_cutbuffer(void) { if (cutbuffer != NULL) - gui_entry_insert_text(cutbuffer); + gui_entry_insert_text(active_entry, cutbuffer); } static void key_transpose_characters(void) @@ -266,61 +303,72 @@ static void key_transpose_characters(void) char *line, c; int pos; - pos = gui_entry_get_pos(); - line = gui_entry_get_text(); + pos = gui_entry_get_pos(active_entry); + line = gui_entry_get_text(active_entry); if (pos == 0 || strlen(line) < 2) return; if (line[pos] != '\0') - gui_entry_move_pos(1); - c = line[gui_entry_get_pos()-1]; - gui_entry_erase(1); - gui_entry_move_pos(-1); - gui_entry_insert_char(c); - gui_entry_set_pos(pos); + gui_entry_move_pos(active_entry, 1); + c = line[gui_entry_get_pos(active_entry)-1]; + gui_entry_erase(active_entry, 1); + gui_entry_move_pos(active_entry, -1); + gui_entry_insert_char(active_entry, c); + gui_entry_set_pos(active_entry, pos); + gui_entry_move_pos(active_entry, 1); } static void key_delete_character(void) { - if (gui_entry_get_pos() < (int)strlen(gui_entry_get_text())) { - gui_entry_move_pos(1); - gui_entry_erase(1); + if (gui_entry_get_pos(active_entry) < (int)strlen(gui_entry_get_text(active_entry))) { + gui_entry_move_pos(active_entry, 1); + gui_entry_erase(active_entry, 1); } } static void key_backspace(void) { - gui_entry_erase(1); + gui_entry_erase(active_entry, 1); } static void key_delete_previous_word(void) { - gui_entry_erase_word(); + gui_entry_erase_word(active_entry, FALSE); } static void key_delete_next_word(void) { - gui_entry_erase_next_word(); + gui_entry_erase_next_word(active_entry, FALSE); } static void key_delete_to_previous_space(void) { - gui_entry_erase_word(); + gui_entry_erase_word(active_entry, TRUE); +} + +static void key_delete_to_next_space(void) +{ + gui_entry_erase_next_word(active_entry, TRUE); } -void readline(void) +static void sig_input(void) { - int key; + unsigned char buffer[128]; + int ret, i; - for (;;) { - key = getch(); - if (key == ERR -#ifdef KEY_RESIZE - || key == KEY_RESIZE -#endif - ) break; + if (!active_entry) { + /* no active entry yet - wait until we have it */ + return; + } - handle_key(key); + ret = term_gets(buffer, sizeof(buffer)); + if (ret == -1) { + /* lost terminal */ + if (!term_detached) + signal_emit("command quit", 1, "Lost terminal"); + } else { + for (i = 0; i < ret; i++) + handle_key(buffer[i]); } } @@ -354,32 +402,43 @@ static void key_change_window(const char *data) signal_emit("command window goto", 3, data, active_win->active_server, active_win->active); } -static void key_word_completion(void) +static void key_completion(int erase) { char *line; int pos; - pos = gui_entry_get_pos(); + pos = gui_entry_get_pos(active_entry); - line = word_complete(active_win, gui_entry_get_text(), &pos); + line = word_complete(active_win, gui_entry_get_text(active_entry), + &pos, erase); if (line != NULL) { - gui_entry_set_text(line); - gui_entry_set_pos(pos); + gui_entry_set_text(active_entry, line); + gui_entry_set_pos(active_entry, pos); g_free(line); } } +static void key_word_completion(void) +{ + key_completion(FALSE); +} + +static void key_erase_completion(void) +{ + key_completion(TRUE); +} + static void key_check_replaces(void) { char *line; int pos; - pos = gui_entry_get_pos(); + pos = gui_entry_get_pos(active_entry); - line = auto_word_complete(gui_entry_get_text(), &pos); + line = auto_word_complete(gui_entry_get_text(active_entry), &pos); if (line != NULL) { - gui_entry_set_text(line); - gui_entry_set_pos(pos); + gui_entry_set_text(active_entry, line); + gui_entry_set_pos(active_entry, pos); g_free(line); } } @@ -467,14 +526,22 @@ static void key_insert_text(const char *data) str = parse_special_string(data, active_win->active_server, active_win->active, "", NULL, 0); - gui_entry_insert_text(str); + gui_entry_insert_text(active_entry, str); g_free(str); } +static void key_sig_stop(void) +{ + term_stop(); +} + static void sig_window_auto_changed(void) { - command_history_next(active_win, gui_entry_get_text()); - gui_entry_set_text(""); + if (active_entry == NULL) + return; + + command_history_next(active_win, gui_entry_get_text(active_entry)); + gui_entry_set_text(active_entry, ""); } static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry, @@ -486,8 +553,8 @@ static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry, redir->data = data; if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN) - gui_entry_set_hidden(TRUE); - gui_entry_set_perm_prompt(entry); + gui_entry_set_hidden(active_entry, TRUE); + gui_entry_set_prompt(active_entry, entry); } void gui_readline_init(void) @@ -499,17 +566,18 @@ void gui_readline_init(void) cutbuffer = NULL; redir = NULL; idle_time = time(NULL); - readtag = g_input_add_full(g_io_channel_unix_new(0), - G_PRIORITY_HIGH, G_INPUT_READ, - (GInputFunction) readline, NULL); + input_listen_init(STDIN_FILENO); settings_add_str("history", "scroll_page_count", "/2"); keyboard = keyboard_create(NULL); key_configure_freeze(); + key_bind("key", NULL, " ", "space", (SIGNAL_FUNC) key_combo); key_bind("key", NULL, "^M", "return", (SIGNAL_FUNC) key_combo); key_bind("key", NULL, "^J", "return", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "^H", "backspace", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "^?", "backspace", (SIGNAL_FUNC) key_combo); /* meta */ key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo); @@ -539,11 +607,18 @@ void gui_readline_init(void) key_bind("key", NULL, "meta2-2~", "insert", (SIGNAL_FUNC) key_combo); key_bind("key", NULL, "meta2-3~", "delete", (SIGNAL_FUNC) key_combo); - /* cursor movement */ + key_bind("key", NULL, "meta2-d", "cleft", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-c", "cright", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-5D", "cleft", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-5C", "cright", (SIGNAL_FUNC) key_combo); + + /* cursor movement */ key_bind("backward_character", "", "left", NULL, (SIGNAL_FUNC) key_backward_character); key_bind("forward_character", "", "right", NULL, (SIGNAL_FUNC) key_forward_character); - key_bind("backward_word", "", "meta2-d", NULL, (SIGNAL_FUNC) key_backward_word); - key_bind("forward_word", "", "meta2-c", NULL, (SIGNAL_FUNC) key_forward_word); + key_bind("backward_word", "", "cleft", NULL, (SIGNAL_FUNC) key_backward_word); + key_bind("forward_word", "", "cright", NULL, (SIGNAL_FUNC) key_forward_word); + key_bind("backward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_backward_to_space); + key_bind("forward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_forward_to_space); key_bind("beginning_of_line", "", "home", NULL, (SIGNAL_FUNC) key_beginning_of_line); key_bind("beginning_of_line", NULL, "^A", NULL, (SIGNAL_FUNC) key_beginning_of_line); key_bind("end_of_line", "", "end", NULL, (SIGNAL_FUNC) key_end_of_line); @@ -554,13 +629,13 @@ void gui_readline_init(void) key_bind("forward_history", "", "down", NULL, (SIGNAL_FUNC) key_forward_history); /* line editing */ - key_bind("backspace", "", "^H", NULL, (SIGNAL_FUNC) key_backspace); - key_bind("backspace", "", "^?", NULL, (SIGNAL_FUNC) key_backspace); + key_bind("backspace", "", "backspace", NULL, (SIGNAL_FUNC) key_backspace); key_bind("delete_character", "", "delete", NULL, (SIGNAL_FUNC) key_delete_character); key_bind("delete_character", NULL, "^D", NULL, (SIGNAL_FUNC) key_delete_character); key_bind("delete_next_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_next_word); - key_bind("delete_previous_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word); + key_bind("delete_previous_word", "meta-backspace", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word); key_bind("delete_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space); + key_bind("delete_to_next_space", "", "", NULL, (SIGNAL_FUNC) key_delete_to_next_space); key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line); key_bind("erase_to_beg_of_line", "", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line); key_bind("erase_to_end_of_line", "", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line); @@ -570,8 +645,8 @@ void gui_readline_init(void) /* line transmitting */ key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line); key_bind("word_completion", "", "^I", NULL, (SIGNAL_FUNC) key_word_completion); - key_bind("check_replaces", "Check word replaces", " ", NULL, (SIGNAL_FUNC) key_check_replaces); - key_bind("check_replaces", NULL, NULL, NULL, (SIGNAL_FUNC) key_check_replaces); + key_bind("erase_completion", "", "meta-k", NULL, (SIGNAL_FUNC) key_erase_completion); + key_bind("check_replaces", "Check word replaces", NULL, NULL, (SIGNAL_FUNC) key_check_replaces); /* window managing */ key_bind("previous_window", "Previous window", "^P", NULL, (SIGNAL_FUNC) key_previous_window); @@ -595,8 +670,11 @@ void gui_readline_init(void) /* inserting special input characters to line.. */ key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text); + /* autoreplaces */ key_bind("multi", NULL, "return", "check_replaces;send_line", NULL); + key_bind("multi", NULL, "space", "check_replaces;insert_text ", NULL); + /* moving between windows */ for (n = 0; changekeys[n] != '\0'; n++) { key = g_strdup_printf("meta-%c", changekeys[n]); ltoa(data, n+1); @@ -604,6 +682,9 @@ void gui_readline_init(void) g_free(key); } + /* misc */ + key_bind("stop_irc", "Send SIGSTOP to client", "^Z", NULL, (SIGNAL_FUNC) key_sig_stop); + key_configure_thaw(); signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed); @@ -613,7 +694,7 @@ void gui_readline_init(void) void gui_readline_deinit(void) { g_free_not_null(cutbuffer); - g_source_remove(readtag); + input_listen_deinit(); key_configure_freeze(); @@ -621,6 +702,8 @@ void gui_readline_deinit(void) key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character); key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word); key_unbind("forward_word", (SIGNAL_FUNC) key_forward_word); + key_unbind("backward_to_space", (SIGNAL_FUNC) key_backward_to_space); + key_unbind("forward_to_space", (SIGNAL_FUNC) key_forward_to_space); key_unbind("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line); key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line); @@ -631,6 +714,7 @@ void gui_readline_deinit(void) key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character); key_unbind("delete_next_word", (SIGNAL_FUNC) key_delete_next_word); key_unbind("delete_previous_word", (SIGNAL_FUNC) key_delete_previous_word); + key_unbind("delete_to_next_space", (SIGNAL_FUNC) key_delete_to_next_space); key_unbind("delete_to_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space); key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line); key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line); @@ -657,6 +741,7 @@ void gui_readline_deinit(void) key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text); key_unbind("change_window", (SIGNAL_FUNC) key_change_window); + key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop); keyboard_destroy(keyboard); key_configure_thaw(); diff --git a/apps/irssi/src/fe-text/gui-readline.h b/apps/irssi/src/fe-text/gui-readline.h index 6464921f..9ce3a742 100644 --- a/apps/irssi/src/fe-text/gui-readline.h +++ b/apps/irssi/src/fe-text/gui-readline.h @@ -3,6 +3,9 @@ extern char *cutbuffer; +void input_listen_init(int handle); +void input_listen_deinit(void); + void readline(void); time_t get_idle_time(void); diff --git a/apps/irssi/src/fe-text/gui-windows.c b/apps/irssi/src/fe-text/gui-windows.c index 5d7ad0d7..cd834ee4 100644 --- a/apps/irssi/src/fe-text/gui-windows.c +++ b/apps/irssi/src/fe-text/gui-windows.c @@ -24,29 +24,33 @@ #include "settings.h" #include "special-vars.h" -#include "screen.h" +#include "term.h" #include "gui-entry.h" #include "gui-windows.h" #include "gui-printtext.h" static int window_create_override; -static char *prompt, *prompt_window; - static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window, MAIN_WINDOW_REC *parent) { GUI_WINDOW_REC *gui; window->width = parent->width; - window->height = parent->height; + window->height = MAIN_WINDOW_TEXT_HEIGHT(parent); gui = g_new0(GUI_WINDOW_REC, 1); gui->parent = parent; gui->view = textbuffer_view_create(textbuffer_create(), window->width, window->height, + settings_get_bool("scroll"), + settings_get_bool("term_utf8")); + textbuffer_view_set_default_indent(gui->view, settings_get_int("indent"), - settings_get_bool("indent_always")); + !settings_get_bool("indent_always"), + get_default_indent_func()); + if (parent->active == window) + textbuffer_view_set_window(gui->view, parent->screen_win); return gui; } @@ -61,33 +65,37 @@ static void sig_window_create_override(gpointer tab) window_create_override = GPOINTER_TO_INT(tab); } -static void gui_window_created(WINDOW_REC *window) +static void gui_window_created(WINDOW_REC *window, void *automatic) { MAIN_WINDOW_REC *parent; + int empty_window, new_parent; g_return_if_fail(window != NULL); - parent = window_create_override != 0 && - active_win != NULL && WINDOW_GUI(active_win) != NULL ? - WINDOW_GUI(active_win)->parent : mainwindow_create(); + new_parent = window_create_override == 0 || + window_create_override == 2 || + active_win == NULL || WINDOW_GUI(active_win) == NULL; + parent = !new_parent ? WINDOW_MAIN(active_win) : mainwindow_create(); if (parent == NULL) { /* not enough space for new window, but we really can't abort creation of the window anymore, so create hidden window instead. */ - parent = WINDOW_GUI(active_win)->parent; + parent = WINDOW_MAIN(active_win); } window_create_override = -1; - if (settings_get_bool("autostick_split_windows") && - (parent->sticky_windows != NULL || - (mainwindows->next != NULL && parent->active == NULL))) { - /* set the window sticky */ - parent->sticky_windows = - g_slist_append(parent->sticky_windows, window); - } + empty_window = parent->active == NULL; if (parent->active == NULL) parent->active = window; window->gui_data = gui_window_init(window, parent); + + /* set only non-automatic windows sticky so that the windows + irssi creates at startup wont get sticky. */ + if (automatic == NULL && + (parent->sticky_windows || + (new_parent && settings_get_bool("autostick_split_windows")))) + gui_window_set_sticky(window); + signal_emit("gui window created", 1, window); } @@ -101,21 +109,29 @@ static void gui_window_destroyed(WINDOW_REC *window) gui = WINDOW_GUI(window); parent = gui->parent; + gui_window_set_unsticky(window); + signal_emit("gui window destroyed", 1, window); gui_window_deinit(gui); window->gui_data = NULL; - if (parent->active == window && mainwindows->next != NULL) - mainwindow_destroy(parent); + if (parent->active == window) + mainwindow_change_active(parent, window); } void gui_window_resize(WINDOW_REC *window, int width, int height) { GUI_WINDOW_REC *gui; + if (window->width == width && window->height == height) + return; + gui = WINDOW_GUI(window); + irssi_set_dirty(); + WINDOW_MAIN(window)->dirty = TRUE; + window->width = width; window->height = height; textbuffer_view_resize(gui->view, width, height); @@ -138,68 +154,66 @@ void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line) signal_emit("gui page scrolled", 1, window); } -void window_update_prompt(void) +void gui_window_set_sticky(WINDOW_REC *window) { - const char *special; - char *prompt, *text; - int var_used; - - special = settings_get_str(active_win->active != NULL ? - "prompt" : "prompt_window"); - if (*special == '\0') { - gui_entry_set_prompt(""); - return; - } + GUI_WINDOW_REC *gui = WINDOW_GUI(window); - prompt = parse_special_string(special, active_win->active_server, - active_win->active, "", &var_used, - PARSE_FLAG_ISSET_ANY | - PARSE_FLAG_ESCAPE_VARS); - if (!var_used && strchr(special, '$') != NULL) { - /* none of the $vars had non-empty values, use empty prompt */ - *prompt = '\0'; + if (!gui->sticky) { + gui->sticky = TRUE; + gui->parent->sticky_windows++; } - - /* set prompt */ - text = show_lowascii(prompt); - gui_entry_set_prompt(text); - g_free(text); - - g_free(prompt); -} - -static void window_update_prompt_server(SERVER_REC *server) -{ - if (server == active_win->active_server) - window_update_prompt(); } -static void window_update_prompt_window(WINDOW_REC *window) +void gui_window_set_unsticky(WINDOW_REC *window) { - if (window == active_win) - window_update_prompt(); -} + GUI_WINDOW_REC *gui = WINDOW_GUI(window); -static void window_update_prompt_window_item(WI_ITEM_REC *item) -{ - if (item == active_win->active) - window_update_prompt(); + if (gui->sticky) { + gui->sticky = FALSE; + gui->parent->sticky_windows--; + } } void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent) { MAIN_WINDOW_REC *oldparent; - oldparent = WINDOW_GUI(window)->parent; + oldparent = WINDOW_MAIN(window); if (oldparent == parent) return; + gui_window_set_unsticky(window); textbuffer_view_set_window(WINDOW_GUI(window)->view, NULL); - WINDOW_GUI(window)->parent = parent; - if (parent->height != oldparent->height || - parent->width != oldparent->width) - gui_window_resize(window, parent->width, parent->height); + WINDOW_MAIN(window) = parent; + if (parent->sticky_windows) + gui_window_set_sticky(window); + + if (MAIN_WINDOW_TEXT_HEIGHT(parent) != + MAIN_WINDOW_TEXT_HEIGHT(oldparent) || + parent->width != oldparent->width) { + gui_window_resize(window, parent->width, + MAIN_WINDOW_TEXT_HEIGHT(parent)); + } +} + +void gui_windows_reset_settings(void) +{ + GSList *tmp; + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + GUI_WINDOW_REC *gui = WINDOW_GUI(rec); + + textbuffer_view_set_default_indent(gui->view, + settings_get_int("indent"), + !settings_get_bool("indent_always"), + get_default_indent_func()); + + textbuffer_view_set_scroll(gui->view, + gui->use_scroll ? gui->scroll : + settings_get_bool("scroll")); + } } static MAIN_WINDOW_REC *mainwindow_find_unsticky(void) @@ -209,7 +223,7 @@ static MAIN_WINDOW_REC *mainwindow_find_unsticky(void) for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; - if (rec->sticky_windows == NULL) + if (!rec->sticky_windows) return rec; } @@ -220,26 +234,26 @@ static MAIN_WINDOW_REC *mainwindow_find_unsticky(void) static void signal_window_changed(WINDOW_REC *window) { MAIN_WINDOW_REC *parent; - WINDOW_REC *old_window; + WINDOW_REC *old_window; g_return_if_fail(window != NULL); if (quitting) return; - parent = WINDOW_GUI(window)->parent; + parent = WINDOW_MAIN(window); if (is_window_visible(window)) { /* already visible */ active_mainwin = parent; } else if (active_mainwin == NULL) { /* no main window set yet */ active_mainwin = parent; - } else if (g_slist_find(parent->sticky_windows, window) != NULL) { + } else if (WINDOW_GUI(window)->sticky) { /* window is sticky, switch to correct main window */ if (parent != active_mainwin) active_mainwin = parent; } else { /* move window to active main window */ - if (active_mainwin->sticky_windows != NULL) { + if (active_mainwin->sticky_windows) { /* active mainwindow is sticky, we'll need to set the window active somewhere else */ active_mainwin = mainwindow_find_unsticky(); @@ -248,54 +262,20 @@ static void signal_window_changed(WINDOW_REC *window) } old_window = active_mainwin->active; - if (old_window != NULL) - textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL); - active_mainwin->active = window; + if (old_window != NULL && old_window != window) + textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL); - textbuffer_view_set_window(WINDOW_GUI(window)->view, - parent->curses_win); - - window_update_prompt(); -} + active_mainwin->active = window; -static void sig_check_window_update(WINDOW_REC *window) -{ - if (window == active_win) - window_update_prompt(); + textbuffer_view_set_window(WINDOW_GUI(window)->view, + active_mainwin->screen_win); + if (WINDOW_GUI(window)->view->dirty) + active_mainwin->dirty = TRUE; } static void read_settings(void) { - GSList *tmp; - - SIGNAL_FUNC funcs[] = { - (SIGNAL_FUNC) window_update_prompt, - (SIGNAL_FUNC) window_update_prompt_server, - (SIGNAL_FUNC) window_update_prompt_window, - (SIGNAL_FUNC) window_update_prompt_window_item - }; - - if (prompt != NULL) { - special_vars_remove_signals(prompt, 4, funcs); - special_vars_remove_signals(prompt_window, 4, funcs); - g_free(prompt); - g_free(prompt_window); - } - prompt = g_strdup(settings_get_str("prompt")); - prompt_window = g_strdup(settings_get_str("prompt_window")); - - for (tmp = windows; tmp != NULL; tmp = tmp->next) { - WINDOW_REC *rec = tmp->data; - - textbuffer_view_set_default_indent(WINDOW_GUI(rec)->view, - settings_get_int("indent"), - settings_get_bool("indent_always")); - } - - special_vars_add_signals(prompt, 4, funcs); - special_vars_add_signals(prompt_window, 4, funcs); - - if (active_win != NULL) window_update_prompt(); + gui_windows_reset_settings(); } void gui_windows_init(void) @@ -303,10 +283,8 @@ void gui_windows_init(void) settings_add_bool("lookandfeel", "autostick_split_windows", TRUE); settings_add_int("lookandfeel", "indent", 10); settings_add_bool("lookandfeel", "indent_always", FALSE); - settings_add_str("lookandfeel", "prompt", "[$[.15]T] "); - settings_add_str("lookandfeel", "prompt_window", "[$winname] "); + settings_add_bool("lookandfeel", "scroll", TRUE); - prompt = NULL; prompt_window = NULL; window_create_override = -1; read_settings(); @@ -314,15 +292,11 @@ void gui_windows_init(void) signal_add("window created", (SIGNAL_FUNC) gui_window_created); signal_add("window destroyed", (SIGNAL_FUNC) gui_window_destroyed); signal_add_first("window changed", (SIGNAL_FUNC) signal_window_changed); - signal_add("window item remove", (SIGNAL_FUNC) sig_check_window_update); signal_add("setup changed", (SIGNAL_FUNC) read_settings); } void gui_windows_deinit(void) { - g_free_not_null(prompt); - g_free_not_null(prompt_window); - while (windows != NULL) window_destroy(windows->data); @@ -330,6 +304,5 @@ void gui_windows_deinit(void) signal_remove("window created", (SIGNAL_FUNC) gui_window_created); signal_remove("window destroyed", (SIGNAL_FUNC) gui_window_destroyed); signal_remove("window changed", (SIGNAL_FUNC) signal_window_changed); - signal_remove("window item remove", (SIGNAL_FUNC) sig_check_window_update); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); } diff --git a/apps/irssi/src/fe-text/gui-windows.h b/apps/irssi/src/fe-text/gui-windows.h index fb94b46f..9d4afac7 100644 --- a/apps/irssi/src/fe-text/gui-windows.h +++ b/apps/irssi/src/fe-text/gui-windows.h @@ -5,6 +5,7 @@ #include "textbuffer-view.h" #define WINDOW_GUI(a) ((GUI_WINDOW_REC *) ((a)->gui_data)) +#define WINDOW_MAIN(a) (WINDOW_GUI(a)->parent) #define is_window_visible(win) \ (WINDOW_GUI(win)->parent->active == (win)) @@ -13,6 +14,10 @@ typedef struct { MAIN_WINDOW_REC *parent; TEXT_BUFFER_VIEW_REC *view; + unsigned int scroll:1; + unsigned int use_scroll:1; + + unsigned int sticky:1; unsigned int use_insert_after:1; LINE_REC *insert_after; } GUI_WINDOW_REC; @@ -31,6 +36,9 @@ void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent); void gui_window_scroll(WINDOW_REC *window, int lines); void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line); -void window_update_prompt(void); +void gui_window_set_sticky(WINDOW_REC *window); +void gui_window_set_unsticky(WINDOW_REC *window); + +void gui_windows_reset_settings(void); #endif diff --git a/apps/irssi/src/fe-text/lastlog.c b/apps/irssi/src/fe-text/lastlog.c index 75e68da3..61fb5fc1 100644 --- a/apps/irssi/src/fe-text/lastlog.c +++ b/apps/irssi/src/fe-text/lastlog.c @@ -31,24 +31,28 @@ #include "gui-windows.h" #include "gui-printtext.h" +#define DEFAULT_LASTLOG_BEFORE 3 +#define DEFAULT_LASTLOG_AFTER 3 #define MAX_LINES_WITHOUT_FORCE 1000 static void window_lastlog_clear(WINDOW_REC *window) { - TEXT_BUFFER_VIEW_REC *view; - GList *tmp, *next; + TEXT_BUFFER_VIEW_REC *view; + LINE_REC *line, *next; - screen_refresh_freeze(); + term_refresh_freeze(); view = WINDOW_GUI(window)->view; - for (tmp = textbuffer_view_get_lines(view); tmp != NULL; tmp = next) { - LINE_REC *line = tmp->data; + line = textbuffer_view_get_lines(view); - next = tmp->next; - if (line->info.level & MSGLEVEL_LASTLOG) + while (line != NULL) { + next = line->next; + + if (line->info.level & MSGLEVEL_LASTLOG) textbuffer_view_remove_line(view, line); + line = next; } textbuffer_view_redraw(view); - screen_refresh_thaw(); + term_refresh_thaw(); } /* Only unknown keys in `optlist' should be levels. @@ -98,7 +102,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist, GList *list, *tmp; GString *line; char *str; - int level, len; + int level, before, after, len; level = cmd_options_get_level("lastlog", optlist); if (level == -1) return; /* error in options */ @@ -131,14 +135,26 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist, else startline = NULL; - if (startline == NULL) { - list = textbuffer_view_get_lines(WINDOW_GUI(window)->view); - startline = list == NULL ? NULL : list->data; + if (startline == NULL) + startline = textbuffer_view_get_lines(WINDOW_GUI(window)->view); + + str = g_hash_table_lookup(optlist, "#"); + if (str != NULL) { + before = after = atoi(str); + } else { + str = g_hash_table_lookup(optlist, "before"); + before = str == NULL ? 0 : *str != '\0' ? + atoi(str) : DEFAULT_LASTLOG_BEFORE; + + str = g_hash_table_lookup(optlist, "after"); + if (str == NULL) str = g_hash_table_lookup(optlist, "a"); + after = str == NULL ? 0 : *str != '\0' ? + atoi(str) : DEFAULT_LASTLOG_AFTER; } list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline, level, MSGLEVEL_LASTLOG, - searchtext, + searchtext, before, after, g_hash_table_lookup(optlist, "regexp") != NULL, g_hash_table_lookup(optlist, "word") != NULL, g_hash_table_lookup(optlist, "case") != NULL); @@ -147,15 +163,20 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist, if (count <= 0) tmp = list; else { - int pos = len-count; - + int pos = len-count-start; if (pos < 0) pos = 0; - pos += start; tmp = pos > len ? NULL : g_list_nth(list, pos); len = g_list_length(tmp); } + if (g_hash_table_lookup(optlist, "count") != NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_LASTLOG_COUNT, len); + g_list_free(list); + return; + } + if (len > MAX_LINES_WITHOUT_FORCE && fhandle == -1 && g_hash_table_lookup(optlist, "force") == NULL) { printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, @@ -171,6 +192,20 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist, while (tmp != NULL && (count < 0 || count > 0)) { LINE_REC *rec = tmp->data; + if (rec == NULL) { + if (tmp->next == NULL) + break; + if (fhandle != -1) { + write(fhandle, "--\n", 3); + } else { + printformat_window(active_win, + MSGLEVEL_LASTLOG, + TXT_LASTLOG_SEPARATOR); + } + tmp = tmp->next; + continue; + } + /* get the line text */ textbuffer_line2text(rec, fhandle == -1, line); if (!settings_get_bool("timestamps")) { @@ -207,9 +242,10 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist, g_list_free(list); } -/* SYNTAX: LASTLOG [-] [-file ] [-clear] [- -] - [-new | -away] [-regexp | -word] [-case] - [-window ] [] [ []] */ +/* SYNTAX: LASTLOG [-] [-file ] [-window ] [-new | -away] + [- -] [-clear] [-count] [-case] + [-regexp | -word] [-before [<#>]] [-after [<#>]] + [-<# before+after>] [] [ []] */ static void cmd_lastlog(const char *data) { GHashTable *optlist; @@ -219,13 +255,14 @@ static void cmd_lastlog(const char *data) g_return_if_fail(data != NULL); - if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS, - "lastlog", &optlist, &text, &countstr, &start)) + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_UNKNOWN_OPTIONS, "lastlog", &optlist, + &text, &countstr, &start)) return; - if (*start == '\0' && is_numeric(text, 0)) { - if (is_numeric(countstr, 0)) - start = countstr; + if (*start == '\0' && is_numeric(text, 0) && *text != '0' && + (*countstr == '\0' || is_numeric(countstr, 0))) { + start = countstr; countstr = text; text = ""; } @@ -258,7 +295,7 @@ void lastlog_init(void) { command_bind("lastlog", NULL, (SIGNAL_FUNC) cmd_lastlog); - command_set_options("lastlog", "!- force clear -file -window new away word regexp case"); + command_set_options("lastlog", "!- # force clear -file -window new away word regexp case count @a @after @before"); } void lastlog_deinit(void) diff --git a/apps/irssi/src/fe-text/mainwindows-layout.c b/apps/irssi/src/fe-text/mainwindows-layout.c new file mode 100644 index 00000000..072b9b2c --- /dev/null +++ b/apps/irssi/src/fe-text/mainwindows-layout.c @@ -0,0 +1,229 @@ +/* + mainwindows-layout.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "lib-config/iconfig.h" +#include "settings.h" + +#include "mainwindows.h" +#include "gui-windows.h" +#include "textbuffer-view.h" + +static void sig_layout_window_save(WINDOW_REC *window, CONFIG_NODE *node) +{ + WINDOW_REC *active; + GUI_WINDOW_REC *gui; + + gui = WINDOW_GUI(window); + if (gui->sticky) { + iconfig_node_set_bool(node, "sticky", TRUE); + active = gui->parent->active; + if (window != active) + iconfig_node_set_int(node, "parent", active->refnum); + } + + if (gui->use_scroll) + iconfig_node_set_bool(node, "scroll", gui->scroll); +} + +static void sig_layout_window_restore(WINDOW_REC *window, CONFIG_NODE *node) +{ + WINDOW_REC *parent; + GUI_WINDOW_REC *gui; + + gui = WINDOW_GUI(window); + + parent = window_find_refnum(config_node_get_int(node, "parent", -1)); + if (parent != NULL) + gui_window_reparent(window, WINDOW_MAIN(parent)); + + if (config_node_get_bool(node, "sticky", FALSE)) + gui_window_set_sticky(window); + if (config_node_get_str(node, "scroll", NULL) != NULL) { + gui->use_scroll = TRUE; + gui->scroll = config_node_get_bool(node, "scroll", TRUE); + textbuffer_view_set_scroll(gui->view, gui->scroll); + } +} + +static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node) +{ + char num[MAX_INT_STRLEN]; + + ltoa(num, window->active->refnum); + node = config_node_section(node, num, NODE_TYPE_BLOCK); + + iconfig_node_set_int(node, "first_line", window->first_line); + iconfig_node_set_int(node, "lines", window->height); +} + +static void sig_layout_save(void) +{ + CONFIG_NODE *node; + + iconfig_set_str(NULL, "mainwindows", NULL); + node = iconfig_node_traverse("mainwindows", TRUE); + + g_slist_foreach(mainwindows, (GFunc) main_window_save, node); +} + +static int window_node_cmp(CONFIG_NODE *n1, CONFIG_NODE *n2) +{ + return config_node_get_int(n1, "first_line", 0) > + config_node_get_int(n2, "first_line", 0) ? -1 : 1; +} + +/* Returns list of mainwindow nodes sorted by first_line + (lowest in screen first) */ +static GSList *get_sorted_windows_config(CONFIG_NODE *node) +{ + GSList *tmp, *output; + + output = NULL; + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + output = g_slist_insert_sorted(output, tmp->data, + (GCompareFunc) window_node_cmp); + } + + return output; +} + +static void sig_layout_restore(void) +{ + MAIN_WINDOW_REC *lower_window; + WINDOW_REC *window; + CONFIG_NODE *node; + GSList *tmp, *sorted_config; + int avail_height, height, *heights; + int i, lower_size, windows_count, diff; + + node = iconfig_node_traverse("mainwindows", FALSE); + if (node == NULL) return; + + sorted_config = get_sorted_windows_config(node); + windows_count = g_slist_length(sorted_config); + + /* calculate the saved terminal height */ + avail_height = term_height - + screen_reserved_top - screen_reserved_bottom; + height = 0; + heights = g_new0(int, windows_count); + for (i = 0, tmp = sorted_config; tmp != NULL; tmp = tmp->next, i++) { + CONFIG_NODE *node = tmp->data; + + heights[i] = config_node_get_int(node, "lines", 0); + height += heights[i]; + } + + if (avail_height <= (WINDOW_MIN_SIZE*2)+1) { + /* we can fit only one window to screen - + give it all the height we can */ + windows_count = 1; + heights[0] = avail_height; + } else if (height != avail_height) { + /* Terminal's height is different from the saved one. + Resize the windows so they fit to screen. */ + while (height > avail_height && + windows_count*(WINDOW_MIN_SIZE+1) > avail_height) { + /* all windows can't fit into screen, + remove the lowest ones */ + windows_count--; + } + + /* try to keep the windows' size about the same in percents */ + for (i = 0; i < windows_count; i++) { + int size = avail_height*heights[i]/height; + if (size < WINDOW_MIN_SIZE+1) + size = WINDOW_MIN_SIZE+1; + heights[i] = size; + } + + /* give/remove the last bits */ + height = 0; + for (i = 0; i < windows_count; i++) + height += heights[i]; + + diff = height < avail_height ? 1 : -1; + for (i = 0; height != avail_height; i++) { + if (i == windows_count) + i = 0; + + if (heights[i] > WINDOW_MIN_SIZE+1) { + height += diff; + heights[i] += diff; + } + } + } + + /* create all the visible windows with correct size */ + lower_window = NULL; lower_size = 0; + for (i = 0, tmp = sorted_config; i < windows_count; tmp = tmp->next, i++) { + CONFIG_NODE *node = tmp->data; + + /* create a new window + mainwindow */ + signal_emit("gui window create override", 1, + GINT_TO_POINTER(0)); + + window = window_create(NULL, TRUE); + window_set_refnum(window, atoi(node->key)); + + if (lower_size > 0) + mainwindow_set_size(lower_window, lower_size, FALSE); + + window_set_active(window); + active_mainwin = WINDOW_MAIN(window); + + lower_window = WINDOW_MAIN(window); + lower_size = heights[i]; + if (lower_size < WINDOW_MIN_SIZE+1) + lower_size = WINDOW_MIN_SIZE+1; + } + g_slist_free(sorted_config); + g_free(heights); + + if (lower_size > 0) + mainwindow_set_size(lower_window, lower_size, FALSE); +} + +static void sig_layout_reset(void) +{ + iconfig_set_str(NULL, "mainwindows", NULL); +} + +void mainwindows_layout_init(void) +{ + signal_add("layout save window", (SIGNAL_FUNC) sig_layout_window_save); + signal_add("layout restore window", (SIGNAL_FUNC) sig_layout_window_restore); + signal_add("layout save", (SIGNAL_FUNC) sig_layout_save); + signal_add_first("layout restore", (SIGNAL_FUNC) sig_layout_restore); + signal_add("layout reset", (SIGNAL_FUNC) sig_layout_reset); +} + +void mainwindows_layout_deinit(void) +{ + signal_remove("layout save window", (SIGNAL_FUNC) sig_layout_window_save); + signal_remove("layout restore window", (SIGNAL_FUNC) sig_layout_window_restore); + signal_remove("layout save", (SIGNAL_FUNC) sig_layout_save); + signal_remove("layout restore", (SIGNAL_FUNC) sig_layout_restore); + signal_remove("layout reset", (SIGNAL_FUNC) sig_layout_reset); +} diff --git a/apps/irssi/src/fe-text/mainwindows.c b/apps/irssi/src/fe-text/mainwindows.c index d727464e..7fcd9103 100644 --- a/apps/irssi/src/fe-text/mainwindows.c +++ b/apps/irssi/src/fe-text/mainwindows.c @@ -27,8 +27,7 @@ #include "settings.h" #include "printtext.h" -#include "screen.h" -#include "statusbar.h" +#include "term.h" #include "gui-windows.h" #define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1) @@ -36,8 +35,21 @@ GSList *mainwindows; MAIN_WINDOW_REC *active_mainwin; -static int reserved_up, reserved_down; -static int screen_width, screen_height; +int screen_reserved_top, screen_reserved_bottom; +static int old_screen_width, old_screen_height; + +#define mainwindow_create_screen(window) \ + term_window_create(0, \ + (window)->first_line + (window)->statusbar_lines_top, \ + (window)->width, \ + (window)->height - (window)->statusbar_lines) + +#define mainwindow_set_screen_size(window) \ + term_window_move((window)->screen_win, 0, \ + (window)->first_line + (window)->statusbar_lines_top, \ + (window)->width, \ + (window)->height - (window)->statusbar_lines); + static MAIN_WINDOW_REC *find_window_with_room(void) { @@ -49,7 +61,7 @@ static MAIN_WINDOW_REC *find_window_with_room(void) for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; - space = rec->height; + space = MAIN_WINDOW_TEXT_HEIGHT(rec); if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) { biggest = space; biggest_rec = rec; @@ -59,45 +71,100 @@ static MAIN_WINDOW_REC *find_window_with_room(void) return biggest_rec; } -#ifdef USE_CURSES_WINDOWS -static void create_curses_window(MAIN_WINDOW_REC *window) +#define window_size_equals(window, mainwin) \ + ((window)->width == (mainwin)->width && \ + (window)->height == MAIN_WINDOW_TEXT_HEIGHT(mainwin)) + +static void mainwindow_resize_windows(MAIN_WINDOW_REC *window) { - window->curses_win = newwin(window->height, window->width, - window->first_line, 0); - idlok(window->curses_win, 1); + GSList *tmp; + int resized; + + mainwindow_set_screen_size(window); + + resized = FALSE; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->gui_data != NULL && + WINDOW_GUI(rec)->parent == window && + !window_size_equals(rec, window)) { + resized = TRUE; + gui_window_resize(rec, window->width, + MAIN_WINDOW_TEXT_HEIGHT(window)); + } + } + + if (resized) + signal_emit("mainwindow resized", 1, window); } -#endif static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff) { - GSList *tmp; - - if (xdiff == 0 && ydiff == 0) + if (quitting || (xdiff == 0 && ydiff == 0)) return; window->width += xdiff; window->height = window->last_line-window->first_line+1; -#ifdef USE_CURSES_WINDOWS -#ifdef HAVE_CURSES_WRESIZE - wresize(window->curses_win, window->height, window->width); - mvwin(window->curses_win, window->first_line, 0); -#else - delwin(window->curses_win); - create_curses_window(window); -#endif -#endif + window->size_dirty = TRUE; +} + +static GSList *get_sticky_windows_sorted(MAIN_WINDOW_REC *mainwin) +{ + GSList *tmp, *list; + list = NULL; for (tmp = windows; tmp != NULL; tmp = tmp->next) { WINDOW_REC *rec = tmp->data; - if (rec->gui_data != NULL && - WINDOW_GUI(rec)->parent == window) - gui_window_resize(rec, window->width, window->height); + if (WINDOW_GUI(rec)->sticky && WINDOW_MAIN(rec) == mainwin) { + list = g_slist_insert_sorted(list, rec, (GCompareFunc) + window_refnum_cmp); + } } - textbuffer_view_set_window(WINDOW_GUI(window->active)->view, - window->curses_win); - signal_emit("mainwindow resized", 1, window); + return list; +} + +void mainwindow_change_active(MAIN_WINDOW_REC *mainwin, + WINDOW_REC *skip_window) +{ + WINDOW_REC *window, *other; + GSList *tmp; + + mainwin->active = NULL; + if (mainwin->sticky_windows) { + /* sticky window */ + tmp = get_sticky_windows_sorted(mainwin); + window = tmp->data; + if (window == skip_window) { + window = tmp->next == NULL ? NULL : + tmp->next->data; + } + g_slist_free(tmp); + + if (window != NULL) { + window_set_active(window); + return; + } + } + + other = NULL; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec != skip_window) { + if (WINDOW_MAIN(rec) == mainwin) { + window_set_active(rec); + return; + } + other = rec; + } + } + + /* no more non-sticky windows, remove main window */ + window_set_active(other); + mainwindow_destroy(mainwin); } void mainwindows_recreate(void) @@ -107,11 +174,10 @@ void mainwindows_recreate(void) for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; -#ifdef USE_CURSES_WINDOWS - create_curses_window(rec); -#endif + rec->screen_win = mainwindow_create_screen(rec); + rec->dirty = TRUE; textbuffer_view_set_window(WINDOW_GUI(rec->active)->view, - rec->curses_win); + rec->screen_win); } } @@ -121,37 +187,36 @@ MAIN_WINDOW_REC *mainwindow_create(void) int space; rec = g_new0(MAIN_WINDOW_REC, 1); - rec->width = screen_width; - rec->statusbar_lines = 1; + rec->dirty = TRUE; + rec->width = term_width; if (mainwindows == NULL) { active_mainwin = rec; - rec->first_line = reserved_up; - rec->last_line = screen_height-1 - - reserved_down-rec->statusbar_lines; + rec->first_line = screen_reserved_top; + rec->last_line = term_height-1 - screen_reserved_bottom; rec->height = rec->last_line-rec->first_line+1; } else { - parent = WINDOW_GUI(active_win)->parent; - if (parent->height < WINDOW_MIN_SIZE+NEW_WINDOW_SIZE) + parent = WINDOW_MAIN(active_win); + if (MAIN_WINDOW_TEXT_HEIGHT(parent) < + WINDOW_MIN_SIZE+NEW_WINDOW_SIZE) parent = find_window_with_room(); if (parent == NULL) return NULL; /* not enough space */ - space = (parent->height-parent->statusbar_lines)/2; + space = parent->height / 2; rec->first_line = parent->first_line; - rec->last_line = rec->first_line + space-rec->statusbar_lines; + rec->last_line = rec->first_line + space; rec->height = rec->last_line-rec->first_line+1; - parent->first_line = rec->last_line+1+rec->statusbar_lines; + + parent->first_line = rec->last_line+1; parent->height = parent->last_line-parent->first_line+1; mainwindow_resize(parent, 0, -space-1); } -#ifdef USE_CURSES_WINDOWS - rec->curses_win = newwin(rec->height, rec->width, rec->first_line, 0); - refresh(); -#endif + rec->screen_win = mainwindow_create_screen(rec); + term_refresh(NULL); mainwindows = g_slist_append(mainwindows, rec); signal_emit("mainwindow created", 1, rec); @@ -211,7 +276,7 @@ static void mainwindows_add_space(int first_line, int last_line) rec = mainwindows_find_upper(first_line); if (rec != NULL) { - rec->last_line = last_line-rec->statusbar_lines; + rec->last_line = last_line; mainwindow_resize(rec, 0, size); } } @@ -225,7 +290,7 @@ static void gui_windows_remove_parent(MAIN_WINDOW_REC *window) for (tmp = windows; tmp != NULL; tmp = tmp->next) { WINDOW_REC *rec = tmp->data; - if (rec->gui_data != NULL && WINDOW_GUI(rec)->parent == window) + if (rec->gui_data != NULL && WINDOW_MAIN(rec) == window) gui_window_reparent(rec, new_parent); } } @@ -234,20 +299,18 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window) { g_return_if_fail(window != NULL); -#ifdef USE_CURSES_WINDOWS - delwin(window->curses_win); -#endif - mainwindows = g_slist_remove(mainwindows, window); signal_emit("mainwindow destroyed", 1, window); + term_window_destroy(window->screen_win); + if (!quitting && mainwindows != NULL) { gui_windows_remove_parent(window); - mainwindows_add_space(window->first_line, window->last_line+window->statusbar_lines); + mainwindows_add_space(window->first_line, window->last_line); mainwindows_redraw(); - statusbar_redraw(NULL); } + g_free(window); if (active_mainwin == window) active_mainwin = NULL; @@ -257,13 +320,12 @@ void mainwindows_redraw(void) { GSList *tmp; - screen_refresh_freeze(); + irssi_set_dirty(); for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; - gui_window_redraw(rec->active); + rec->dirty = TRUE; } - screen_refresh_thaw(); } static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2) @@ -289,62 +351,46 @@ GSList *mainwindows_get_sorted(int reverse) return list; } -static void mainwindows_resize_too_small(int xdiff, int ydiff) -{ - GSList *sorted, *tmp; - int space, moved; - - /* terminal is too small - just take the space whereever possible */ - sorted = mainwindows_get_sorted(FALSE); - moved = 0; - for (tmp = sorted; tmp != NULL; tmp = tmp->next) { - MAIN_WINDOW_REC *rec = tmp->data; - - space = rec->height; - if (ydiff == 0 || space <= 0) { - if (moved > 0) { - rec->first_line -= moved; - rec->last_line -= moved; - signal_emit("mainwindow moved", 1, rec); - } - continue; - } - - if (space > -ydiff) space = -ydiff; - ydiff += space; - rec->first_line -= moved; - moved += space; - rec->last_line -= space; - mainwindow_resize(rec, xdiff, -space); - } - g_slist_free(sorted); -} - static void mainwindows_resize_smaller(int xdiff, int ydiff) { + MAIN_WINDOW_REC *rec; GSList *sorted, *tmp; int space; - space = 0; - for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { - MAIN_WINDOW_REC *rec = tmp->data; + for (;;) { + sorted = mainwindows_get_sorted(TRUE); + space = 0; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + rec = tmp->data; + space += MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE; + } - space += rec->height-WINDOW_MIN_SIZE; - } + if (space >= -ydiff) + break; - if (space < -ydiff) { - /* not enough space, use different algorithm */ - mainwindows_resize_too_small(xdiff, ydiff); - return; + rec = sorted->data; + sorted = g_slist_remove(sorted, rec); + + if (sorted != NULL) { + /* terminal is too small - destroy the + uppest window and try again */ + mainwindow_destroy(rec); + } else { + /* only one window in screen.. just force the resize */ + rec->last_line += ydiff; + mainwindow_resize(rec, xdiff, ydiff); + return; + } } /* resize windows that have space */ - sorted = mainwindows_get_sorted(TRUE); for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) { - MAIN_WINDOW_REC *rec = tmp->data; + rec = tmp->data; - space = rec->height-WINDOW_MIN_SIZE; + space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE; if (space <= 0) { + mainwindow_resize(rec, xdiff, 0); + rec->first_line += ydiff; rec->last_line += ydiff; signal_emit("mainwindow moved", 1, rec); @@ -359,6 +405,14 @@ static void mainwindows_resize_smaller(int xdiff, int ydiff) mainwindow_resize(rec, xdiff, -space); } + + if (xdiff != 0) { + while (tmp != NULL) { + mainwindow_resize(tmp->data, xdiff, 0); + tmp = tmp->next; + } + } + g_slist_free(sorted); } @@ -372,8 +426,9 @@ static void mainwindows_resize_bigger(int xdiff, int ydiff) for (tmp = sorted; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; - space = rec->height-WINDOW_MIN_SIZE; + space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE; if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) { + mainwindow_resize(rec, xdiff, 0); if (moved > 0) { rec->first_line += moved; rec->last_line += moved; @@ -415,12 +470,11 @@ void mainwindows_resize(int width, int height) { int xdiff, ydiff; - xdiff = width-screen_width; - ydiff = height-screen_height; - screen_width = width; - screen_height = height; + xdiff = width-old_screen_width; + ydiff = height-old_screen_height; + old_screen_width = width; + old_screen_height = height; - screen_refresh_freeze(); if (ydiff < 0) mainwindows_resize_smaller(xdiff, ydiff); else if (ydiff > 0) @@ -428,104 +482,197 @@ void mainwindows_resize(int width, int height) else if (xdiff != 0) mainwindows_resize_horiz(xdiff); + signal_emit("terminal resized", 0); + irssi_redraw(); - screen_refresh_thaw(); } -int mainwindows_reserve_lines(int count, int up) +int mainwindows_reserve_lines(int top, int bottom) { MAIN_WINDOW_REC *window; int ret; - if (up) { - g_return_val_if_fail(count > 0 || reserved_up > count, -1); + ret = -1; + if (top != 0) { + g_return_val_if_fail(top > 0 || screen_reserved_top > top, -1); - ret = reserved_up; - reserved_up += count; + ret = screen_reserved_top; + screen_reserved_top += top; window = mainwindows_find_lower(-1); - if (window != NULL) window->first_line += count; - } else { - g_return_val_if_fail(count > 0 || reserved_down > count, -1); + if (window != NULL) { + window->first_line += top; + mainwindow_resize(window, 0, -top); + } + } - ret = reserved_down; - reserved_down += count; + if (bottom != 0) { + g_return_val_if_fail(bottom > 0 || screen_reserved_bottom > bottom, -1); - window = mainwindows_find_upper(screen_height); - if (window != NULL) window->last_line -= count; - } + ret = screen_reserved_bottom; + screen_reserved_bottom += bottom; - if (window != NULL) - mainwindow_resize(window, 0, -count); + window = mainwindows_find_upper(term_height); + if (window != NULL) { + window->last_line -= bottom; + mainwindow_resize(window, 0, -bottom); + } + } return ret; } +int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window, + int top, int bottom) +{ + int ret; + + ret = -1; + if (top != 0) { + ret = window->statusbar_lines_top; + window->statusbar_lines_top += top; + window->statusbar_lines += top; + } + + if (bottom != 0) { + ret = window->statusbar_lines_bottom; + window->statusbar_lines_bottom += bottom; + window->statusbar_lines += bottom; + } + + if (top+bottom != 0) + window->size_dirty = TRUE; + + return ret; +} + static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win, MAIN_WINDOW_REC *shrink_win, int count) { + irssi_set_dirty(); + mainwindow_resize(grow_win, 0, count); mainwindow_resize(shrink_win, 0, -count); - gui_window_redraw(grow_win->active); - gui_window_redraw(shrink_win->active); - statusbar_redraw(grow_win->statusbar); - statusbar_redraw(shrink_win->statusbar); + grow_win->dirty = TRUE; + shrink_win->dirty = TRUE; } -static int mainwindow_grow(MAIN_WINDOW_REC *window, int count) +static int try_shrink_lower(MAIN_WINDOW_REC *window, int count) { MAIN_WINDOW_REC *shrink_win; - /* shrink lower window */ shrink_win = mainwindows_find_lower(window->last_line); - if (shrink_win != NULL && shrink_win->height-count >= WINDOW_MIN_SIZE) { + if (shrink_win != NULL && + MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) { window->last_line += count; shrink_win->first_line += count; - } else { - /* shrink upper window */ - shrink_win = mainwindows_find_upper(window->first_line); - if (shrink_win == NULL || - shrink_win->height-count < WINDOW_MIN_SIZE) - return FALSE; + mainwindows_resize_two(window, shrink_win, count); + return TRUE; + } + return FALSE; +} + +static int try_shrink_upper(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *shrink_win; + + shrink_win = mainwindows_find_upper(window->first_line); + if (shrink_win != NULL && + MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) { window->first_line -= count; shrink_win->last_line -= count; + mainwindows_resize_two(window, shrink_win, count); + return TRUE; + } + + return FALSE; +} + +static int mainwindow_grow(MAIN_WINDOW_REC *window, int count, + int resize_lower) +{ + if (!resize_lower || !try_shrink_lower(window, count)) { + if (!try_shrink_upper(window, count)) { + if (resize_lower || !try_shrink_lower(window, count)) + return FALSE; + } } - mainwindows_resize_two(window, shrink_win, count); return TRUE; } -static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count) +static int try_grow_lower(MAIN_WINDOW_REC *window, int count) { MAIN_WINDOW_REC *grow_win; - if (window->height-count < WINDOW_MIN_SIZE) - return FALSE; - grow_win = mainwindows_find_lower(window->last_line); if (grow_win != NULL) { window->last_line -= count; grow_win->first_line -= count; - } else { - grow_win = mainwindows_find_upper(window->first_line); - if (grow_win == NULL) return FALSE; + mainwindows_resize_two(grow_win, window, count); + } + return grow_win != NULL; +} + +static int try_grow_upper(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *grow_win; + + grow_win = mainwindows_find_upper(window->first_line); + if (grow_win != NULL) { window->first_line += count; grow_win->last_line += count; + mainwindows_resize_two(grow_win, window, count); + } + + return grow_win != NULL; +} + +static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count, int resize_lower) +{ + if (MAIN_WINDOW_TEXT_HEIGHT(window)-count < WINDOW_MIN_SIZE) + return FALSE; + + if (!resize_lower || !try_grow_lower(window, count)) { + if (!try_grow_upper(window, count)) { + if (resize_lower || !try_grow_lower(window, count)) + return FALSE; + } } - mainwindows_resize_two(grow_win, window, count); return TRUE; } -void mainwindow_set_size(MAIN_WINDOW_REC *window, int size) +/* Change the window height - the height includes the lines needed for + statusbars. If resize_lower is TRUE, the lower window is first tried + to be resized instead of upper window. */ +void mainwindow_set_size(MAIN_WINDOW_REC *window, int height, int resize_lower) { - size -= window->height; - if (size < 0) - mainwindow_shrink(window, size); + height -= window->height; + if (height < 0) + mainwindow_shrink(window, -height, resize_lower); else - mainwindow_grow(window, size); + mainwindow_grow(window, height, resize_lower); +} + +void mainwindows_redraw_dirty(void) +{ + GSList *tmp; + + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + if (rec->size_dirty) { + rec->size_dirty = FALSE; + mainwindow_resize_windows(rec); + } + if (rec->dirty) { + rec->dirty = FALSE; + gui_window_redraw(rec->active); + } + } } /* SYNTAX: WINDOW GROW [] */ @@ -535,9 +682,9 @@ static void cmd_window_grow(const char *data) int count; count = *data == '\0' ? 1 : atoi(data); - window = WINDOW_GUI(active_win)->parent; + window = WINDOW_MAIN(active_win); - if (!mainwindow_grow(window, count)) { + if (!mainwindow_grow(window, count, FALSE)) { printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL); } @@ -546,13 +693,10 @@ static void cmd_window_grow(const char *data) /* SYNTAX: WINDOW SHRINK [] */ static void cmd_window_shrink(const char *data) { - MAIN_WINDOW_REC *window; int count; count = *data == '\0' ? 1 : atoi(data); - window = WINDOW_GUI(active_win)->parent; - - if (!mainwindow_shrink(window, count)) { + if (!mainwindow_shrink(WINDOW_MAIN(active_win), count, FALSE)) { printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL); } @@ -567,7 +711,8 @@ static void cmd_window_size(const char *data) if (!is_numeric(data, 0)) return; size = atoi(data); - size -= WINDOW_GUI(active_win)->parent->height; + size -= WINDOW_MAIN(active_win)->height - + WINDOW_MAIN(active_win)->statusbar_lines; if (size == 0) return; ltoa(sizestr, size < 0 ? -size : size); @@ -587,33 +732,32 @@ static void cmd_window_balance(void) windows = g_slist_length(mainwindows); if (windows == 1) return; - avail_size = screen_height - reserved_up-reserved_down; + avail_size = term_height - screen_reserved_top-screen_reserved_bottom; unit_size = avail_size/windows; bigger_units = avail_size%windows; sorted = mainwindows_get_sorted(FALSE); - last_line = 0; + last_line = screen_reserved_top; for (tmp = sorted; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; old_size = rec->height; - rec->first_line = last_line+1; - rec->last_line = rec->first_line-1 + unit_size - - rec->statusbar_lines; - rec->height = rec->last_line-rec->first_line+1; + rec->first_line = last_line; + rec->last_line = rec->first_line + unit_size-1; if (bigger_units > 0) { rec->last_line++; bigger_units--; } - last_line = rec->last_line + rec->statusbar_lines; + + rec->height = rec->last_line-rec->first_line+1; + last_line = rec->last_line+1; mainwindow_resize(rec, 0, rec->height-old_size); } g_slist_free(sorted); mainwindows_redraw(); - statusbar_redraw(NULL); } /* SYNTAX: WINDOW HIDE [|] */ @@ -642,16 +786,16 @@ static void cmd_window_hide(const char *data) if (window == NULL || !is_window_visible(window)) return; - if (WINDOW_GUI(window)->parent->sticky_windows != NULL) { + if (WINDOW_MAIN(window)->sticky_windows) { printformat_window(active_win, MSGLEVEL_CLIENTERROR, TXT_CANT_HIDE_STICKY_WINDOWS); return; } - mainwindow_destroy(WINDOW_GUI(window)->parent); + mainwindow_destroy(WINDOW_MAIN(window)); if (active_mainwin == NULL) { - active_mainwin = WINDOW_GUI(active_win)->parent; + active_mainwin = WINDOW_MAIN(active_win); window_set_active(active_mainwin->active); } } @@ -677,20 +821,19 @@ static void cmd_window_show(const char *data) if (window == NULL || is_window_visible(window)) return; - if (WINDOW_GUI(window)->parent->sticky_windows != NULL) { + if (WINDOW_MAIN(window)->sticky_windows) { printformat_window(active_win, MSGLEVEL_CLIENTERROR, TXT_CANT_SHOW_STICKY_WINDOWS); return; } parent = mainwindow_create(); - if (settings_get_bool("autostick_split_windows")) { - parent->sticky_windows = - g_slist_append(parent->sticky_windows, window); - } - parent->active = window; + parent->active = window; gui_window_reparent(window, parent); + if (settings_get_bool("autostick_split_windows")) + gui_window_set_sticky(window); + active_mainwin = NULL; window_set_active(window); } @@ -701,6 +844,8 @@ static void cmd_window_up(void) MAIN_WINDOW_REC *rec; rec = mainwindows_find_upper(active_mainwin->first_line); + if (rec == NULL) + rec = mainwindows_find_upper(term_height); if (rec != NULL) window_set_active(rec->active); } @@ -711,171 +856,220 @@ static void cmd_window_down(void) MAIN_WINDOW_REC *rec; rec = mainwindows_find_lower(active_mainwin->last_line); + if (rec == NULL) + rec = mainwindows_find_lower(-1); if (rec != NULL) window_set_active(rec->active); } +#define WINDOW_STICKY_MATCH(window, sticky_parent) \ + ((!WINDOW_GUI(window)->sticky && (sticky_parent) == NULL) || \ + (WINDOW_GUI(window)->sticky && \ + WINDOW_MAIN(window) == (sticky_parent))) + +static int window_refnum_left(int refnum, int wrap) +{ + MAIN_WINDOW_REC *find_sticky; + WINDOW_REC *window; + + window = window_find_refnum(refnum); + g_return_val_if_fail(window != NULL, -1); + + find_sticky = WINDOW_MAIN(window)->sticky_windows ? + WINDOW_MAIN(window) : NULL; + + do { + refnum = window_refnum_prev(refnum, wrap); + if (refnum < 0) + break; + + window = window_find_refnum(refnum); + } while (!WINDOW_STICKY_MATCH(window, find_sticky)); + + return refnum; +} + +static int window_refnum_right(int refnum, int wrap) +{ + MAIN_WINDOW_REC *find_sticky; + WINDOW_REC *window; + + window = window_find_refnum(refnum); + g_return_val_if_fail(window != NULL, -1); + + find_sticky = WINDOW_MAIN(window)->sticky_windows ? + WINDOW_MAIN(window) : NULL; + + do { + refnum = window_refnum_next(refnum, wrap); + if (refnum < 0) + break; + + window = window_find_refnum(refnum); + } while (!WINDOW_STICKY_MATCH(window, find_sticky)); + + return refnum; +} + /* SYNTAX: WINDOW LEFT */ static void cmd_window_left(const char *data, SERVER_REC *server, void *item) { - MAIN_WINDOW_REC *parent; - WINDOW_REC *window; - int pos, num; - - window = NULL; - if (active_mainwin->sticky_windows == NULL) { - /* no sticky windows, go to previous non-sticky window */ - num = active_win->refnum; - do { - num = window_refnum_prev(num, TRUE); - if (num < 0) { - window = NULL; - break; - } - window = window_find_refnum(num); - parent = WINDOW_GUI(window)->parent; - } while (g_slist_find(parent->sticky_windows, window) != NULL); - } else { - pos = g_slist_index(active_mainwin->sticky_windows, - active_win); - if (pos > 0) { - window = g_slist_nth_data( - active_mainwin->sticky_windows, pos-1); - } else { - window = g_slist_last( - active_mainwin->sticky_windows)->data; - } - } + int refnum; - if (window != NULL) - window_set_active(window); + refnum = window_refnum_left(active_win->refnum, TRUE); + if (refnum != -1) + window_set_active(window_find_refnum(refnum)); } /* SYNTAX: WINDOW RIGHT */ static void cmd_window_right(void) { - MAIN_WINDOW_REC *parent; - WINDOW_REC *window; - GSList *tmp; - int num; - - window = NULL; - if (active_mainwin->sticky_windows == NULL) { - /* no sticky windows, go to next non-sticky window */ - num = active_win->refnum; - do { - num = window_refnum_next(num, TRUE); - if (num < 0) { - window = NULL; - break; - } - window = window_find_refnum(num); - parent = WINDOW_GUI(window)->parent; - } while (g_slist_find(parent->sticky_windows, window) != NULL); - } else { - tmp = g_slist_find(active_mainwin->sticky_windows, active_win); - if (tmp != NULL) { - window = tmp->next != NULL ? tmp->next->data : - active_mainwin->sticky_windows->data; - } - } + int refnum; - if (window != NULL) - window_set_active(window); + refnum = window_refnum_right(active_win->refnum, TRUE); + if (refnum != -1) + window_set_active(window_find_refnum(refnum)); } -static void mainwindow_change_window(MAIN_WINDOW_REC *mainwin, - WINDOW_REC *window) +static void window_reparent(WINDOW_REC *win, MAIN_WINDOW_REC *mainwin) { - MAIN_WINDOW_REC *parent; - GSList *tmp; + MAIN_WINDOW_REC *old_mainwin; - if (mainwin->sticky_windows != NULL) { - /* sticky window */ - window_set_active(mainwin->sticky_windows->data); - return; - } + old_mainwin = WINDOW_MAIN(win); - for (tmp = windows; tmp != NULL; tmp = tmp->next) { - WINDOW_REC *rec = tmp->data; + if (old_mainwin != mainwin) { + gui_window_set_unsticky(win); - parent = WINDOW_GUI(rec)->parent; - if (rec != window && - g_slist_find(parent->sticky_windows, rec) == NULL) { - window_set_active(rec); - return; + if (old_mainwin->active == win) { + mainwindow_change_active(old_mainwin, win); + if (active_mainwin == NULL) { + active_mainwin = mainwin; + window_set_active(mainwin->active); + } } - } - /* no more non-sticky windows, remove main window */ - mainwindow_destroy(mainwin); + gui_window_reparent(win, mainwin); + window_set_active(win); + } } -/* SYNTAX: WINDOW STICK [ON|OFF|] */ +/* SYNTAX: WINDOW STICK [] [ON|OFF] */ static void cmd_window_stick(const char *data) { - MAIN_WINDOW_REC *window = active_mainwin; + MAIN_WINDOW_REC *mainwin; + WINDOW_REC *win; - if (is_numeric(data, '\0')) { - WINDOW_REC *win = window_find_refnum(atoi(data)); + mainwin = active_mainwin; + win = active_mainwin->active; + + if (is_numeric(data, ' ')) { + /* ref# specified */ + win = window_find_refnum(atoi(data)); if (win == NULL) { printformat_window(active_win, MSGLEVEL_CLIENTERROR, TXT_REFNUM_NOT_FOUND, data); return; } - window = WINDOW_GUI(win)->parent; + + while (*data != ' ' && *data != '\0') data++; + while (*data == ' ') data++; } - if (g_strncasecmp(data, "OF", 2) == 0 || toupper(*data) == 'N') { + if (g_strncasecmp(data, "OF", 2) == 0 || i_toupper(*data) == 'N') { /* unset sticky */ - if (g_slist_find(window->sticky_windows, active_win) == NULL) { - printformat_window(active_win, MSGLEVEL_CLIENTERROR, + if (!WINDOW_GUI(win)->sticky) { + printformat_window(win, MSGLEVEL_CLIENTERROR, TXT_WINDOW_NOT_STICKY); } else { - window->sticky_windows = - g_slist_remove(window->sticky_windows, - active_win); - printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + gui_window_set_unsticky(win); + printformat_window(win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_UNSET_STICKY); } } else { - /* set sticky */ - active_mainwin->sticky_windows = - g_slist_remove(active_mainwin->sticky_windows, - active_win); - - if (g_slist_find(window->sticky_windows, active_win) == NULL) { - window->sticky_windows = - g_slist_append(window->sticky_windows, - active_win); - } - if (window != active_mainwin) { - WINDOW_REC *movewin; - - movewin = active_win; - gui_window_reparent(movewin, window); - mainwindow_change_window(active_mainwin, movewin); - - active_mainwin = window; - window_set_active(movewin); - } + /* set sticky */ + window_reparent(win, mainwin); + gui_window_set_sticky(win); printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_SET_STICKY); } } +/* SYNTAX: WINDOW MOVE LEFT */ +static void cmd_window_move_left(void) +{ + int refnum; + + refnum = window_refnum_left(active_win->refnum, TRUE); + if (refnum != -1) + window_set_refnum(active_win, refnum); +} + +/* SYNTAX: WINDOW MOVE RIGHT */ +static void cmd_window_move_right(void) +{ + int refnum; + + refnum = window_refnum_right(active_win->refnum, TRUE); + if (refnum != -1) + window_set_refnum(active_win, refnum); +} + +/* SYNTAX: WINDOW MOVE UP */ +static void cmd_window_move_up(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_upper(active_mainwin->first_line); + if (rec != NULL) + window_reparent(active_win, rec); +} + +/* SYNTAX: WINDOW MOVE DOWN */ +static void cmd_window_move_down(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_lower(active_mainwin->last_line); + if (rec != NULL) + window_reparent(active_win, rec); +} + +static void windows_print_sticky(MAIN_WINDOW_REC *win) +{ + GSList *tmp, *list; + GString *str; + + /* convert to string */ + str = g_string_new(NULL); + list = get_sticky_windows_sorted(win); + for (tmp = list; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + g_string_sprintfa(str, "#%d, ", rec->refnum); + } + g_string_truncate(str, str->len-2); + g_slist_free(list); + + printformat_window(win->active, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_STICKY, str->str); + g_string_free(str, TRUE); +} + +static void sig_window_print_info(WINDOW_REC *win) +{ + if (WINDOW_MAIN(win)->sticky_windows) + windows_print_sticky(WINDOW_MAIN(win)); +} + void mainwindows_init(void) { - screen_width = COLS; - screen_height = LINES; + old_screen_width = term_width; + old_screen_height = term_height; mainwindows = NULL; active_mainwin = NULL; - reserved_up = reserved_down = 0; - - /* for entry line */ - mainwindows_reserve_lines(1, FALSE); + screen_reserved_top = screen_reserved_bottom = 0; command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow); command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink); @@ -888,6 +1082,11 @@ void mainwindows_init(void) command_bind("window left", NULL, (SIGNAL_FUNC) cmd_window_left); command_bind("window right", NULL, (SIGNAL_FUNC) cmd_window_right); command_bind("window stick", NULL, (SIGNAL_FUNC) cmd_window_stick); + command_bind("window move left", NULL, (SIGNAL_FUNC) cmd_window_move_left); + command_bind("window move right", NULL, (SIGNAL_FUNC) cmd_window_move_right); + command_bind("window move up", NULL, (SIGNAL_FUNC) cmd_window_move_up); + command_bind("window move down", NULL, (SIGNAL_FUNC) cmd_window_move_down); + signal_add("window print info", (SIGNAL_FUNC) sig_window_print_info); } void mainwindows_deinit(void) @@ -906,4 +1105,9 @@ void mainwindows_deinit(void) command_unbind("window left", (SIGNAL_FUNC) cmd_window_left); command_unbind("window right", (SIGNAL_FUNC) cmd_window_right); command_unbind("window stick", (SIGNAL_FUNC) cmd_window_stick); + command_unbind("window move left", (SIGNAL_FUNC) cmd_window_move_left); + command_unbind("window move right", (SIGNAL_FUNC) cmd_window_move_right); + command_unbind("window move up", (SIGNAL_FUNC) cmd_window_move_up); + command_unbind("window move down", (SIGNAL_FUNC) cmd_window_move_down); + signal_remove("window print info", (SIGNAL_FUNC) sig_window_print_info); } diff --git a/apps/irssi/src/fe-text/mainwindows.h b/apps/irssi/src/fe-text/mainwindows.h index 491449c6..1bca333d 100644 --- a/apps/irssi/src/fe-text/mainwindows.h +++ b/apps/irssi/src/fe-text/mainwindows.h @@ -2,27 +2,34 @@ #define __MAINWINDOWS_H #include "fe-windows.h" -#include "screen.h" +#include "term.h" #define WINDOW_MIN_SIZE 2 +#define MAIN_WINDOW_TEXT_HEIGHT(window) \ + ((window)->height-(window)->statusbar_lines) + typedef struct { WINDOW_REC *active; - GSList *sticky_windows; /* list of windows allowed to show only in this mainwindow */ -#ifdef USE_CURSES_WINDOWS - WINDOW *curses_win; -#else -#error disable-curses-windows is currently broken /* FIXME */ -#endif - int first_line, last_line, width, height; - int statusbar_lines; - void *statusbar; - void *statusbar_window_item; + TERM_WINDOW *screen_win; + int sticky_windows; /* number of sticky windows */ + + int first_line, last_line; /* first/last line used by this window (0..x) (includes statusbars) */ + int width, height; /* width/height of the window (includes statusbars) */ + + GSList *statusbars; + int statusbar_lines_top; + int statusbar_lines_bottom; + int statusbar_lines; /* top+bottom */ + + unsigned int dirty:1; /* This window needs a redraw */ + unsigned int size_dirty:1; /* We'll need to resize the window, but haven't got around doing it just yet. */ } MAIN_WINDOW_REC; extern GSList *mainwindows; extern MAIN_WINDOW_REC *active_mainwin; +extern int screen_reserved_top, screen_reserved_bottom; void mainwindows_init(void); void mainwindows_deinit(void); @@ -33,10 +40,21 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window); void mainwindows_redraw(void); void mainwindows_recreate(void); -void mainwindow_set_size(MAIN_WINDOW_REC *window, int size); +/* Change the window height - the height includes the lines needed for + statusbars. If resize_lower is TRUE, the lower window is first tried + to be resized instead of upper window. */ +void mainwindow_set_size(MAIN_WINDOW_REC *window, int height, + int resize_lower); void mainwindows_resize(int width, int height); -int mainwindows_reserve_lines(int count, int up); +void mainwindow_change_active(MAIN_WINDOW_REC *mainwin, + WINDOW_REC *skip_window); + +int mainwindows_reserve_lines(int top, int bottom); +int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window, + int top, int bottom); +void mainwindows_redraw_dirty(void); + GSList *mainwindows_get_sorted(int reverse); #endif diff --git a/apps/irssi/src/fe-text/module-formats.c b/apps/irssi/src/fe-text/module-formats.c index be3c0eab..4a37db25 100644 --- a/apps/irssi/src/fe-text/module-formats.c +++ b/apps/irssi/src/fe-text/module-formats.c @@ -26,8 +26,10 @@ FORMAT_REC gui_text_formats[] = { MODULE_NAME, "Text user interface", 0 }, { "lastlog_too_long", "/LASTLOG would print $0 lines. If you really want to print all these lines use -force option.", 1, { 1 } }, + { "lastlog_count", "{hilight Lastlog}: $0 lines", 1, { 1 } }, { "lastlog_start", "{hilight Lastlog}:", 0 }, { "lastlog_end", "{hilight End of Lastlog}", 0 }, + { "lastlog_separator", "--", 0 }, { "refnum_not_found", "Window number $0 not found", 1, { 0 } }, { "window_too_small", "Not enough room to resize this window", 0 }, @@ -37,6 +39,9 @@ FORMAT_REC gui_text_formats[] = { "window_not_sticky", "Window is not sticky", 0 }, { "window_set_sticky", "Window set sticky", 0 }, { "window_unset_sticky", "Window is not sticky anymore", 0 }, + { "window_info_sticky", "Sticky : $0", 1, { 0 } }, + { "window_scroll", "Window scroll mode is now $0", 1, { 0 } }, + { "window_scroll_unknown", "Unknown scroll mode $0, must be ON, OFF or DEFAULT", 1, { 0 } }, { NULL, NULL, 0 } }; diff --git a/apps/irssi/src/fe-text/module-formats.h b/apps/irssi/src/fe-text/module-formats.h index fe33c8e2..2bbf4007 100644 --- a/apps/irssi/src/fe-text/module-formats.h +++ b/apps/irssi/src/fe-text/module-formats.h @@ -4,8 +4,10 @@ enum { TXT_MODULE_NAME, TXT_LASTLOG_TOO_LONG, + TXT_LASTLOG_COUNT, TXT_LASTLOG_START, TXT_LASTLOG_END, + TXT_LASTLOG_SEPARATOR, TXT_REFNUM_NOT_FOUND, TXT_WINDOW_TOO_SMALL, @@ -14,7 +16,10 @@ enum { TXT_CANT_SHOW_STICKY_WINDOWS, TXT_WINDOW_NOT_STICKY, TXT_WINDOW_SET_STICKY, - TXT_WINDOW_UNSET_STICKY + TXT_WINDOW_UNSET_STICKY, + TXT_WINDOW_INFO_STICKY, + TXT_WINDOW_SCROLL, + TXT_WINDOW_SCROLL_UNKNOWN }; extern FORMAT_REC gui_text_formats[]; diff --git a/apps/irssi/src/fe-text/module.h b/apps/irssi/src/fe-text/module.h index ba5888ad..c6643545 100644 --- a/apps/irssi/src/fe-text/module.h +++ b/apps/irssi/src/fe-text/module.h @@ -4,3 +4,4 @@ extern int quitting; void irssi_redraw(void); +void irssi_set_dirty(void); diff --git a/apps/irssi/src/fe-text/silc.c b/apps/irssi/src/fe-text/silc.c index 32ec6bd2..7e296dcc 100644 --- a/apps/irssi/src/fe-text/silc.c +++ b/apps/irssi/src/fe-text/silc.c @@ -20,24 +20,27 @@ #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 @@ -64,8 +67,13 @@ void lastlog_deinit(void); void mainwindow_activity_init(void); void mainwindow_activity_deinit(void); -void mainwindows_save_init(void); -void mainwindows_save_deinit(void); +void mainwindows_layout_init(void); +void mainwindows_layout_deinit(void); + +void term_dummy_init(void); +void term_dummy_deinit(void); + +static int dirty, full_redraw, dummy; static GMainLoop *main_loop; int quitting; @@ -89,25 +97,51 @@ static void sig_exit(void) /* redraw irssi's screen.. */ void irssi_redraw(void) { - clear(); - refresh(); - - /* windows */ - mainwindows_redraw(); - /* statusbar */ - statusbar_redraw(NULL); - /* entry line */ - gui_entry_redraw(); + dirty = TRUE; + full_redraw = TRUE; +} + +void irssi_set_dirty(void) +{ + dirty = TRUE; +} + +static void dirty_check(void) +{ + if (!dirty || dummy) + return; + + term_resize_dirty(); + + if (full_redraw) { + full_redraw = FALSE; + + /* first clear the screen so curses will be + forced to redraw the screen */ + term_clear(); + term_refresh(NULL); + + mainwindows_redraw(); + statusbar_redraw(NULL, TRUE); + } + + mainwindows_redraw_dirty(); + statusbar_redraw_dirty(); + term_refresh(NULL); + + dirty = FALSE; } static void textui_init(void) { - static struct poptOption options[] = { - POPT_AUTOHELP - { NULL, '\0', 0, NULL } - }; +#ifdef SIGTRAP + struct sigaction act; - args_register(options); + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigaction(SIGTRAP, &act, NULL); +#endif irssi_gui = IRSSI_GUI_TEXT; core_init(); @@ -116,36 +150,44 @@ static void textui_init(void) fe_common_silc_init(); theme_register(gui_text_formats); - signal_add("gui exit", (SIGNAL_FUNC) sig_exit); + signal_add_last("gui exit", (SIGNAL_FUNC) sig_exit); } static void textui_finish_init(void) { quitting = FALSE; - screen_refresh_freeze(); - textbuffer_init(); - textbuffer_view_init(); - textbuffer_commands_init(); - gui_entry_init(); - gui_expandos_init(); - gui_printtext_init(); - gui_readline_init(); - lastlog_init(); - mainwindows_init(); - mainwindow_activity_init(); - mainwindows_save_init(); - gui_windows_init(); - statusbar_init(); + if (dummy) + term_dummy_init(); + else { + term_refresh_freeze(); + textbuffer_init(); + textbuffer_view_init(); + textbuffer_commands_init(); + textbuffer_reformat_init(); + gui_expandos_init(); + gui_printtext_init(); + gui_readline_init(); + lastlog_init(); + mainwindows_init(); + mainwindow_activity_init(); + mainwindows_layout_init(); + gui_windows_init(); + statusbar_init(); + term_refresh_thaw(); + } settings_check(); - - fe_common_core_finish_init(); + module_register("core", "fe-text"); #ifdef HAVE_STATIC_PERL perl_core_init(); - fe_perl_init(); + fe_perl_init(); #endif + + dirty_check(); + + fe_common_core_finish_init(); signal_emit("irssi init finished", 0); #if 0 @@ -154,42 +196,45 @@ static void textui_finish_init(void) "%s", firsttimer_text); } #endif - - screen_refresh_thaw(); } static void textui_deinit(void) { signal(SIGINT, SIG_DFL); - screen_refresh_freeze(); + term_refresh_freeze(); while (modules != NULL) module_unload(modules->data); - signal_remove("gui exit", (SIGNAL_FUNC) sig_exit); - - lastlog_deinit(); - statusbar_deinit(); - gui_printtext_deinit(); - gui_readline_deinit(); - gui_windows_deinit(); - mainwindows_save_deinit(); - mainwindow_activity_deinit(); - mainwindows_deinit(); - gui_expandos_deinit(); - gui_entry_deinit(); - textbuffer_commands_deinit(); - textbuffer_view_deinit(); - textbuffer_deinit(); - - screen_refresh_thaw(); - deinit_screen(); - #ifdef HAVE_STATIC_PERL - fe_perl_deinit(); perl_core_deinit(); + fe_perl_deinit(); #endif + dirty_check(); /* one last time to print any quit messages */ + signal_remove("gui exit", (SIGNAL_FUNC) sig_exit); + + if (dummy) + term_dummy_deinit(); + else { + lastlog_deinit(); + statusbar_deinit(); + gui_printtext_deinit(); + gui_readline_deinit(); + gui_windows_deinit(); + mainwindows_layout_deinit(); + mainwindow_activity_deinit(); + mainwindows_deinit(); + gui_expandos_deinit(); + textbuffer_reformat_deinit(); + textbuffer_commands_deinit(); + textbuffer_view_deinit(); + textbuffer_deinit(); + + term_refresh_thaw(); + term_deinit(); + } + theme_unregister(); fe_common_silc_deinit(); @@ -205,7 +250,7 @@ static void check_oldcrap(void) int found; /* check that default.theme is up-to-date */ - path = g_strdup_printf("%s/.silc/default.theme", g_get_home_dir()); + path = g_strdup_printf("%s/default.theme", get_irssi_dir()); f = fopen(path, "r+"); if (f == NULL) { g_free(path); @@ -221,7 +266,7 @@ static void check_oldcrap(void) return; } - printf("\nYou seem to have old default.theme in ~/.silc/ directory.\n"); + printf("\nYou seem to have old default.theme in "IRSSI_DIR_SHORT"/ directory.\n"); printf("Themeing system has changed a bit since last irssi release,\n"); printf("you should either delete your old default.theme or manually\n"); printf("merge it with the new default.theme.\n\n"); @@ -229,7 +274,7 @@ static void check_oldcrap(void) str[0] = '\0'; fgets(str, sizeof(str), stdin); - if (toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0') + if (i_toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0') remove(path); g_free(path); } @@ -237,16 +282,13 @@ static void check_oldcrap(void) static void check_files(void) { struct stat statbuf; - char *path; - path = g_strdup_printf("%s/.silc", g_get_home_dir()); - if (stat(path, &statbuf) != 0) { + if (stat(get_irssi_dir(), &statbuf) != 0) { /* ~/.irssi doesn't exist, first time running irssi */ display_firsttimer = TRUE; } else { check_oldcrap(); } - g_free(path); } #ifdef WIN32 @@ -266,7 +308,17 @@ static void winsock_init(void) int main(int argc, char **argv) { + static struct poptOption options[] = { + { "dummy", 'd', POPT_ARG_NONE, &dummy, 0, "Use the dummy terminal mode", NULL }, + { NULL, '\0', 0, NULL } + }; + + dummy = FALSE; + quitting = FALSE; + core_init_paths(argc, argv); + check_files(); + #ifdef WIN32 winsock_init(); #endif @@ -279,29 +331,33 @@ int main(int argc, char **argv) textdomain(PACKAGE); #endif - quitting = FALSE; - textui_init(); + args_register(options); args_execute(argc, argv); + +#if 0 silc_init_finish(); +#endif - if (!init_screen()) - g_error("Can't initialize screen handling, quitting.\n"); + if (!dummy && !term_init()) { + fprintf(stderr, "Can't initialize screen handling, quitting.\n"); + fprintf(stderr, "You can still use the dummy mode with -d parameter\n"); + return 1; + } textui_finish_init(); main_loop = g_main_new(TRUE); + /* Does the same as g_main_run(main_loop), except we + can call our dirty-checker after each iteration */ while (!quitting) { g_main_iteration(TRUE); - screen_check_resizes(); + dirty_check(); } g_main_destroy(main_loop); textui_deinit(); -#ifdef MEM_DEBUG - ig_mem_profile(); -#endif - + session_upgrade(); /* if we /UPGRADEd, start the new process */ return 0; } diff --git a/apps/irssi/src/fe-text/statusbar-config.c b/apps/irssi/src/fe-text/statusbar-config.c new file mode 100644 index 00000000..f9ea05f2 --- /dev/null +++ b/apps/irssi/src/fe-text/statusbar-config.c @@ -0,0 +1,261 @@ +/* + statusbar-config.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "settings.h" +#include "lib-config/iconfig.h" + +#include "statusbar.h" + +static void read_statusbar_config_from_node(CONFIG_NODE *node); + +static STATUSBAR_CONFIG_REC * +statusbar_config_create(STATUSBAR_GROUP_REC *group, const char *name) +{ + STATUSBAR_CONFIG_REC *bar; + + g_return_val_if_fail(group != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + bar = g_new0(STATUSBAR_CONFIG_REC, 1); + group->config_bars = g_slist_append(group->config_bars, bar); + + bar->name = g_strdup(name); + return bar; +} + +static SBAR_ITEM_CONFIG_REC * +statusbar_item_config_create(STATUSBAR_CONFIG_REC *bar, const char *name, + int priority, int right_alignment) +{ + SBAR_ITEM_CONFIG_REC *rec; + + g_return_val_if_fail(bar != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + rec = g_new0(SBAR_ITEM_CONFIG_REC, 1); + bar->items = g_slist_append(bar->items, rec); + + rec->name = g_strdup(name); + rec->priority = priority; + rec->right_alignment = right_alignment; + + return rec; +} + +static void statusbar_config_item_destroy(STATUSBAR_CONFIG_REC *barconfig, + SBAR_ITEM_CONFIG_REC *itemconfig) +{ + barconfig->items = g_slist_remove(barconfig->items, itemconfig); + + g_free(itemconfig->name); + g_free(itemconfig); +} + +void statusbar_config_destroy(STATUSBAR_GROUP_REC *group, + STATUSBAR_CONFIG_REC *config) +{ + group->config_bars = g_slist_remove(group->config_bars, config); + + while (config->items != NULL) + statusbar_config_item_destroy(config, config->items->data); + + g_free(config->name); + g_free(config); +} + +static STATUSBAR_CONFIG_REC * +statusbar_config_find(STATUSBAR_GROUP_REC *group, const char *name) +{ + GSList *tmp; + + for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_CONFIG_REC *config = tmp->data; + + if (strcmp(config->name, name) == 0) + return config; + } + + return NULL; +} + +static void statusbar_reset_defaults(void) +{ + CONFIG_REC *config; + CONFIG_NODE *node; + + while (statusbar_groups != NULL) + statusbar_group_destroy(statusbar_groups->data); + active_statusbar_group = NULL; + + /* read the default statusbar settings from internal config */ + config = config_open(NULL, -1); + config_parse_data(config, default_config, "internal"); + node = config_node_traverse(config, "statusbar", FALSE); + if (node != NULL) + read_statusbar_config_from_node(node); + config_close(config); +} + +static void statusbar_read_items(CONFIG_NODE *items) +{ + GSList *tmp; + + tmp = config_node_first(items->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + CONFIG_NODE *node = tmp->data; + + statusbar_item_register(node->key, node->value, NULL); + } +} + +static void statusbar_read_item(STATUSBAR_CONFIG_REC *bar, CONFIG_NODE *node) +{ + int priority, right_alignment; + + priority = config_node_get_int(node, "priority", 0); + right_alignment = strcmp(config_node_get_str(node, "alignment", ""), "right") == 0; + statusbar_item_config_create(bar, node->key, + priority, right_alignment); +} + +static void statusbar_read(STATUSBAR_GROUP_REC *group, CONFIG_NODE *node) +{ + STATUSBAR_CONFIG_REC *bar; + GSList *tmp; + int visible; + const char *visible_str; + + bar = statusbar_config_find(group, node->key); + visible = bar == NULL ? STATUSBAR_VISIBLE_ALWAYS : bar->visible; + + /* first make sure we want to see this statusbar */ + visible_str = config_node_get_str(node, "visible", ""); + if (strcmp(visible_str, "active") == 0) + visible = STATUSBAR_VISIBLE_ACTIVE; + else if (strcmp(visible_str, "inactive") == 0) + visible = STATUSBAR_VISIBLE_INACTIVE; + else if (strcmp(visible_str, "never") == 0) { + /* we don't want this statusbar - + destroy it if it already exists */ + if (bar != NULL) + statusbar_config_destroy(group, bar); + return; + } + + if (bar == NULL) { + bar = statusbar_config_create(group, node->key); + bar->type = STATUSBAR_TYPE_ROOT; + bar->placement = STATUSBAR_BOTTOM; + bar->position = 0; + } + bar->visible = visible; + + if (strcmp(config_node_get_str(node, "type", ""), "window") == 0) + bar->type = STATUSBAR_TYPE_WINDOW; + if (strcmp(config_node_get_str(node, "placement", ""), "top") == 0) + bar->placement = STATUSBAR_TOP; + bar->position = config_node_get_int(node, "position", 0); + + node = config_node_section(node, "items", -1); + if (node != NULL) { + /* we're overriding the items - destroy the old */ + while (bar->items != NULL) + statusbar_config_item_destroy(bar, bar->items->data); + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) + statusbar_read_item(bar, tmp->data); + } +} + +static void statusbar_read_group(CONFIG_NODE *node) +{ + STATUSBAR_GROUP_REC *group; + GSList *tmp; + + group = statusbar_group_find(node->key); + if (group == NULL) { + group = statusbar_group_create(node->key); + if (active_statusbar_group == NULL) + active_statusbar_group = group; + } + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) + statusbar_read(group, tmp->data); +} + +static void create_root_statusbars(void) +{ + STATUSBAR_REC *bar; + GSList *tmp; + + tmp = active_statusbar_group->config_bars; + for (; tmp != NULL; tmp = tmp->next) { + STATUSBAR_CONFIG_REC *rec = tmp->data; + + if (rec->type == STATUSBAR_TYPE_ROOT) { + bar = statusbar_create(active_statusbar_group, rec, NULL); + statusbar_redraw(bar, TRUE); + } + } +} + +static void read_statusbar_config_from_node(CONFIG_NODE *node) +{ + CONFIG_NODE *items; + GSList *tmp; + + items = config_node_section(node, "items", -1); + if (items != NULL) + statusbar_read_items(items); + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + if (tmp->data != items) + statusbar_read_group(tmp->data); + } +} + +static void read_statusbar_config(void) +{ + CONFIG_NODE *node; + + statusbar_reset_defaults(); + + node = iconfig_node_traverse("statusbar", FALSE); + if (node != NULL) + read_statusbar_config_from_node(node); + + create_root_statusbars(); +} + +void statusbar_config_init(void) +{ + read_statusbar_config(); + signal_add("setup reread", (SIGNAL_FUNC) read_statusbar_config); +} + +void statusbar_config_deinit(void) +{ + signal_remove("setup reread", (SIGNAL_FUNC) read_statusbar_config); +} diff --git a/apps/irssi/src/fe-text/statusbar-config.h b/apps/irssi/src/fe-text/statusbar-config.h new file mode 100644 index 00000000..a2a0b049 --- /dev/null +++ b/apps/irssi/src/fe-text/statusbar-config.h @@ -0,0 +1,12 @@ +#ifndef __STATUSBAR_CONFIG_H +#define __STATUSBAR_CONFIG_H + +#include "statusbar.h" + +void statusbar_config_destroy(STATUSBAR_GROUP_REC *group, + STATUSBAR_CONFIG_REC *config); + +void statusbar_config_init(void); +void statusbar_config_deinit(void); + +#endif diff --git a/apps/irssi/src/fe-text/statusbar-items.c b/apps/irssi/src/fe-text/statusbar-items.c index 137e7417..ac98c77a 100644 --- a/apps/irssi/src/fe-text/statusbar-items.c +++ b/apps/irssi/src/fe-text/statusbar-items.c @@ -20,193 +20,68 @@ #include "module.h" #include "signals.h" -#include "misc.h" #include "settings.h" -#include "special-vars.h" - -#include "window-items.h" -#include "formats.h" +#include "servers.h" +#include "themes.h" #include "statusbar.h" -#include "gui-printtext.h" +#include "gui-entry.h" +#include "gui-windows.h" /* how often to redraw lagging time (seconds) */ #define LAG_REFRESH_TIME 10 -/* how often to check for new mail (seconds) */ -#define MAIL_REFRESH_TIME 60 - -/* If we haven't been able to check lag for this long, "(??)" is added after - the lag */ -#define MAX_LAG_UNKNOWN_TIME 30 - -static STATUSBAR_REC *mainbar; -static MAIN_WINDOW_REC *mainbar_window; -static int use_colors; - -/* clock */ -static SBAR_ITEM_REC *clock_item; -static int clock_timetag; -static time_t clock_last; - -/* nick */ -static SBAR_ITEM_REC *nick_item; - -/* channel */ -static SBAR_ITEM_REC *window_item; - -/* activity */ -static SBAR_ITEM_REC *activity_item; static GList *activity_list; +static GSList *more_visible; /* list of MAIN_WINDOW_RECs which have --more-- */ +static GHashTable *input_entries; +static int last_lag, last_lag_unknown, lag_timeout_tag; -/* more */ -static SBAR_ITEM_REC *more_item; - -/* lag */ -static SBAR_ITEM_REC *lag_item; -static int lag_timetag, lag_min_show; -static time_t lag_last_draw; - -/* mbox counter */ -static SBAR_ITEM_REC *mail_item; -static int mail_timetag, mail_last_count; -static time_t mail_last_mtime = -1; -static off_t mail_last_size = -1; - -/* topic */ -static SBAR_ITEM_REC *topic_item; -static STATUSBAR_REC *topic_bar; - -static void item_default(SBAR_ITEM_REC *item, int get_size_only, - const char *str, const char *data) -{ - SERVER_REC *server; - WI_ITEM_REC *wiitem; - char *tmpstr, *tmpstr2; - int len; - - if (active_win == NULL) { - server = NULL; - wiitem = NULL; - } else { - server = active_win->active_server; - wiitem = active_win->active; - } - - /* expand $variables */ - tmpstr = parse_special_string(str, server, wiitem, data, NULL, - PARSE_FLAG_ESCAPE_VARS); - - /* expand templates */ - str = tmpstr; - tmpstr2 = theme_format_expand_data(current_theme, &str, - 'n', '0' + item->bar->color, - NULL, NULL, - EXPAND_FLAG_ROOT | - EXPAND_FLAG_IGNORE_REPLACES | - EXPAND_FLAG_IGNORE_EMPTY); - g_free(tmpstr); - - /* remove color codes */ - tmpstr = strip_codes(tmpstr2); - g_free(tmpstr2); - - if (get_size_only) { - item->min_size = item->max_size = format_get_length(tmpstr); - } else { - if (item->size < item->min_size) { - /* they're forcing us smaller than minimum size.. */ - len = format_real_length(tmpstr, item->size); - tmpstr[len] = '\0'; - } - - tmpstr2 = g_strconcat(item->bar->color_string, tmpstr, NULL); - gui_printtext(item->xpos, item->bar->ypos, tmpstr2); - g_free(tmpstr2); - } - g_free(tmpstr); -} - -/* redraw clock */ -static void statusbar_clock(SBAR_ITEM_REC *item, int get_size_only) +static void item_window_active(SBAR_ITEM_REC *item, int get_size_only) { - item_default(item, get_size_only, "{sb $Z}", ""); -} - -/* check if we need to redraw clock.. */ -static int statusbar_clock_timeout(void) -{ - struct tm *tm; - time_t t; - int min; - - tm = localtime(&clock_last); - min = tm->tm_min; + WINDOW_REC *window; - t = time(NULL); - tm = localtime(&t); + window = active_win; + if (item->bar->parent_window != NULL) + window = item->bar->parent_window->active; - if (tm->tm_min != min) { - /* minute changed, redraw! */ - clock_last = t; - statusbar_item_redraw(clock_item); + if (window != NULL && window->active != NULL) { + statusbar_item_default_handler(item, get_size_only, + NULL, "", TRUE); + } else if (get_size_only) { + item->min_size = item->max_size = 0; } - return 1; } -/* redraw nick */ -static void statusbar_nick(SBAR_ITEM_REC *item, int get_size_only) +static void item_window_empty(SBAR_ITEM_REC *item, int get_size_only) { - item_default(item, get_size_only, - "{sb $P$N{sbmode $usermode}{sbaway $A}}", ""); -} + WINDOW_REC *window; -static void sig_statusbar_nick_redraw(void) -{ - statusbar_item_redraw(nick_item); -} + window = active_win; + if (item->bar->parent_window != NULL) + window = item->bar->parent_window->active; -/* redraw window */ -static void statusbar_window(SBAR_ITEM_REC *item, int get_size_only) -{ - if (active_win->active != NULL) { - item_default(item, get_size_only, - "{sb $winref:$T{sbmode $M}}", ""); - } else { - item_default(item, get_size_only, - "{sb $winref{sbservertag $tag}}", ""); + if (window != NULL && window->active == NULL) { + statusbar_item_default_handler(item, get_size_only, + NULL, "", TRUE); + } else if (get_size_only) { + item->min_size = item->max_size = 0; } } -static void sig_statusbar_window_redraw(void) -{ - statusbar_item_redraw(window_item); -} - -static void sig_statusbar_window_redraw_window(WINDOW_REC *window) -{ - if (is_window_visible(window)) - statusbar_item_redraw(window_item); -} - -static void sig_statusbar_window_redraw_window_item(WI_ITEM_REC *item) -{ - WINDOW_REC *window; - - window = window_item_window(item); - if (window->active == item && is_window_visible(window)) - statusbar_item_redraw(window_item); -} - -static char *get_activity_list(int normal, int hilight) +static char *get_activity_list(MAIN_WINDOW_REC *window, int normal, int hilight) { + THEME_REC *theme; GString *str; GList *tmp; - char *ret; + char *ret, *name, *format, *value; int is_det; str = g_string_new(NULL); + theme = window != NULL && window->active != NULL && + window->active->theme != NULL ? + window->active->theme : current_theme; + for (tmp = activity_list; tmp != NULL; tmp = tmp->next) { WINDOW_REC *window = tmp->data; @@ -214,26 +89,41 @@ static char *get_activity_list(int normal, int hilight) if ((!is_det && !normal) || (is_det && !hilight)) continue; - g_string_append(str, "%c"); - if (str->len > 2) - g_string_append_c(str, ','); + /* comma separator */ + if (str->len > 0) { + value = theme_format_expand(theme, "{sb_act_sep ,}"); + g_string_append(str, value); + g_free(value); + } switch (window->data_level) { case DATA_LEVEL_NONE: case DATA_LEVEL_TEXT: + name = "{sb_act_text %d}"; break; case DATA_LEVEL_MSG: - g_string_append(str, "%W"); + name = "{sb_act_msg %d}"; break; default: - g_string_append(str, window->hilight_color == NULL ? - "%M" : window->hilight_color); + if (window->hilight_color == NULL) + name = "{sb_act_hilight %d}"; + else + name = NULL; break; } - g_string_sprintfa(str, "%d", window->refnum); - /* make sure the background is returned to default */ - g_string_append(str, "%n"); + if (name != NULL) + format = g_strdup_printf(name, window->refnum); + else + format = g_strdup_printf("{sb_act_hilight_color %s %d}", + window->hilight_color, + window->refnum); + + value = theme_format_expand(theme, format); + g_string_append(str, value); + g_free(value); + + g_free(format); } ret = str->len == 0 ? NULL : str->str; @@ -244,31 +134,21 @@ static char *get_activity_list(int normal, int hilight) /* redraw activity, FIXME: if we didn't get enough size, this gets buggy. At least "Det:" isn't printed properly. also we should rearrange the act list so that the highest priority items comes first. */ -static void statusbar_activity(SBAR_ITEM_REC *item, int get_size_only) +static void item_act(SBAR_ITEM_REC *item, int get_size_only) { - char *actlist, *detlist, *data; + char *actlist; - if (use_colors) { - actlist = get_activity_list(TRUE, TRUE); - detlist = NULL; - } else { - actlist = get_activity_list(TRUE, FALSE); - detlist = get_activity_list(FALSE, TRUE); - } - - if (actlist == NULL && detlist == NULL) { + actlist = get_activity_list(item->bar->parent_window, TRUE, TRUE); + if (actlist == NULL) { if (get_size_only) item->min_size = item->max_size = 0; return; } - data = g_strconcat("{sbact ", actlist != NULL ? actlist : "", - " ", detlist != NULL ? detlist : "", "}", NULL); - item_default(item, get_size_only, data, ""); - g_free(data); + statusbar_item_default_handler(item, get_size_only, + NULL, actlist, FALSE); g_free_not_null(actlist); - g_free_not_null(detlist); } static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel) @@ -284,7 +164,7 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel activity_list = g_list_remove(activity_list, window); if (window->data_level != 0) activity_list = g_list_prepend(activity_list, window); - statusbar_item_redraw(activity_item); + statusbar_items_redraw("act"); return; } @@ -293,12 +173,12 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel if (window->data_level == 0) { /* remove from activity list */ activity_list = g_list_remove(activity_list, window); - statusbar_item_redraw(activity_item); + statusbar_items_redraw("act"); } else if (window->data_level != GPOINTER_TO_INT(oldlevel) || window->hilight_color != 0) { /* different level as last time (or maybe different hilight color?), just redraw it. */ - statusbar_item_redraw(activity_item); + statusbar_items_redraw("act"); } return; } @@ -320,7 +200,7 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel if (tmp == NULL) activity_list = g_list_append(activity_list, window); - statusbar_item_redraw(activity_item); + statusbar_items_redraw("act"); } static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window) @@ -329,437 +209,250 @@ static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window) if (g_list_find(activity_list, window) != NULL) activity_list = g_list_remove(activity_list, window); - statusbar_item_redraw(activity_item); + statusbar_items_redraw("act"); } static void sig_statusbar_activity_updated(void) { - statusbar_item_redraw(activity_item); + statusbar_items_redraw("act"); } -/* redraw -- more -- */ -static void statusbar_more(SBAR_ITEM_REC *item, int get_size_only) +static void item_more(SBAR_ITEM_REC *item, int get_size_only) { - item_default(item, get_size_only, "{sbmore}", ""); -} - -static void sig_statusbar_more_check_remove(WINDOW_REC *window) -{ - g_return_if_fail(window != NULL); - - if (!is_window_visible(window)) - return; + MAIN_WINDOW_REC *mainwin; + int visible; - if (more_item != NULL && WINDOW_GUI(window)->view->bottom) { - statusbar_item_remove(more_item); - more_item = NULL; + if (active_win == NULL) { + mainwin = NULL; + visible = FALSE; + } else { + mainwin = WINDOW_MAIN(active_win); + visible = WINDOW_GUI(active_win)->view->more_text; } -} -static void sig_statusbar_more_check(WINDOW_REC *window) -{ - if (window == NULL || !is_window_visible(window)) - return; - - if (!WINDOW_GUI(window)->view->bottom) { - if (more_item == NULL) { - more_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_more); - statusbar_redraw(mainbar); - } - } else if (more_item != NULL) { - statusbar_item_remove(more_item); - more_item = NULL; - } -} - -static void statusbar_lag(SBAR_ITEM_REC *item, int get_size_only) -{ - SERVER_REC *server; - GString *str; - int lag_unknown; - time_t now; - - server = active_win == NULL ? NULL : active_win->active_server; - if (server == NULL || server->lag_last_check == 0) { - /* No lag information */ + if (!visible) { + if (mainwin != NULL) + more_visible = g_slist_remove(more_visible, mainwin); if (get_size_only) item->min_size = item->max_size = 0; return; } - now = time(NULL); - str = g_string_new(NULL); - - /* FIXME: ugly ugly.. */ - if (server->lag_sent == 0 || now-server->lag_sent < 5) { - lag_unknown = now-server->lag_last_check > - MAX_LAG_UNKNOWN_TIME+settings_get_int("lag_check_time"); - - if (lag_min_show < 0 || (server->lag < lag_min_show && !lag_unknown)) { - /* small lag, don't display */ - } else { - g_string_sprintfa(str, "%d.%02d", server->lag/1000, - (server->lag % 1000)/10); - if (lag_unknown) - g_string_append(str, " (??)"); - } - } else { - /* big lag, still waiting .. */ - g_string_sprintfa(str, "%ld (??)", - (long) (now-server->lag_sent)); - } - - item_default(item, get_size_only, "{sblag $0-}", str->str); - - g_string_free(str, TRUE); + more_visible = g_slist_prepend(more_visible, mainwin); + statusbar_item_default_handler(item, get_size_only, NULL, "", FALSE); } -static void sig_statusbar_lag_redraw(void) +static void sig_statusbar_more_updated(void) { - statusbar_item_redraw(lag_item); + int visible; + + visible = g_slist_find(more_visible, WINDOW_MAIN(active_win)) != NULL; + if (WINDOW_GUI(active_win)->view->more_text != visible) + statusbar_items_redraw("more"); } -static int statusbar_lag_timeout(void) +/* Returns the lag in milliseconds. If we haven't been able to ask the lag + for a while, unknown is set to TRUE. */ +static int get_lag(SERVER_REC *server, int *unknown) { - /* refresh statusbar every 10 seconds */ - if (time(NULL)-lag_last_draw < LAG_REFRESH_TIME) - return 1; + long lag; - statusbar_item_redraw(lag_item); - return 1; -} + *unknown = FALSE; -/* FIXME: this isn't very good.. it handles only mbox mailboxes. - this whole mail feature should really be in it's own module with lots - of other mail formats supported and people who don't want to use it - wouldn't need to.. */ -static int get_mail_count(void) -{ - struct stat statbuf; - FILE *f; - char str[512], *fname; - int count; - - fname = g_getenv("MAIL"); - if (fname == NULL) return 0; - - if (stat(fname, &statbuf) != 0) { - mail_last_mtime = -1; - mail_last_size = -1; - mail_last_count = 0; + if (server == NULL || server->lag_last_check == 0) { + /* lag has not been asked even once yet */ return 0; } - if (statbuf.st_mtime == mail_last_mtime && - statbuf.st_size == mail_last_size) - return mail_last_count; - mail_last_mtime = statbuf.st_mtime; - mail_last_size = statbuf.st_size; - - f = fopen(fname, "r"); - if (f == NULL) { - mail_last_count = 0; - return 0; + if (server->lag_sent.tv_sec == 0) { + /* no lag queries going on currently */ + return server->lag; } - count = 0; - while (fgets(str, sizeof(str), f) != NULL) { - if (strncmp(str, "From ", 5) == 0) - count++; - if (strncmp(str, "Subject: ", 9) == 0 && - strstr(str, "FOLDER INTERNAL DATA")) { - /* don't count these. */ - count--; - } + /* we're not sure about our current lag.. */ + *unknown = TRUE; + + lag = (long) (time(NULL)-server->lag_sent.tv_sec); + if (server->lag/1000 > lag) { + /* we've been waiting the lag reply less time than + what last known lag was -> use the last known lag */ + return server->lag; } - fclose(f); - mail_last_count = count; - return count; + /* return how long we have been waiting for lag reply */ + return lag*1000; } -static void statusbar_mail(SBAR_ITEM_REC *item, int get_size_only) +static void item_lag(SBAR_ITEM_REC *item, int get_size_only) { - char countstr[MAX_INT_STRLEN]; - int mail_count; + SERVER_REC *server; + char str[MAX_INT_STRLEN+10]; + int lag, lag_unknown; - mail_count = settings_get_bool("mail_counter") ? get_mail_count() : 0; + server = active_win == NULL ? NULL : active_win->active_server; + lag = get_lag(server, &lag_unknown)/10; - if (mail_count <= 0) { + if (lag <= 0 || lag < settings_get_int("lag_min_show")) { + /* don't print the lag item */ if (get_size_only) item->min_size = item->max_size = 0; return; } - ltoa(countstr, mail_count); - item_default(item, get_size_only, "{sbmail $0-}", countstr); -} - -static int statusbar_mail_timeout(void) -{ - statusbar_item_redraw(mail_item); - return 1; -} - -static void statusbar_topic(SBAR_ITEM_REC *item, int get_size_only) -{ - item_default(item, get_size_only, "$topic", ""); -} - -static void sig_statusbar_topic_redraw(void) -{ - if (topic_item != NULL) statusbar_item_redraw(topic_item); -} - -static void sig_sidebars_redraw(void) -{ - GSList *tmp; + last_lag = lag; + last_lag_unknown = lag_unknown; - for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { - MAIN_WINDOW_REC *rec = tmp->data; - - if (rec->statusbar_window_item != NULL) - statusbar_item_redraw(rec->statusbar_window_item); + if (lag_unknown) { + g_snprintf(str, sizeof(str), "%d (?""?)", lag/100); + } else { + g_snprintf(str, sizeof(str), + lag%100 == 0 ? "%d" : "%d.%02d", lag/100, lag%100); } -} -static void topicbar_create(void) -{ - if (topic_bar != NULL) - return; - - topic_bar = statusbar_create(STATUSBAR_POS_UP, 0); - topic_item = statusbar_item_create(topic_bar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_topic); - topic_item->max_size = TRUE; - statusbar_redraw(topic_bar); - - signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); - signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); - signal_add("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); - signal_add("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); + statusbar_item_default_handler(item, get_size_only, + NULL, str, TRUE); } -static void topicbar_destroy(void) +static void lag_check_update(void) { - if (topic_bar == NULL) - return; + SERVER_REC *server; + int lag, lag_unknown; - statusbar_destroy(topic_bar); - topic_item = NULL; - topic_bar = NULL; + server = active_win == NULL ? NULL : active_win->active_server; + lag = get_lag(server, &lag_unknown)/10; - signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); - signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); - signal_remove("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); - signal_remove("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); -} + if (lag < settings_get_int("lag_min_show")) + lag = 0; -static void mainbar_remove_items(void) -{ - statusbar_item_remove(clock_item); - statusbar_item_remove(nick_item); - statusbar_item_remove(window_item); - statusbar_item_remove(mail_item); - statusbar_item_remove(lag_item); - statusbar_item_remove(activity_item); + if (lag != last_lag || (lag > 0 && lag_unknown != last_lag_unknown)) + statusbar_items_redraw("lag"); } -static void mainbar_add_items(MAIN_WINDOW_REC *window) +static void sig_server_lag_updated(SERVER_REC *server) { - mainbar = window->statusbar; - mainbar_window = window; - - clock_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_clock); - nick_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_nick); - window_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window); - mail_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_mail); - lag_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_lag); - activity_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_activity); + if (active_win != NULL && active_win->active_server == server) + lag_check_update(); } -static void sidebar_add_items(MAIN_WINDOW_REC *window) +static int sig_lag_timeout(void) { - window->statusbar_window_item = - statusbar_item_create(window->statusbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window); + lag_check_update(); + return 1; } -static void sidebar_remove_items(MAIN_WINDOW_REC *window) +static void item_input(SBAR_ITEM_REC *item, int get_size_only) { - if (window->statusbar_window_item != NULL) { - statusbar_item_remove(window->statusbar_window_item); - window->statusbar_window_item = NULL; - } -} + GUI_ENTRY_REC *rec; -static void sig_mainwindow_created(MAIN_WINDOW_REC *window) -{ - window->statusbar = - statusbar_create(STATUSBAR_POS_MIDDLE, - window->first_line+window->height); - ((STATUSBAR_REC *) window->statusbar)->window = window; - sidebar_add_items(window); -} + rec = g_hash_table_lookup(input_entries, item); + if (rec == NULL) { + rec = gui_entry_create(item->xpos, item->bar->real_ypos, + item->size, + settings_get_bool("term_utf8")); + gui_entry_set_active(rec); + g_hash_table_insert(input_entries, item, rec); + } -static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window) -{ - if (window == mainbar_window) { - mainbar = NULL; - mainbar_window = NULL; + if (get_size_only) { + item->min_size = 2+term_width/10; + item->max_size = term_width; + return; } - if (window->statusbar != NULL) - statusbar_destroy(window->statusbar); + gui_entry_move(rec, item->xpos, item->bar->real_ypos, + item->size); + gui_entry_redraw(rec); /* FIXME: this is only necessary with ^L.. */ } -static void sig_main_statusbar_changed(WINDOW_REC *window) +static void sig_statusbar_item_destroyed(SBAR_ITEM_REC *item) { - MAIN_WINDOW_REC *parent; - - if (window == NULL) - return; - - parent = WINDOW_GUI(window)->parent; - if (mainbar == parent->statusbar) - return; + GUI_ENTRY_REC *rec; - if (mainbar != NULL) { - mainbar_remove_items(); - sidebar_add_items(mainbar_window); + rec = g_hash_table_lookup(input_entries, item); + if (rec != NULL) { + gui_entry_destroy(rec); + g_hash_table_remove(input_entries, item); } - sidebar_remove_items(parent); - mainbar_add_items(parent); } static void read_settings(void) { - use_colors = settings_get_bool("colors") && has_colors(); - if (settings_get_bool("topicbar")) - topicbar_create(); - else - topicbar_destroy(); - - lag_min_show = settings_get_int("lag_min_show")*10; - statusbar_redraw(NULL); + if (active_entry != NULL) { + gui_entry_set_utf8(active_entry, + settings_get_bool("term_utf8")); + } } void statusbar_items_init(void) { - GSList *tmp; - settings_add_int("misc", "lag_min_show", 100); - settings_add_bool("lookandfeel", "topicbar", TRUE); settings_add_bool("lookandfeel", "actlist_moves", FALSE); - settings_add_bool("misc", "mail_counter", TRUE); - - /* clock */ - clock_timetag = g_timeout_add(1000, (GSourceFunc) statusbar_clock_timeout, NULL); - - /* nick */ - signal_add("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_add("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - - /* channel */ - signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw); - signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); - signal_add("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item); - signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); - signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); - - /* activity */ + + statusbar_item_register("window", NULL, item_window_active); + statusbar_item_register("window_empty", NULL, item_window_empty); + statusbar_item_register("prompt", NULL, item_window_active); + statusbar_item_register("prompt_empty", NULL, item_window_empty); + statusbar_item_register("lag", NULL, item_lag); + statusbar_item_register("act", NULL, item_act); + statusbar_item_register("more", NULL, item_more); + statusbar_item_register("input", NULL, item_input); + + /* activity */ activity_list = NULL; signal_add("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight); signal_add("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed); signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated); - /* more */ - more_item = NULL; - signal_add("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove); - signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_more_check); - signal_add("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check); - - /* lag */ - lag_timetag = g_timeout_add(1000*LAG_REFRESH_TIME, (GSourceFunc) statusbar_lag_timeout, NULL); - signal_add("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw); - signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw); - - /* mail */ - mail_timetag = g_timeout_add(1000*MAIL_REFRESH_TIME, (GSourceFunc) statusbar_mail_timeout, NULL); - - /* topic */ - topic_item = NULL; topic_bar = NULL; - signal_add("setup changed", (SIGNAL_FUNC) read_settings); + /* more */ + more_visible = NULL; + signal_add("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_add_last("gui print text finished", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_add_last("command clear", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_add_last("command scrollback", (SIGNAL_FUNC) sig_statusbar_more_updated); + + /* lag */ + last_lag = 0; last_lag_unknown = FALSE; + signal_add("server lag", (SIGNAL_FUNC) sig_server_lag_updated); + signal_add("window changed", (SIGNAL_FUNC) lag_check_update); + signal_add("window server changed", (SIGNAL_FUNC) lag_check_update); + lag_timeout_tag = g_timeout_add(5000, (GSourceFunc) sig_lag_timeout, NULL); + + /* input */ + input_entries = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + signal_add("statusbar item destroyed", (SIGNAL_FUNC) sig_statusbar_item_destroyed); read_settings(); - statusbar_redraw(NULL); - - /* middle bars */ - signal_add("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created); - signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed); - signal_add("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed); - signal_add("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw); - - /* add statusbars to existing windows */ - for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) - sig_mainwindow_created(tmp->data); - sig_main_statusbar_changed(active_win); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); } void statusbar_items_deinit(void) { - /* clock */ - g_source_remove(clock_timetag); - - /* nick */ - signal_remove("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - signal_remove("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); - - /* channel */ - signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw); - signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); - signal_remove("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item); - signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); - signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); - - /* activity */ + /* activity */ signal_remove("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight); signal_remove("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed); signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated); g_list_free(activity_list); - - /* more */ - signal_remove("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove); - signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_more_check); - signal_remove("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check); - - /* lag */ - g_source_remove(lag_timetag); - signal_remove("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw); - signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw); - - /* mail */ - g_source_remove(mail_timetag); - - /* topic */ - topicbar_destroy(); - signal_remove("setup changed", (SIGNAL_FUNC) read_settings); - - /* middle bars */ - signal_remove("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created); - signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed); - signal_remove("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed); - signal_remove("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw); + activity_list = NULL; + + /* more */ + g_slist_free(more_visible); + signal_remove("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_remove("gui print text finished", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_remove("command clear", (SIGNAL_FUNC) sig_statusbar_more_updated); + signal_remove("command scrollback", (SIGNAL_FUNC) sig_statusbar_more_updated); + + /* lag */ + signal_remove("server lag", (SIGNAL_FUNC) sig_server_lag_updated); + signal_remove("window changed", (SIGNAL_FUNC) lag_check_update); + signal_remove("window server changed", (SIGNAL_FUNC) lag_check_update); + g_source_remove(lag_timeout_tag); + + /* input */ + signal_remove("statusbar item destroyed", (SIGNAL_FUNC) sig_statusbar_item_destroyed); + g_hash_table_destroy(input_entries); + + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); } diff --git a/apps/irssi/src/fe-text/statusbar.c b/apps/irssi/src/fe-text/statusbar.c index 8e99c4fe..a9271b3c 100644 --- a/apps/irssi/src/fe-text/statusbar.c +++ b/apps/irssi/src/fe-text/statusbar.c @@ -20,32 +20,129 @@ #include "module.h" #include "signals.h" +#include "expandos.h" +#include "special-vars.h" #include "themes.h" #include "statusbar.h" +#include "statusbar-config.h" #include "gui-windows.h" - -static int backs[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* FIXME: should be in some more generic place.. */ +#include "gui-printtext.h" void statusbar_items_init(void); void statusbar_items_deinit(void); -static GSList *statusbars; -static int sbar_uppest, sbar_lowest, sbars_up, sbars_down; +GSList *statusbar_groups; +STATUSBAR_GROUP_REC *active_statusbar_group; + +/* + sbar_item_defs: char *name => char *value + sbar_item_funcs: char *name => STATUSBAR_FUNC func + sbar_signal_items: int signal_id => GSList *(SBAR_ITEM_REC *items) + sbar_item_signals: SBAR_ITEM_REC *item => GSList *(int *signal_ids) + named_sbar_items: const char *name => GSList *(SBAR_ITEM_REC *items) +*/ +static GHashTable *sbar_item_defs, *sbar_item_funcs; +static GHashTable *sbar_signal_items, *sbar_item_signals; +static GHashTable *named_sbar_items; +static int statusbar_need_recreate_items; + +void statusbar_item_register(const char *name, const char *value, + STATUSBAR_FUNC func) +{ + gpointer hkey, hvalue; + + statusbar_need_recreate_items = TRUE; + if (value != NULL) { + if (g_hash_table_lookup_extended(sbar_item_defs, + name, &hkey, &hvalue)) { + g_hash_table_remove(sbar_item_defs, name); + g_free(hkey); + g_free(hvalue); + } + g_hash_table_insert(sbar_item_defs, + g_strdup(name), g_strdup(value)); + } + + if (func != NULL) { + if (g_hash_table_lookup(sbar_item_funcs, name) == NULL) { + g_hash_table_insert(sbar_item_funcs, + g_strdup(name), (void *) func); + } + } +} + +void statusbar_item_unregister(const char *name) +{ + gpointer key, value; + + statusbar_need_recreate_items = TRUE; + if (g_hash_table_lookup_extended(sbar_item_defs, + name, &key, &value)) { + g_hash_table_remove(sbar_item_defs, key); + g_free(key); + g_free(value); + } + + if (g_hash_table_lookup_extended(sbar_item_funcs, + name, &key, &value)) { + g_hash_table_remove(sbar_item_funcs, key); + g_free(key); + } +} + +STATUSBAR_GROUP_REC *statusbar_group_create(const char *name) +{ + STATUSBAR_GROUP_REC *rec; + + rec = g_new0(STATUSBAR_GROUP_REC, 1); + rec->name = g_strdup(name); + + statusbar_groups = g_slist_append(statusbar_groups, rec); + return rec; +} -static void statusbar_item_destroy(SBAR_ITEM_REC *rec) +void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec) { - rec->bar->items = g_slist_remove(rec->bar->items, rec); - g_free(rec); + statusbar_groups = g_slist_remove(statusbar_groups, rec); + + while (rec->bars != NULL) + statusbar_destroy(rec->bars->data); + while (rec->config_bars != NULL) + statusbar_config_destroy(rec, rec->config_bars->data); + + g_free(rec->name); + g_free(rec); +} + +STATUSBAR_GROUP_REC *statusbar_group_find(const char *name) +{ + GSList *tmp; + + for (tmp = statusbar_groups; tmp != NULL; tmp = tmp->next) { + STATUSBAR_GROUP_REC *rec = tmp->data; + + if (strcmp(rec->name, name) == 0) + return rec; + } + + return NULL; } static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2) { - return item1->priority == item2->priority ? 0 : - item1->priority < item2->priority ? -1 : 1; + return item1->config->priority == item2->config->priority ? 0 : + item1->config->priority < item2->config->priority ? -1 : 1; } +static int sbar_cmp_position(STATUSBAR_REC *bar1, STATUSBAR_REC *bar2) +{ + return bar1->config->position < bar2->config->position ? -1 : 1; +} + +/* Shink all items in statusbar to their minimum requested size. + The items list should be sorted by priority, highest first. */ static int statusbar_shrink_to_min(GSList *items, int size, int max_width) { GSList *tmp; @@ -71,6 +168,9 @@ static int statusbar_shrink_to_min(GSList *items, int size, int max_width) return size; } +/* shink the items in statusbar, even if their size gets smaller than + their minimum requested size. The items list should be sorted by + priority, highest first. */ static void statusbar_shrink_forced(GSList *items, int size, int max_width) { GSList *tmp; @@ -80,7 +180,7 @@ static void statusbar_shrink_forced(GSList *items, int size, int max_width) if (size-rec->size > max_width) { /* remove the whole item */ - size -= rec->size+1; /* +1 == the marginal */ + size -= rec->size; rec->size = 0; } else { /* shrink the item */ @@ -90,14 +190,14 @@ static void statusbar_shrink_forced(GSList *items, int size, int max_width) } } -static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width) +static void statusbar_resize_items(STATUSBAR_REC *bar, int max_width) { GSList *tmp, *prior_sorted; int width; /* first give items their max. size */ prior_sorted = NULL; - width = -1; /* -1 because of the marginals */ + width = 0; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; @@ -105,8 +205,7 @@ static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width) rec->size = rec->max_size; if (rec->size > 0) { - /* +1 == marginal between items */ - width += rec->max_size+1; + width += rec->max_size; prior_sorted = g_slist_insert_sorted(prior_sorted, rec, (GCompareFunc) @@ -131,239 +230,908 @@ static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width) g_slist_free(prior_sorted); } -static void statusbar_redraw_line(STATUSBAR_REC *bar) +#define SBAR_ITEM_REDRAW_NEEDED(_bar, _item, _xpos) \ + (((_bar)->dirty_xpos != -1 && \ + (_xpos) >= (_bar)->dirty_xpos) || \ + (_item)->xpos != (_xpos) || (_item)->current_size != (_item)->size) + +static void statusbar_calc_item_positions(STATUSBAR_REC *bar) { WINDOW_REC *old_active_win; - GSList *tmp; + GSList *tmp, *right_items; int xpos, rxpos; old_active_win = active_win; - if (bar->window != NULL) - active_win = bar->window->active; + if (bar->parent_window != NULL) + active_win = bar->parent_window->active; - statusbar_get_sizes(bar, COLS-2); + statusbar_resize_items(bar, term_width); - xpos = 1; + /* left-aligned items */ + xpos = 0; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; - if (!rec->right_justify && rec->size > 0) { - rec->xpos = xpos; - xpos += rec->size+1; - rec->func(rec, FALSE); + if (!rec->config->right_alignment && + (rec->size > 0 || rec->current_size > 0)) { + if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, xpos)) { + /* redraw the item */ + rec->dirty = TRUE; + if (bar->dirty_xpos == -1 || + xpos < bar->dirty_xpos) { + irssi_set_dirty(); + bar->dirty = TRUE; + bar->dirty_xpos = xpos; + } + + rec->xpos = xpos; + } + xpos += rec->size; } } - rxpos = COLS-1; + /* right-aligned items - first copy them to a new list backwards, + easier to draw them in right order */ + right_items = NULL; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; - if (rec->right_justify && rec->size > 0) { - rxpos -= rec->size+1; - rec->xpos = rxpos+1; - rec->func(rec, FALSE); + if (rec->config->right_alignment) { + if (rec->size > 0) + right_items = g_slist_prepend(right_items, rec); + else if (rec->current_size > 0) { + /* item was hidden - set the dirty position + to begin from the item's old xpos */ + irssi_set_dirty(); + bar->dirty = TRUE; + bar->dirty_xpos = rec->xpos; + } + } + } + + rxpos = term_width; + for (tmp = right_items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_REC *rec = tmp->data; + + rxpos -= rec->size; + if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, rxpos)) { + rec->dirty = TRUE; + if (bar->dirty_xpos == -1 || + rxpos < bar->dirty_xpos) { + irssi_set_dirty(); + bar->dirty = TRUE; + bar->dirty_xpos = rxpos; + } + rec->xpos = rxpos; } } + g_slist_free(right_items); active_win = old_active_win; } -static void statusbar_redraw_all(void) +void statusbar_redraw(STATUSBAR_REC *bar, int force) { - screen_refresh_freeze(); - g_slist_foreach(statusbars, (GFunc) statusbar_redraw, NULL); - screen_refresh_thaw(); + if (statusbar_need_recreate_items) + return; /* don't bother yet */ + + if (bar != NULL) { + if (force) { + irssi_set_dirty(); + bar->dirty = TRUE; + bar->dirty_xpos = 0; + } + statusbar_calc_item_positions(bar); + } else if (active_statusbar_group != NULL) { + g_slist_foreach(active_statusbar_group->bars, + (GFunc) statusbar_redraw, + GINT_TO_POINTER(force)); + } } -STATUSBAR_REC *statusbar_find(int pos, int line) +void statusbar_item_redraw(SBAR_ITEM_REC *item) { - GSList *tmp; + WINDOW_REC *old_active_win; - for (tmp = statusbars; tmp != NULL; tmp = tmp->next) { - STATUSBAR_REC *rec = tmp->data; + g_return_if_fail(item != NULL); + + old_active_win = active_win; + if (item->bar->parent_window != NULL) + active_win = item->bar->parent_window->active; + + item->func(item, TRUE); + + item->dirty = TRUE; + item->bar->dirty = TRUE; + irssi_set_dirty(); - if (rec->pos == pos && rec->line == line) - return rec; + if (item->max_size != item->size) { + /* item wants a new size - we'll need to redraw + the statusbar to see if this is allowed */ + statusbar_redraw(item->bar, FALSE); } - return NULL; + active_win = old_active_win; } -void statusbar_redraw(STATUSBAR_REC *bar) +void statusbar_items_redraw(const char *name) { - if (bar == NULL) { - statusbar_redraw_all(); + g_slist_foreach(g_hash_table_lookup(named_sbar_items, name), + (GFunc) statusbar_item_redraw, NULL); +} + +static void statusbars_recalc_ypos(STATUSBAR_REC *bar) +{ + GSList *tmp, *bar_group; + int ypos; + + /* get list of statusbars with same type and placement, + sorted by position */ + bar_group = NULL; + tmp = bar->config->type == STATUSBAR_TYPE_ROOT ? bar->group->bars : + bar->parent_window->statusbars; + + for (; tmp != NULL; tmp = tmp->next) { + STATUSBAR_REC *rec = tmp->data; + + if (rec->config->type == bar->config->type && + rec->config->placement == bar->config->placement) { + bar_group = g_slist_insert_sorted(bar_group, rec, + (GCompareFunc) + sbar_cmp_position); + } + } + + if (bar_group == NULL) { + /* we just destroyed the last statusbar in this + type/placement group */ return; } - set_bg(stdscr, backs[bar->color] << 4); - move(bar->ypos, 0); clrtoeol(); - set_bg(stdscr, 0); + /* get the Y-position for the first statusbar */ + if (bar->config->type == STATUSBAR_TYPE_ROOT) { + ypos = bar->config->placement == STATUSBAR_TOP ? 0 : + term_height - g_slist_length(bar_group); + } else { + ypos = bar->config->placement == STATUSBAR_TOP ? + bar->parent_window->first_line : + bar->parent_window->last_line - + (g_slist_length(bar_group)-1); + } - statusbar_redraw_line(bar); + /* set the Y-positions */ + while (bar_group != NULL) { + bar = bar_group->data; + + if (bar->real_ypos != ypos) { + bar->real_ypos = ypos; + statusbar_redraw(bar, TRUE); + } - screen_refresh(NULL); + ypos++; + bar_group = g_slist_remove(bar_group, bar_group->data); + } } -void statusbar_item_redraw(SBAR_ITEM_REC *item) +static void sig_terminal_resized(void) { - g_return_if_fail(item != NULL); + GSList *tmp; - item->func(item, TRUE); - if (item->max_size != item->size) - statusbar_redraw(item->bar); - else { - item->func(item, FALSE); - screen_refresh(NULL); + for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_REC *bar = tmp->data; + + if (bar->config->type == STATUSBAR_TYPE_ROOT && + bar->config->placement == STATUSBAR_BOTTOM) { + statusbars_recalc_ypos(bar); + break; + } } } -static int get_last_bg(const char *str) +static void mainwindow_recalc_ypos(MAIN_WINDOW_REC *window, int placement) { - int last = -1; + GSList *tmp; - while (*str != '\0') { - if (*str == '%' && str[1] != '\0') { - str++; - if (*str >= '0' && *str <= '7') - last = *str-'0'; + for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_REC *bar = tmp->data; + + if (bar->config->placement == placement) { + statusbars_recalc_ypos(bar); + break; } - str++; } +} - return last; +static void sig_mainwindow_resized(MAIN_WINDOW_REC *window) +{ + mainwindow_recalc_ypos(window, STATUSBAR_TOP); + mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM); } -/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */ -STATUSBAR_REC *statusbar_create(int pos, int ypos) +STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group, + STATUSBAR_CONFIG_REC *config, + MAIN_WINDOW_REC *parent_window) { - STATUSBAR_REC *rec; - char *str; + STATUSBAR_REC *bar; + THEME_REC *theme; + GSList *tmp; + char *name, *value; + + g_return_val_if_fail(group != NULL, NULL); + g_return_val_if_fail(config != NULL, NULL); + g_return_val_if_fail(config->type != STATUSBAR_TYPE_WINDOW || + parent_window != NULL, NULL); + + bar = g_new0(STATUSBAR_REC, 1); + group->bars = g_slist_append(group->bars, bar); + + bar->group = group; + + bar->config = config; + bar->parent_window = parent_window; + + irssi_set_dirty(); + bar->dirty = TRUE; + bar->dirty_xpos = 0; + + signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized); + signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); + signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); - rec = g_new0(STATUSBAR_REC, 1); - statusbars = g_slist_append(statusbars, rec); + if (config->type == STATUSBAR_TYPE_ROOT) { + /* top/bottom of the screen */ + mainwindows_reserve_lines(config->placement == STATUSBAR_TOP, + config->placement == STATUSBAR_BOTTOM); + theme = current_theme; + } else { + /* top/bottom of the window */ + parent_window->statusbars = + g_slist_append(parent_window->statusbars, bar); + mainwindow_set_statusbar_lines(parent_window, + config->placement == STATUSBAR_TOP, + config->placement == STATUSBAR_BOTTOM); + theme = parent_window != NULL && parent_window->active != NULL && + parent_window->active->theme != NULL ? + parent_window->active->theme : current_theme; + } - rec->pos = pos; - rec->line = pos == STATUSBAR_POS_MIDDLE ? ypos : - mainwindows_reserve_lines(1, pos == STATUSBAR_POS_UP); - rec->ypos = pos == STATUSBAR_POS_MIDDLE ? ypos : - pos == STATUSBAR_POS_UP ? rec->line : LINES-1-rec->line; + signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized); + signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); + signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); /* get background color from sb_background abstract */ - str = theme_format_expand(current_theme, "{sb_background}"); - if (str == NULL) str = g_strdup("%n%8"); - rec->color_string = g_strconcat("%n", str, NULL); - g_free(str); + name = g_strdup_printf("{sb_%s_bg}", config->name); + value = theme_format_expand(theme, name); + g_free(name); + + if (*value == '\0') { + /* try with the statusbar group name */ + g_free(value); + + name = g_strdup_printf("{sb_%s_bg}", group->name); + value = theme_format_expand(theme, name); + g_free(name); + + if (*value == '\0') { + /* fallback to default statusbar background + (also provides backwards compatibility..) */ + g_free(value); + value = theme_format_expand(theme, "{sb_background}"); + } + } + + if (*value == '\0') { + g_free(value); + value = g_strdup("%8"); + } + bar->color = g_strconcat("%n", value, NULL); + g_free(value); - rec->color = get_last_bg(rec->color_string); - if (rec->color < 0) rec->color = current_theme->default_real_color; + statusbars_recalc_ypos(bar); + signal_emit("statusbar created", 1, bar); - if (pos == STATUSBAR_POS_UP) { - if (sbars_up == 0) sbar_uppest = rec->line; - sbars_up++; - rec->line -= sbar_uppest; - } else if (pos == STATUSBAR_POS_DOWN) { - if (sbars_down == 0) sbar_lowest = rec->line; - sbars_down++; - rec->line -= sbar_lowest; + /* create the items to statusbar */ + for (tmp = config->items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_CONFIG_REC *rec = tmp->data; + + statusbar_item_create(bar, rec); } + return bar; +} - set_bg(stdscr, backs[rec->color] << 4); - move(rec->ypos, 0); clrtoeol(); - set_bg(stdscr, 0); +void statusbar_destroy(STATUSBAR_REC *bar) +{ + int top; - return rec; + g_return_if_fail(bar != NULL); + + bar->group->bars = g_slist_remove(bar->group->bars, bar); + if (bar->parent_window != NULL) { + bar->parent_window->statusbars = + g_slist_remove(bar->parent_window->statusbars, bar); + } + + signal_emit("statusbar destroyed", 1, bar); + + while (bar->items != NULL) + statusbar_item_destroy(bar->items->data); + + g_free(bar->color); + + if (bar->config->type != STATUSBAR_TYPE_WINDOW || + bar->parent_window != NULL) + statusbars_recalc_ypos(bar); + + top = bar->config->placement == STATUSBAR_TOP; + if (bar->config->type == STATUSBAR_TYPE_ROOT) { + /* top/bottom of the screen */ + mainwindows_reserve_lines(top ? -1 : 0, !top ? -1 : 0); + } else if (bar->parent_window != NULL) { + /* top/bottom of the window */ + mainwindow_set_statusbar_lines(bar->parent_window, + top ? -1 : 0, !top ? -1 : 0); + } + + g_free(bar); } -static void statusbars_pack(int pos, int line) +void statusbar_recreate_items(STATUSBAR_REC *bar) { GSList *tmp; - for (tmp = statusbars; tmp != NULL; tmp = tmp->next) { + /* destroy */ + while (bar->items != NULL) + statusbar_item_destroy(bar->items->data); + + /* create */ + for (tmp = bar->config->items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_CONFIG_REC *rec = tmp->data; + + statusbar_item_create(bar, rec); + } + + statusbar_redraw(bar, TRUE); +} + +void statusbars_recreate_items(void) +{ + if (active_statusbar_group != NULL) { + g_slist_foreach(active_statusbar_group->bars, + (GFunc) statusbar_recreate_items, NULL); + } +} + +STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name, + MAIN_WINDOW_REC *window) +{ + GSList *tmp; + + for (tmp = group->bars; tmp != NULL; tmp = tmp->next) { STATUSBAR_REC *rec = tmp->data; - if (rec->pos == pos && rec->line > line) { - rec->line--; - rec->ypos += (pos == STATUSBAR_POS_UP ? -1 : 1); + if (rec->parent_window == window && + strcmp(rec->config->name, name) == 0) + return rec; + } + + return NULL; +} + +static char *update_statusbar_bg(const char *str, const char *color) +{ + GString *out; + char *ret; + + out = g_string_new(color); + while (*str != '\0') { + if (*str == '%' && str[1] == 'n') { + g_string_append(out, color); + str += 2; + continue; } + + g_string_append_c(out, *str); + str++; } + + ret = out->str; + g_string_free(out, FALSE); + return ret; } -void statusbar_destroy(STATUSBAR_REC *bar) +const char *statusbar_item_get_value(SBAR_ITEM_REC *item) { - g_return_if_fail(bar != NULL); + const char *value; - if (bar->pos != STATUSBAR_POS_MIDDLE) - mainwindows_reserve_lines(-1, bar->pos == STATUSBAR_POS_UP); + value = item->config->value; + if (value == NULL) { + value = g_hash_table_lookup(sbar_item_defs, + item->config->name); + } - if (bar->pos == STATUSBAR_POS_UP) sbars_up--; - if (bar->pos == STATUSBAR_POS_DOWN) sbars_down--; - statusbars = g_slist_remove(statusbars, bar); + return value; +} - while (bar->items != NULL) - statusbar_item_destroy(bar->items->data); +void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only, + const char *str, const char *data, + int escape_vars) +{ + SERVER_REC *server; + WI_ITEM_REC *wiitem; + char *tmpstr, *tmpstr2; + int len; + + if (str == NULL) + str = statusbar_item_get_value(item); + if (str == NULL || *str == '\0') { + item->min_size = item->max_size = 0; + return; + } - if (bar->pos != STATUSBAR_POS_MIDDLE) - statusbars_pack(bar->pos, bar->pos); - g_free(bar->color_string); - g_free(bar); + if (active_win == NULL) { + server = NULL; + wiitem = NULL; + } else { + server = active_win->active_server; + wiitem = active_win->active; + } + + /* expand templates */ + tmpstr = theme_format_expand_data(current_theme, &str, + 'n', 'n', + NULL, NULL, + EXPAND_FLAG_ROOT | + EXPAND_FLAG_IGNORE_REPLACES | + EXPAND_FLAG_IGNORE_EMPTY); + /* expand $variables */ + tmpstr2 = parse_special_string(tmpstr, server, wiitem, data, NULL, + (escape_vars ? PARSE_FLAG_ESCAPE_VARS : 0 )); + g_free(tmpstr); + + /* remove color codes (not %formats) */ + tmpstr = strip_codes(tmpstr2); + g_free(tmpstr2); + + if (get_size_only) { + item->min_size = item->max_size = format_get_length(tmpstr); + } else { + if (item->size < item->min_size) { + /* they're forcing us smaller than minimum size.. */ + len = format_real_length(tmpstr, item->size); + tmpstr[len] = '\0'; + } + + tmpstr2 = update_statusbar_bg(tmpstr, item->bar->color); + gui_printtext(item->xpos, item->bar->real_ypos, tmpstr2); + g_free(tmpstr2); + } + g_free(tmpstr); +} + +static void statusbar_item_default_func(SBAR_ITEM_REC *item, int get_size_only) +{ + statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE); +} + +static void statusbar_update_item(void) +{ + GSList *items; + + items = g_hash_table_lookup(sbar_signal_items, + GINT_TO_POINTER(signal_get_emitted_id())); + while (items != NULL) { + SBAR_ITEM_REC *item = items->data; + + statusbar_item_redraw(item); + items = items->next; + } +} + +static void statusbar_update_server(SERVER_REC *server) +{ + SERVER_REC *item_server; + GSList *items; + + items = g_hash_table_lookup(sbar_signal_items, + GINT_TO_POINTER(signal_get_emitted_id())); + while (items != NULL) { + SBAR_ITEM_REC *item = items->data; + + item_server = item->bar->parent_window != NULL ? + item->bar->parent_window->active->active_server : + active_win->active_server; - if (!quitting) statusbar_redraw_all(); + if (item_server == server) + statusbar_item_redraw(item); + + items = items->next; + } +} + +static void statusbar_update_window(WINDOW_REC *window) +{ + WINDOW_REC *item_window; + GSList *items; + + items = g_hash_table_lookup(sbar_signal_items, + GINT_TO_POINTER(signal_get_emitted_id())); + while (items != NULL) { + SBAR_ITEM_REC *item = items->data; + + item_window = item->bar->parent_window != NULL ? + item->bar->parent_window->active : active_win; + + if (item_window == window) + statusbar_item_redraw(item); + + items = items->next; + } +} + +static void statusbar_update_window_item(WI_ITEM_REC *wiitem) +{ + WI_ITEM_REC *item_wi; + GSList *items; + + items = g_hash_table_lookup(sbar_signal_items, + GINT_TO_POINTER(signal_get_emitted_id())); + while (items != NULL) { + SBAR_ITEM_REC *item = items->data; + + item_wi = item->bar->parent_window != NULL ? + item->bar->parent_window->active->active : + active_win->active; + + if (item_wi == wiitem) + statusbar_item_redraw(item); + + items = items->next; + } +} + +static void statusbar_item_default_signals(SBAR_ITEM_REC *item) +{ + SIGNAL_FUNC func; + GSList *list; + const char *value; + void *signal_id; + int *signals, *pos; + + value = statusbar_item_get_value(item); + if (value == NULL) + return; + + signals = special_vars_get_signals(value); + if (signals == NULL) + return; + + for (pos = signals; *pos != -1; pos += 2) { + /* update signal -> item mappings */ + signal_id = GINT_TO_POINTER(*pos); + list = g_hash_table_lookup(sbar_signal_items, signal_id); + if (list == NULL) { + switch (pos[1]) { + case EXPANDO_ARG_NONE: + func = (SIGNAL_FUNC) statusbar_update_item; + break; + case EXPANDO_ARG_SERVER: + func = (SIGNAL_FUNC) statusbar_update_server; + break; + case EXPANDO_ARG_WINDOW: + func = (SIGNAL_FUNC) statusbar_update_window; + break; + case EXPANDO_ARG_WINDOW_ITEM: + func = (SIGNAL_FUNC) statusbar_update_window_item; + break; + default: + func = NULL; + break; + } + if (func != NULL) + signal_add_to_id(MODULE_NAME, 1, *pos, func); + } + + if (g_slist_find(list, item) == NULL) + list = g_slist_append(list, item); + g_hash_table_insert(sbar_signal_items, signal_id, list); + + /* update item -> signal mappings */ + list = g_hash_table_lookup(sbar_item_signals, item); + if (g_slist_find(list, signal_id) == NULL) + list = g_slist_append(list, signal_id); + g_hash_table_insert(sbar_item_signals, item, list); + } + g_free(signals); } SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar, - int priority, int right_justify, - STATUSBAR_FUNC func) + SBAR_ITEM_CONFIG_REC *config) { SBAR_ITEM_REC *rec; + GSList *items; g_return_val_if_fail(bar != NULL, NULL); - g_return_val_if_fail(func != NULL, NULL); + g_return_val_if_fail(config != NULL, NULL); rec = g_new0(SBAR_ITEM_REC, 1); - rec->bar = bar; bar->items = g_slist_append(bar->items, rec); - rec->priority = priority; - rec->right_justify = right_justify; - rec->func = func; + rec->bar = bar; + rec->config = config; + + rec->func = (STATUSBAR_FUNC) g_hash_table_lookup(sbar_item_funcs, + config->name); + if (rec->func == NULL) + rec->func = statusbar_item_default_func; + statusbar_item_default_signals(rec); + + items = g_hash_table_lookup(named_sbar_items, config->name); + items = g_slist_append(items, rec); + g_hash_table_insert(named_sbar_items, config->name, items); + irssi_set_dirty(); + rec->dirty = TRUE; + bar->dirty = TRUE; + + signal_emit("statusbar item created", 1, rec); return rec; } -void statusbar_item_remove(SBAR_ITEM_REC *item) +static void statusbar_signal_remove(int signal_id) { + signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_item); + signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_server); + signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window); + signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window_item); +} + +static void statusbar_item_remove_signal(SBAR_ITEM_REC *item, int signal_id) +{ + GSList *list; + + /* update signal -> item hash */ + list = g_hash_table_lookup(sbar_signal_items, + GINT_TO_POINTER(signal_id)); + list = g_slist_remove(list, item); + if (list != NULL) { + g_hash_table_insert(sbar_signal_items, + GINT_TO_POINTER(signal_id), list); + } else { + g_hash_table_remove(sbar_signal_items, + GINT_TO_POINTER(signal_id)); + statusbar_signal_remove(signal_id); + } +} + +void statusbar_item_destroy(SBAR_ITEM_REC *item) +{ + GSList *list; + g_return_if_fail(item != NULL); - statusbar_item_destroy(item); - if (!quitting) statusbar_redraw_all(); + item->bar->items = g_slist_remove(item->bar->items, item); + + list = g_hash_table_lookup(named_sbar_items, item->config->name); + list = g_slist_remove(list, item); + if (list == NULL) + g_hash_table_remove(named_sbar_items, item->config->name); + else + g_hash_table_insert(named_sbar_items, item->config->name, list); + + signal_emit("statusbar item destroyed", 1, item); + + list = g_hash_table_lookup(sbar_item_signals, item); + g_hash_table_remove(sbar_item_signals, item); + + while (list != NULL) { + statusbar_item_remove_signal(item, GPOINTER_TO_INT(list->data)); + list = g_slist_remove(list, list->data); + } + + g_free(item); } -static void sig_mainwindow_resized(MAIN_WINDOW_REC *window) +static void statusbar_redraw_needed_items(STATUSBAR_REC *bar) { - STATUSBAR_REC *rec; + WINDOW_REC *old_active_win; + GSList *tmp; + char *str; - rec = window->statusbar; - rec->ypos = window->first_line+window->height; + old_active_win = active_win; + if (bar->parent_window != NULL) + active_win = bar->parent_window->active; + + if (bar->dirty_xpos >= 0) { + str = g_strconcat(bar->color, "%>", NULL); + gui_printtext(bar->dirty_xpos, bar->real_ypos, str); + g_free(str); + } + + for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_REC *rec = tmp->data; + + if (rec->dirty || + (bar->dirty_xpos != -1 && + rec->xpos >= bar->dirty_xpos)) { + rec->current_size = rec->size; + rec->func(rec, FALSE); + rec->dirty = FALSE; + } + } + + active_win = old_active_win; } -void statusbar_init(void) +void statusbar_redraw_dirty(void) { - statusbars = NULL; - sbars_up = sbars_down = 0; + GSList *tmp; - statusbar_items_init(); + if (statusbar_need_recreate_items) { + statusbar_need_recreate_items = FALSE; + statusbars_recreate_items(); + } + + for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_REC *rec = tmp->data; + + if (rec->dirty) { + statusbar_redraw_needed_items(rec); + rec->dirty = FALSE; + rec->dirty_xpos = -1; + } + } +} + +#define STATUSBAR_IS_VISIBLE(bar, window) \ + ((bar)->visible == STATUSBAR_VISIBLE_ALWAYS || \ + (active_mainwin == (window) && \ + (bar)->visible == STATUSBAR_VISIBLE_ACTIVE) || \ + (active_mainwin != (window) && \ + (bar)->visible == STATUSBAR_VISIBLE_INACTIVE)) + +static void statusbars_remove_unvisible(MAIN_WINDOW_REC *window) +{ + GSList *tmp, *next; + + for (tmp = window->statusbars; tmp != NULL; tmp = next) { + STATUSBAR_REC *bar = tmp->data; + + next = tmp->next; + if (!STATUSBAR_IS_VISIBLE(bar->config, window)) + statusbar_destroy(bar); + } +} + +static void statusbars_add_visible(MAIN_WINDOW_REC *window) +{ + STATUSBAR_GROUP_REC *group; + STATUSBAR_REC *bar; + GSList *tmp; + + group = active_statusbar_group; + for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_CONFIG_REC *config = tmp->data; + + if (config->type == STATUSBAR_TYPE_WINDOW && + STATUSBAR_IS_VISIBLE(config, window) && + statusbar_find(group, config->name, window) == NULL) { + bar = statusbar_create(group, config, window); + statusbar_redraw(bar, TRUE); + } + } +} + +static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window) +{ + while (window->statusbars != NULL) { + STATUSBAR_REC *bar = window->statusbars->data; + + bar->parent_window->statusbars = + g_slist_remove(bar->parent_window->statusbars, bar); + bar->parent_window = NULL; + statusbar_destroy(bar); + } +} + +static void sig_window_changed(void) +{ + GSList *tmp; + + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + statusbars_remove_unvisible(rec); + statusbars_add_visible(rec); + } +} + +static void sig_gui_window_created(WINDOW_REC *window) +{ + statusbars_add_visible(WINDOW_MAIN(window)); +} + +static void statusbar_item_def_destroy(void *key, void *value) +{ + g_free(key); + g_free(value); +} + +static void statusbar_signal_item_destroy(void *key, GSList *value) +{ + while (value != NULL) { + statusbar_signal_remove(GPOINTER_TO_INT(value->data)); + value->data = g_slist_remove(value, value->data); + } +} + +static void statusbar_item_signal_destroy(void *key, GSList *value) +{ + g_slist_free(value); +} + +static void sig_setup_reload(void) +{ + /* statusbar-config.c recreates root statusbars, + we need to create window-statusbars */ + g_slist_foreach(mainwindows, (GFunc) statusbars_add_visible, NULL); +} + +void statusbar_init(void) +{ + statusbar_need_recreate_items = FALSE; + statusbar_groups = NULL; + active_statusbar_group = NULL; + sbar_item_defs = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + sbar_item_funcs = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + sbar_signal_items = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + sbar_item_signals = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + named_sbar_items = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + + signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized); signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); + signal_add("gui window created", (SIGNAL_FUNC) sig_gui_window_created); + signal_add("window changed", (SIGNAL_FUNC) sig_window_changed); + signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed); + signal_add_last("setup reread", (SIGNAL_FUNC) sig_setup_reload); + + statusbar_items_init(); + statusbar_config_init(); /* signals need to be before this call */ } void statusbar_deinit(void) { - statusbar_items_deinit(); + while (statusbar_groups != NULL) + statusbar_group_destroy(statusbar_groups->data); - while (statusbars != NULL) - statusbar_destroy(statusbars->data); + g_hash_table_foreach(sbar_item_defs, + (GHFunc) statusbar_item_def_destroy, NULL); + g_hash_table_destroy(sbar_item_defs); + g_hash_table_foreach(sbar_item_funcs, (GHFunc) g_free, NULL); + g_hash_table_destroy(sbar_item_funcs); + + g_hash_table_foreach(sbar_signal_items, + (GHFunc) statusbar_signal_item_destroy, NULL); + g_hash_table_destroy(sbar_signal_items); + g_hash_table_foreach(sbar_item_signals, + (GHFunc) statusbar_item_signal_destroy, NULL); + g_hash_table_destroy(sbar_item_signals); + g_hash_table_destroy(named_sbar_items); + + signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized); signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); + signal_remove("gui window created", (SIGNAL_FUNC) sig_gui_window_created); + signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed); + signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed); + signal_remove("setup reread", (SIGNAL_FUNC) sig_setup_reload); + + statusbar_items_deinit(); + statusbar_config_deinit(); } diff --git a/apps/irssi/src/fe-text/statusbar.h b/apps/irssi/src/fe-text/statusbar.h index 32d35b82..0d61c059 100644 --- a/apps/irssi/src/fe-text/statusbar.h +++ b/apps/irssi/src/fe-text/statusbar.h @@ -3,59 +3,115 @@ #include "mainwindows.h" -#define SBAR_PRIORITY_HIGH 100 -#define SBAR_PRIORITY_NORMAL 0 -#define SBAR_PRIORITY_LOW -100 - -enum { - STATUSBAR_POS_UP, - STATUSBAR_POS_MIDDLE, - STATUSBAR_POS_DOWN -}; +#define STATUSBAR_PRIORITY_HIGH 100 +#define STATUSBAR_PRIORITY_NORMAL 0 +#define STATUSBAR_PRIORITY_LOW -100 typedef struct SBAR_ITEM_REC SBAR_ITEM_REC; typedef void (*STATUSBAR_FUNC) (SBAR_ITEM_REC *item, int get_size_only); +/* type */ +#define STATUSBAR_TYPE_ROOT 1 +#define STATUSBAR_TYPE_WINDOW 2 + +/* placement */ +#define STATUSBAR_TOP 1 +#define STATUSBAR_BOTTOM 2 + +/* visible */ +#define STATUSBAR_VISIBLE_ALWAYS 1 +#define STATUSBAR_VISIBLE_ACTIVE 2 +#define STATUSBAR_VISIBLE_INACTIVE 3 + typedef struct { - MAIN_WINDOW_REC *window; + char *name; + GSList *config_bars; + GSList *bars; +} STATUSBAR_GROUP_REC; - int pos; - int line; +typedef struct { + char *name; - char *color_string; - int color; + int type; /* root/window */ + int placement; /* top/bottom */ + int position; /* the higher the number, the lower it is in screen */ + int visible; /* active/inactive/always */ - int ypos; /* real position in screen at the moment */ GSList *items; +} STATUSBAR_CONFIG_REC; + +typedef struct { + STATUSBAR_GROUP_REC *group; + STATUSBAR_CONFIG_REC *config; + + MAIN_WINDOW_REC *parent_window; /* if config->type == STATUSBAR_TYPE_WINDOW */ + GSList *items; + + char *color; /* background color */ + int real_ypos; /* real Y-position in screen at the moment */ + + int dirty:1; + int dirty_xpos; /* -1 = only redraw some items, >= 0 = redraw all items after from xpos */ } STATUSBAR_REC; +typedef struct { + char *name; + char *value; /* if non-NULL, overrides the default */ + + int priority; + unsigned int right_alignment:1; +} SBAR_ITEM_CONFIG_REC; + struct SBAR_ITEM_REC { STATUSBAR_REC *bar; - STATUSBAR_FUNC func; + SBAR_ITEM_CONFIG_REC *config; + STATUSBAR_FUNC func; /* what item wants */ - int priority; int min_size, max_size; - unsigned int right_justify:1; /* what item gets */ - int xpos, size; + int xpos, size; + + int current_size; /* item size currently in screen */ + int dirty:1; }; -/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */ -STATUSBAR_REC *statusbar_create(int pos, int ypos); -void statusbar_destroy(STATUSBAR_REC *bar); +extern GSList *statusbar_groups; +extern STATUSBAR_GROUP_REC *active_statusbar_group; + +void statusbar_item_register(const char *name, const char *value, + STATUSBAR_FUNC func); +void statusbar_item_unregister(const char *name); -STATUSBAR_REC *statusbar_find(int pos, int line); +STATUSBAR_GROUP_REC *statusbar_group_create(const char *name); +void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec); +STATUSBAR_GROUP_REC *statusbar_group_find(const char *name); + +STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group, + STATUSBAR_CONFIG_REC *config, + MAIN_WINDOW_REC *parent_window); +void statusbar_destroy(STATUSBAR_REC *bar); +STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name, + MAIN_WINDOW_REC *window); SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar, - int priority, int right_justify, - STATUSBAR_FUNC func); -void statusbar_item_remove(SBAR_ITEM_REC *item); + SBAR_ITEM_CONFIG_REC *config); +void statusbar_item_destroy(SBAR_ITEM_REC *item); + +void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only, + const char *str, const char *data, + int escape_vars); /* redraw statusbar, NULL = all */ -void statusbar_redraw(STATUSBAR_REC *bar); +void statusbar_redraw(STATUSBAR_REC *bar, int force); void statusbar_item_redraw(SBAR_ITEM_REC *item); +void statusbar_items_redraw(const char *name); + +void statusbar_recreate_items(STATUSBAR_REC *bar); +void statusbars_recreate_items(void); + +void statusbar_redraw_dirty(void); void statusbar_init(void); void statusbar_deinit(void); diff --git a/apps/irssi/src/fe-text/term-curses.c b/apps/irssi/src/fe-text/term-curses.c new file mode 100644 index 00000000..69e2b922 --- /dev/null +++ b/apps/irssi/src/fe-text/term-curses.c @@ -0,0 +1,385 @@ +/* + term-curses.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "settings.h" + +#include "term.h" +#include "mainwindows.h" + +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +# include +#else +# include +#endif +#include +#include + +#ifndef COLOR_PAIRS +# define COLOR_PAIRS 64 +#endif + +#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM) +# define USE_RESIZE_TERM +#endif + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE 0 +#endif + +struct _TERM_WINDOW { + int x, y; + int width, height; + WINDOW *win; +}; + +TERM_WINDOW *root_window; +int term_width, term_height; + +static int curs_x, curs_y; +static int freeze_refresh; +static struct termios old_tio; + +static int init_curses(void) +{ + char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + int num; + struct termios tio; + + if (!initscr()) + return FALSE; + + cbreak(); noecho(); idlok(stdscr, 1); +#ifdef HAVE_CURSES_IDCOK + /*idcok(stdscr, 1); - disabled currently, causes redrawing problems with NetBSD */ +#endif + intrflush(stdscr, FALSE); nodelay(stdscr, TRUE); + + /* Disable INTR, QUIT, VDSUSP and SUSP keys */ + if (tcgetattr(0, &old_tio) == 0) { + memcpy(&tio, &old_tio, sizeof(tio)); + tio.c_cc[VINTR] = _POSIX_VDISABLE; + tio.c_cc[VQUIT] = _POSIX_VDISABLE; +#ifdef VDSUSP + tio.c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif +#ifdef VSUSP + tio.c_cc[VSUSP] = _POSIX_VDISABLE; +#endif + tcsetattr(0, TCSADRAIN, &tio); + } + + if (has_colors()) + start_color(); + else if (term_use_colors) + term_use_colors = FALSE; + +#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS + /* this lets us to use the "default" background color for colors <= 7 so + background pixmaps etc. show up right */ + use_default_colors(); + + for (num = 1; num < COLOR_PAIRS; num++) + init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]); + + init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more + people want dark grey than white on white.. */ +#else + for (num = 1; num < COLOR_PAIRS; num++) + init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]); + init_pair(63, 0, 0); +#endif + + clear(); + return TRUE; +} + +static int term_init_int(void) +{ + int ret; + + ret = init_curses(); + if (!ret) return 0; + + curs_x = curs_y = 0; + freeze_refresh = 0; + + root_window = g_new0(TERM_WINDOW, 1); + root_window->win = stdscr; + + term_width = COLS; + term_height = LINES; + return ret; +} + +static void term_deinit_int(void) +{ + tcsetattr(0, TCSADRAIN, &old_tio); + + endwin(); + g_free_and_null(root_window); +} + +int term_init(void) +{ + if (!term_init_int()) + return FALSE; + + settings_add_int("lookandfeel", "default_color", 7); + term_common_init(); + return TRUE; +} + +void term_deinit(void) +{ + term_common_deinit(); + term_deinit_int(); +} + +/* Resize terminal - if width or height is negative, + the new size is unknown and should be figured out somehow */ +void term_resize(int width, int height) +{ +#ifdef HAVE_CURSES_RESIZETERM + if (width < 0 || height < 0) { +#endif + term_deinit_int(); + term_init_int(); +#ifdef HAVE_CURSES_RESIZETERM + } else if (term_width != width || term_height != height) { + term_width = width; + term_height = height; + resizeterm(term_height, term_width); + } +#endif +} + +void term_resize_final(int width, int height) +{ +#ifdef HAVE_CURSES_RESIZETERM + if (width < 0 || height < 0) + mainwindows_recreate(); +#else + mainwindows_recreate(); +#endif +} + +/* Returns TRUE if terminal has colors */ +int term_has_colors(void) +{ + return has_colors(); +} + +/* Force the colors on any way you can */ +void term_force_colors(int set) +{ + /* don't do anything with curses */ +} + +/* Clear screen */ +void term_clear(void) +{ + term_set_color(root_window, 0); + clear(); +} + +/* Beep */ +void term_beep(void) +{ + beep(); +} + +/* Create a new window in terminal */ +TERM_WINDOW *term_window_create(int x, int y, int width, int height) +{ + TERM_WINDOW *window; + + window = g_new0(TERM_WINDOW, 1); + window->x = x; window->y = y; + window->width = width; window->height = height; + window->win = newwin(height, width, y, x); + if (window->win == NULL) + g_error("newwin() failed: %d,%d %d,%d", x, y, width, height); + idlok(window->win, 1); + + return window; +} + +/* Destroy a terminal window */ +void term_window_destroy(TERM_WINDOW *window) +{ + delwin(window->win); + g_free(window); +} + +/* Move/resize a window */ +void term_window_move(TERM_WINDOW *window, int x, int y, + int width, int height) +{ + /* some checks to make sure the window is visible in screen, + otherwise curses could get nasty and not show our window anymore. */ + if (width < 1) width = 1; + if (height < 1) height = 1; + if (x+width > term_width) x = term_width-width; + if (y+height > term_height) y = term_height-height; + +#ifdef HAVE_CURSES_WRESIZE + if (window->width != width || window->height != height) + wresize(window->win, height, width); + if (window->x != x || window->y != y) + mvwin(window->win, y, x); +#else + if (window->width != width || window->height != height || + window->x != x || window->y != y) { + delwin(window->win); + window->win = newwin(height, width, y, x); + idlok(window->win, 1); + } +#endif + window->x = x; window->y = y; + window->width = width; window->height = height; +} + +/* Clear window */ +void term_window_clear(TERM_WINDOW *window) +{ + werase(window->win); +} + +/* Scroll window up/down */ +void term_window_scroll(TERM_WINDOW *window, int count) +{ + scrollok(window->win, TRUE); + wscrl(window->win, count); + scrollok(window->win, FALSE); +} + +static int get_attr(int color) +{ + int attr; + + if (!term_use_colors) + attr = (color & 0x70) ? A_REVERSE : 0; + else if (((color & 0x0f) == 8) && (color & ATTR_BOLD) == 0) + attr = (A_DIM | COLOR_PAIR(63)); + else if ((color & 0x77) == 0) + attr = A_NORMAL; + else { + if (color & ATTR_RESETFG) { + color &= ~0x0f; + color |= settings_get_int("default_color"); + } + attr = (COLOR_PAIR((color&7) + (color&0x70)/2)); + } + + if ((color & 0x08) || (color & ATTR_BOLD)) attr |= A_BOLD; + if (color & ATTR_BLINK) attr |= A_BLINK; + + if (color & ATTR_UNDERLINE) attr |= A_UNDERLINE; + if (color & ATTR_REVERSE) attr |= A_REVERSE; + return attr; +} + +/* Change active color */ +void term_set_color(TERM_WINDOW *window, int col) +{ + wattrset(window->win, get_attr(col)); + wbkgdset(window->win, ' ' | get_attr(col)); +} + +void term_move(TERM_WINDOW *window, int x, int y) +{ + wmove(window->win, y, x); +} + +void term_addch(TERM_WINDOW *window, int chr) +{ + waddch(window->win, chr); +} + +void term_addstr(TERM_WINDOW *window, const char *str) +{ + waddstr(window->win, (const char *) str); +} + +void term_clrtoeol(TERM_WINDOW *window) +{ + wclrtoeol(window->win); +} + +void term_move_cursor(int x, int y) +{ + curs_x = x; + curs_y = y; +} + +void term_refresh_freeze(void) +{ + freeze_refresh++; +} + +void term_refresh_thaw(void) +{ + if (freeze_refresh > 0) { + freeze_refresh--; + if (freeze_refresh == 0) term_refresh(NULL); + } +} + +void term_refresh(TERM_WINDOW *window) +{ + if (window != NULL) + wnoutrefresh(window->win); + + if (freeze_refresh == 0) { + move(curs_y, curs_x); + wnoutrefresh(stdscr); + doupdate(); + } +} + +void term_stop(void) +{ + term_deinit_int(); + kill(getpid(), SIGSTOP); + term_init_int(); + irssi_redraw(); +} + +int term_gets(unsigned char *buffer, int size) +{ + int key, count; + + for (count = 0; count < size; ) { + key = getch(); +#ifdef KEY_RESIZE + if (key == KEY_RESIZE) + continue; +#endif + + if (key == ERR) + break; + + buffer[count] = key; + count++; + } + + return count; +} diff --git a/apps/irssi/src/fe-text/term-dummy.c b/apps/irssi/src/fe-text/term-dummy.c new file mode 100644 index 00000000..a4f5c091 --- /dev/null +++ b/apps/irssi/src/fe-text/term-dummy.c @@ -0,0 +1,106 @@ +/* + term-dummy.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" + +#include "fe-windows.h" + +static int newline; + +static GIOChannel *stdin_channel; +static int readtag; +static GString *input; + +static void sig_gui_printtext(WINDOW_REC *window, void *fgcolor, + void *bgcolor, void *pflags, + char *str, void *level) +{ + if (newline) { + newline = FALSE; + printf("\r"); + } + + printf("%s", str); +} + +static void sig_gui_printtext_finished(WINDOW_REC *window) +{ + printf("\n"); + newline = TRUE; +} + +static void sig_window_created(WINDOW_REC *window) +{ + window->width = 80; + window->height = 25; +} + +static void readline(void) +{ + unsigned char buffer[128]; + char *p; + int ret, i; + + ret = read(0, buffer, sizeof(buffer)); + if (ret == 0 || (ret == -1 && errno != EINTR)) { + /* lost terminal */ + signal_emit("command quit", 1, "Lost terminal"); + return; + } + + for (i = 0; i < ret; i++) + g_string_append_c(input, buffer[i]); + + p = strchr(input->str, '\n'); + if (p != NULL) { + *p = '\0'; + signal_emit("send command", 3, input->str, + active_win->active_server, active_win->active); + *p = '\n'; + g_string_erase(input, 0, (int) (p-input->str)+1); + } +} + +void term_dummy_init(void) +{ + newline = TRUE; + input = g_string_new(NULL); + + signal_add("gui print text", (SIGNAL_FUNC) sig_gui_printtext); + signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished); + signal_add("window created", (SIGNAL_FUNC) sig_window_created); + + stdin_channel = g_io_channel_unix_new(0); + readtag = g_input_add_full(stdin_channel, + G_PRIORITY_HIGH, G_INPUT_READ, + (GInputFunction) readline, NULL); + g_io_channel_unref(stdin_channel); +} + +void term_dummy_deinit(void) +{ + signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_printtext); + signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished); + signal_remove("window created", (SIGNAL_FUNC) sig_window_created); + + g_source_remove(readtag); + g_string_free(input, TRUE); +} diff --git a/apps/irssi/src/fe-text/term-terminfo.c b/apps/irssi/src/fe-text/term-terminfo.c new file mode 100644 index 00000000..b900e2e9 --- /dev/null +++ b/apps/irssi/src/fe-text/term-terminfo.c @@ -0,0 +1,504 @@ +/* + term-terminfo.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "term.h" +#include "terminfo-core.h" + +#include + +struct _TERM_WINDOW { + /* Terminal to use for window */ + TERM_REC *term; + + /* Area for window in terminal */ + int x, y; + int width, height; +}; + +TERM_WINDOW *root_window; +int term_width, term_height, term_detached; + +static char *term_lines_empty; /* 1 if line is entirely empty */ +static int vcmove, vcx, vcy, curs_visible; +static int crealx, crealy, cforcemove; +static int curs_x, curs_y; +static int auto_detach; + +static int last_fg, last_bg, last_attrs; + +static int redraw_needed, redraw_tag; +static int freeze_counter; + +/* SIGCONT handler */ +static void sig_cont(int p) +{ + redraw_needed = TRUE; + terminfo_cont(current_term); +} + +static int redraw_timeout(void) +{ + if (redraw_needed) { + irssi_redraw(); + redraw_needed = FALSE; + } + + return 1; +} + +int term_init(void) +{ + struct sigaction act; + + last_fg = last_bg = -1; + last_attrs = 0; + vcx = vcy = 0; crealx = crealy = -1; + vcmove = FALSE; cforcemove = TRUE; + curs_visible = TRUE; + + current_term = terminfo_core_init(stdin, stdout); + if (current_term == NULL) + return FALSE; + + /* grab CONT signal */ + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = sig_cont; + sigaction(SIGCONT, &act, NULL); + redraw_tag = g_timeout_add(500, (GSourceFunc) redraw_timeout, NULL); + + curs_x = curs_y = 0; + term_width = current_term->width; + term_height = current_term->height; + root_window = term_window_create(0, 0, term_width, term_height); + term_detached = FALSE; + + term_lines_empty = g_new0(char, term_height); + + term_common_init(); + return TRUE; +} + +void term_deinit(void) +{ + g_source_remove(redraw_tag); + + term_common_deinit(); + terminfo_core_deinit(current_term); +} + +static void term_move_real(void) +{ + if (term_detached) return; + + if (vcx != crealx || vcy != crealy || cforcemove) { + if (curs_visible) { + terminfo_set_cursor_visible(FALSE); + curs_visible = FALSE; + } + + if (cforcemove) { + crealx = crealy = -1; + cforcemove = FALSE; + } + terminfo_move_relative(crealx, crealy, vcx, vcy); + crealx = vcx; crealy = vcy; + } + + vcmove = FALSE; +} + +/* Cursor position is unknown - move it immediately to known position */ +static void term_move_reset(int x, int y) +{ + if (x >= term_width) x = term_width-1; + if (y >= term_height) y = term_height-1; + + vcx = x; vcy = y; + cforcemove = TRUE; + term_move_real(); +} + +/* Resize terminal - if width or height is negative, + the new size is unknown and should be figured out somehow */ +void term_resize(int width, int height) +{ + if (width < 0 || height < 0) { + terminfo_resize(current_term); + width = current_term->width; + height = current_term->height; + } + + if (term_width != width || term_height != height) { + term_width = current_term->width = width; + term_height = current_term->height = height; + term_window_move(root_window, 0, 0, term_width, term_height); + + g_free(term_lines_empty); + term_lines_empty = g_new0(char, term_height); + } + + term_move_reset(0, 0); +} + +void term_resize_final(int width, int height) +{ +} + +/* Returns TRUE if terminal has colors */ +int term_has_colors(void) +{ + return current_term->has_colors; +} + +/* Force the colors on any way you can */ +void term_force_colors(int set) +{ + if (term_detached) return; + + terminfo_setup_colors(current_term, set); +} + +/* Clear screen */ +void term_clear(void) +{ + if (term_detached) return; + + term_set_color(root_window, 0); + terminfo_clear(); + term_move_reset(0, 0); + + memset(term_lines_empty, 1, term_height); +} + +/* Beep */ +void term_beep(void) +{ + if (term_detached) return; + + terminfo_beep(current_term); +} + +/* Create a new window in terminal */ +TERM_WINDOW *term_window_create(int x, int y, int width, int height) +{ + TERM_WINDOW *window; + + window = g_new0(TERM_WINDOW, 1); + window->term = current_term; + window->x = x; window->y = y; + window->width = width; window->height = height; + return window; +} + +/* Destroy a terminal window */ +void term_window_destroy(TERM_WINDOW *window) +{ + g_free(window); +} + +/* Move/resize a window */ +void term_window_move(TERM_WINDOW *window, int x, int y, + int width, int height) +{ + window->x = x; + window->y = y; + window->width = width; + window->height = height; +} + +/* Clear window */ +void term_window_clear(TERM_WINDOW *window) +{ + int y; + + if (term_detached) return; + + terminfo_set_normal(); + if (window->y == 0 && window->height == term_height) { + term_clear(); + } else { + for (y = 0; y < window->height; y++) { + term_move(window, 0, y); + term_clrtoeol(window); + } + } +} + +/* Scroll window up/down */ +void term_window_scroll(TERM_WINDOW *window, int count) +{ + int y; + + if (term_detached) return; + + terminfo_scroll(window->y, window->y+window->height-1, count); + term_move_reset(vcx, vcy); + + /* set the newly scrolled area dirty */ + for (y = 0; y < window->height; y++) + term_lines_empty[window->y+y] = FALSE; +} + +/* Change active color */ +void term_set_color(TERM_WINDOW *window, int col) +{ + int set_normal; + + if (term_detached) return; + + set_normal = ((col & ATTR_RESETFG) && last_fg != -1) || + ((col & ATTR_RESETBG) && last_bg != -1); + if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) || + ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) { + /* we'll need to get rid of bold/blink - this can only be + done with setting the default color */ + set_normal = TRUE; + } + + if (set_normal) { + last_fg = last_bg = -1; + last_attrs = 0; + terminfo_set_normal(); + } + + if (!term_use_colors && (col & 0xf0) != 0) + col |= ATTR_REVERSE; + + /* reversed text (use standout) */ + if (col & ATTR_REVERSE) { + if ((last_attrs & ATTR_REVERSE) == 0) + terminfo_set_standout(TRUE); + } else if (last_attrs & ATTR_REVERSE) + terminfo_set_standout(FALSE); + + /* set foreground color */ + if ((col & 0x0f) != last_fg && + ((col & 0x0f) != 0 || (col & ATTR_RESETFG) == 0)) { + if (term_use_colors) { + last_fg = col & 0x0f; + terminfo_set_fg(last_fg); + } + } + + /* set background color */ + if (col & ATTR_BLINK) + col |= 0x80; + else if (col & 0x80) + col |= ATTR_BLINK; + + if ((col & 0xf0) >> 4 != last_bg && + ((col & 0xf0) != 0 || (col & ATTR_RESETBG) == 0)) { + if (term_use_colors) { + last_bg = (col & 0xf0) >> 4; + terminfo_set_bg(last_bg); + } + } + + /* bold */ + if (col & 0x08) + col |= ATTR_BOLD; + else if (col & ATTR_BOLD) + terminfo_set_bold(); + + /* underline */ + if (col & ATTR_UNDERLINE) { + if ((last_attrs & ATTR_UNDERLINE) == 0) + terminfo_set_uline(TRUE); + } else if (last_attrs & ATTR_UNDERLINE) + terminfo_set_uline(FALSE); + + last_attrs = col & ~0xff; +} + +void term_move(TERM_WINDOW *window, int x, int y) +{ + vcmove = TRUE; + vcx = x+window->x; + vcy = y+window->y; + + if (vcx >= term_width) + vcx = term_width-1; + if (vcy >= term_height) + vcy = term_height-1; +} + +static void term_printed_text(int count) +{ + term_lines_empty[vcy] = FALSE; + + /* if we continued writing past the line, wrap to next line. + However, next term_move() really shouldn't try to cache + the move, otherwise terminals would try to combine the + last word in upper line with first word in lower line. */ + cforcemove = TRUE; + vcx += count; + while (vcx >= term_width) { + vcx -= term_width; + if (vcy < term_height) vcy++; + if (vcx > 0) term_lines_empty[vcy] = FALSE; + } +} + +void term_addch(TERM_WINDOW *window, int chr) +{ + if (term_detached) return; + + if (vcmove) term_move_real(); + term_printed_text(1); + if (vcy != term_height || vcx != 0) + putc(chr, window->term->out); +} + +void term_addstr(TERM_WINDOW *window, const char *str) +{ + int len; + + if (term_detached) return; + + if (vcmove) term_move_real(); + len = strlen(str); + term_printed_text(len); + + if (vcy != term_height || vcx != 0) + fputs(str, window->term->out); + else + fwrite(str, 1, len-1, window->term->out); +} + +void term_clrtoeol(TERM_WINDOW *window) +{ + if (term_detached) return; + + /* clrtoeol() doesn't necessarily understand colors */ + if (last_fg == -1 && last_bg == -1 && + (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE)) == 0) { + if (!term_lines_empty[vcy]) { + if (vcmove) term_move_real(); + terminfo_clrtoeol(); + if (vcx == 0) term_lines_empty[vcy] = TRUE; + } + } else if (vcx < term_width) { + /* we'll need to fill the line ourself. */ + if (vcmove) term_move_real(); + terminfo_repeat(' ', term_width-vcx); + terminfo_move(vcx, vcy); + term_lines_empty[vcy] = FALSE; + } +} + +void term_move_cursor(int x, int y) +{ + curs_x = x; + curs_y = y; +} + +void term_refresh(TERM_WINDOW *window) +{ + if (term_detached || freeze_counter > 0) + return; + + term_move(root_window, curs_x, curs_y); + term_move_real(); + + if (!curs_visible) { + terminfo_set_cursor_visible(TRUE); + curs_visible = TRUE; + } + term_set_color(window, ATTR_RESET); + fflush(window != NULL ? window->term->out : current_term->out); +} + +void term_refresh_freeze(void) +{ + freeze_counter++; + + if (!term_detached && curs_visible) { + terminfo_set_cursor_visible(FALSE); + curs_visible = FALSE; + } +} + +void term_refresh_thaw(void) +{ + if (--freeze_counter == 0) + term_refresh(NULL); +} + +void term_auto_detach(int set) +{ + auto_detach = set; +} + +void term_detach(void) +{ + terminfo_stop(current_term); + + fclose(current_term->in); + fclose(current_term->out); + + current_term->in = NULL; + current_term->out = NULL; + term_detached = TRUE; +} + +void term_attach(FILE *in, FILE *out) +{ + current_term->in = in; + current_term->out = out; + term_detached = FALSE; + + terminfo_cont(current_term); + irssi_redraw(); +} + +void term_stop(void) +{ + if (term_detached) { + kill(getpid(), SIGSTOP); + } else { + terminfo_stop(current_term); + kill(getpid(), SIGSTOP); + terminfo_cont(current_term); + irssi_redraw(); + } +} + +int term_gets(unsigned char *buffer, int size) +{ + int ret; + + if (term_detached) + return 0; + + /* fread() doesn't work */ + ret = read(fileno(current_term->in), buffer, size); + if (ret == 0) { + /* EOF - terminal got lost */ + if (auto_detach) + term_detach(); + ret = -1; + } else if (ret == -1 && (errno == EINTR || errno == EAGAIN)) + ret = 0; + + return ret; +} diff --git a/apps/irssi/src/fe-text/term.c b/apps/irssi/src/fe-text/term.c new file mode 100644 index 00000000..c32b2491 --- /dev/null +++ b/apps/irssi/src/fe-text/term.c @@ -0,0 +1,141 @@ +/* + term.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "settings.h" + +#include "term.h" +#include "mainwindows.h" + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#include +#include + +#define MIN_SCREEN_WIDTH 20 + +int term_use_colors; + +static int force_colors; +static int resize_dirty; + +/* Resize the terminal if needed */ +void term_resize_dirty(void) +{ +#ifdef TIOCGWINSZ + struct winsize ws; +#endif + int width, height; + + if (!resize_dirty) + return; + + resize_dirty = FALSE; + +#ifdef TIOCGWINSZ + /* Get new window size */ + if (ioctl(0, TIOCGWINSZ, &ws) < 0) + return; + + if (ws.ws_row == term_height && ws.ws_col == term_width) { + /* Same size, abort. */ + return; + } + + if (ws.ws_col < MIN_SCREEN_WIDTH) + ws.ws_col = MIN_SCREEN_WIDTH; + + width = ws.ws_col; + height = ws.ws_row; +#else + width = height = -1; +#endif + term_resize(width, height); + mainwindows_resize(term_width, term_height); + term_resize_final(width, height); +} + +#ifdef SIGWINCH +static void sig_winch(int p) +{ + irssi_set_dirty(); + resize_dirty = TRUE; +} +#endif + +static void cmd_resize(void) +{ + resize_dirty = TRUE; + term_resize_dirty(); +} + +static void read_settings(void) +{ + int old_colors = term_use_colors; + + term_auto_detach(settings_get_bool("term_auto_detach")); + + if (force_colors != settings_get_bool("term_force_colors")) { + force_colors = settings_get_bool("term_force_colors"); + term_force_colors(force_colors); + } + + term_use_colors = settings_get_bool("colors") && + (force_colors || term_has_colors()); + + if (term_use_colors != old_colors) + irssi_redraw(); +} + +void term_common_init(void) +{ +#ifdef SIGWINCH + struct sigaction act; +#endif + settings_add_bool("lookandfeel", "colors", TRUE); + settings_add_bool("lookandfeel", "term_force_colors", FALSE); + settings_add_bool("lookandfeel", "term_auto_detach", FALSE); + settings_add_bool("lookandfeel", "term_utf8", FALSE); + + force_colors = FALSE; + term_use_colors = term_has_colors() && settings_get_bool("colors"); + read_settings(); + + signal_add("beep", (SIGNAL_FUNC) term_beep); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + command_bind("resize", NULL, (SIGNAL_FUNC) cmd_resize); + +#ifdef SIGWINCH + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = sig_winch; + sigaction(SIGWINCH, &act, NULL); +#endif +} + +void term_common_deinit(void) +{ + command_unbind("resize", (SIGNAL_FUNC) cmd_resize); + signal_remove("beep", (SIGNAL_FUNC) term_beep); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/apps/irssi/src/fe-text/term.h b/apps/irssi/src/fe-text/term.h new file mode 100644 index 00000000..9cd1b153 --- /dev/null +++ b/apps/irssi/src/fe-text/term.h @@ -0,0 +1,87 @@ +#ifndef __TERM_H +#define __TERM_H + +typedef struct _TERM_WINDOW TERM_WINDOW; + +#define ATTR_RESETFG 0x0100 +#define ATTR_RESETBG 0x0200 +#define ATTR_BOLD 0x0400 +#define ATTR_BLINK 0x0800 +#define ATTR_UNDERLINE 0x1000 +#define ATTR_REVERSE 0x2000 + +#define ATTR_RESET (ATTR_RESETFG|ATTR_RESETBG) + +#define ATTR_NOCOLORS (ATTR_UNDERLINE|ATTR_REVERSE) + +#ifdef WANT_BIG5 +/* XXX I didn't check the encoding range of big5+. This is standard big5. */ +# define is_big5_los(lo) (((char)0x40<=lo)&&(lo<=(char)0x7E)) /* standard */ +# define is_big5_lox(lo) (((char)0x80<=lo)&&(lo<=(char)0xFE)) /* extended */ +# define is_big5_hi(hi) (((char)0x81<=hi)&&(hi<=(char)0xFE)) +# define is_big5(hi,lo) is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo)) +#endif + +extern TERM_WINDOW *root_window; +extern int term_width, term_height, term_use_colors, term_detached; + +/* Initialize / deinitialize terminal */ +int term_init(void); +void term_deinit(void); + +/* Resize terminal - if width or height is negative, + the new size is unknown and should be figured out somehow */ +void term_resize(int width, int height); +void term_resize_final(int width, int height); +/* Resize the terminal if needed */ +void term_resize_dirty(void); + +/* Returns TRUE if terminal has colors */ +int term_has_colors(void); +/* Force the colors on any way you can */ +void term_force_colors(int set); + +/* Clear screen */ +void term_clear(void); +/* Beep */ +void term_beep(void); + +/* Create a new window in terminal */ +TERM_WINDOW *term_window_create(int x, int y, int width, int height); +/* Destroy a terminal window */ +void term_window_destroy(TERM_WINDOW *window); + +/* Move/resize window */ +void term_window_move(TERM_WINDOW *window, int x, int y, + int width, int height); +/* Clear window */ +void term_window_clear(TERM_WINDOW *window); +/* Scroll window up/down */ +void term_window_scroll(TERM_WINDOW *window, int count); + +void term_set_color(TERM_WINDOW *window, int col); + +void term_move(TERM_WINDOW *window, int x, int y); +void term_addch(TERM_WINDOW *window, int chr); +void term_addstr(TERM_WINDOW *window, const char *str); +void term_clrtoeol(TERM_WINDOW *window); + +void term_move_cursor(int x, int y); + +void term_refresh_freeze(void); +void term_refresh_thaw(void); +void term_refresh(TERM_WINDOW *window); + +/* Automatically detach irssi when terminal is lost */ +void term_auto_detach(int set); +void term_detach(void); +void term_attach(FILE *in, FILE *out); + +void term_stop(void); +int term_gets(unsigned char *buffer, int size); + +/* internal */ +void term_common_init(void); +void term_common_deinit(void); + +#endif diff --git a/apps/irssi/src/fe-text/terminfo-core.c b/apps/irssi/src/fe-text/terminfo-core.c new file mode 100644 index 00000000..21a7b0a6 --- /dev/null +++ b/apps/irssi/src/fe-text/terminfo-core.c @@ -0,0 +1,658 @@ +#include "module.h" +#include "signals.h" +#include "terminfo-core.h" + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE 0 +#endif + +#define tput(s) tputs(s, 0, term_putchar) +inline static int term_putchar(int c) +{ + return fputc(c, current_term->out); +} + +/* Don't bother including curses.h because of these - + they might not even be defined there */ +char *tparm(); +int tputs(); + +#ifdef HAVE_TERMINFO +int setupterm(); +char *tigetstr(); +int tigetnum(); +int tigetflag(); +#define term_getstr(x, buffer) tigetstr(x.ti_name) +#define term_getnum(x) tigetnum(x.ti_name); +#define term_getflag(x) tigetflag(x.ti_name); +#else +int tgetent(); +char *tgetstr(); +int tgetnum(); +int tgetflag(); +#define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer) +#define term_getnum(x) tgetnum(x.tc_name) +#define term_getflag(x) tgetflag(x.tc_name) +#endif + +#define CAP_TYPE_FLAG 0 +#define CAP_TYPE_INT 1 +#define CAP_TYPE_STR 2 + +typedef struct { + const char *ti_name; /* terminfo name */ + const char *tc_name; /* termcap name */ + int type; + void *ptr; +} TERMINFO_REC; + +TERM_REC *current_term; +static TERM_REC temp_term; /* not really used for anything */ + +/* Define only what we might need */ +static TERMINFO_REC tcaps[] = { + /* Terminal size */ + { "cols", "co", CAP_TYPE_INT, &temp_term.width }, + { "lines", "li", CAP_TYPE_INT, &temp_term.height }, + + /* Cursor movement */ + { "smcup", "ti", CAP_TYPE_STR, &temp_term.TI_smcup }, + { "rmcup", "te", CAP_TYPE_STR, &temp_term.TI_rmcup }, + { "cup", "cm", CAP_TYPE_STR, &temp_term.TI_cup }, + { "hpa", "ch", CAP_TYPE_STR, &temp_term.TI_hpa }, + { "vpa", "vh", CAP_TYPE_STR, &temp_term.TI_vpa }, + { "cub1", "le", CAP_TYPE_STR, &temp_term.TI_cub1 }, + { "cuf1", "nd", CAP_TYPE_STR, &temp_term.TI_cuf1 }, + { "civis", "vi", CAP_TYPE_STR, &temp_term.TI_civis }, + { "cnorm", "ve", CAP_TYPE_STR, &temp_term.TI_cnorm }, + + /* Scrolling */ + { "csr", "cs", CAP_TYPE_STR, &temp_term.TI_csr }, + { "wind", "wi", CAP_TYPE_STR, &temp_term.TI_wind }, + { "ri", "sr", CAP_TYPE_STR, &temp_term.TI_ri }, + { "rin", "SR", CAP_TYPE_STR, &temp_term.TI_rin }, + { "ind", "sf", CAP_TYPE_STR, &temp_term.TI_ind }, + { "indn", "SF", CAP_TYPE_STR, &temp_term.TI_indn }, + { "il", "AL", CAP_TYPE_STR, &temp_term.TI_il }, + { "il1", "al", CAP_TYPE_STR, &temp_term.TI_il1 }, + { "dl", "DL", CAP_TYPE_STR, &temp_term.TI_dl }, + { "dl1", "dl", CAP_TYPE_STR, &temp_term.TI_dl1 }, + + /* Clearing screen */ + { "clear", "cl", CAP_TYPE_STR, &temp_term.TI_clear }, + { "ed", "cd", CAP_TYPE_STR, &temp_term.TI_ed }, + + /* Clearing to end of line */ + { "el", "ce", CAP_TYPE_STR, &temp_term.TI_el }, + + /* Repeating character */ + { "rep", "rp", CAP_TYPE_STR, &temp_term.TI_rep }, + + /* Colors */ + { "sgr0", "me", CAP_TYPE_STR, &temp_term.TI_sgr0 }, + { "smul", "us", CAP_TYPE_STR, &temp_term.TI_smul }, + { "rmul", "ue", CAP_TYPE_STR, &temp_term.TI_rmul }, + { "smso", "so", CAP_TYPE_STR, &temp_term.TI_smso }, + { "rmso", "se", CAP_TYPE_STR, &temp_term.TI_rmso }, + { "bold", "md", CAP_TYPE_STR, &temp_term.TI_bold }, + { "blink", "mb", CAP_TYPE_STR, &temp_term.TI_blink }, + { "setaf", "AF", CAP_TYPE_STR, &temp_term.TI_setaf }, + { "setab", "AB", CAP_TYPE_STR, &temp_term.TI_setab }, + { "setf", "Sf", CAP_TYPE_STR, &temp_term.TI_setf }, + { "setb", "Sb", CAP_TYPE_STR, &temp_term.TI_setb }, + + /* Beep */ + { "bel", "bl", CAP_TYPE_STR, &temp_term.TI_bel }, +}; + +/* Move cursor (cursor_address / cup) */ +static void _move_cup(TERM_REC *term, int x, int y) +{ + tput(tparm(term->TI_cup, y, x)); +} + +/* Move cursor (column_address+row_address / hpa+vpa) */ +static void _move_pa(TERM_REC *term, int x, int y) +{ + tput(tparm(term->TI_hpa, x)); + tput(tparm(term->TI_vpa, y)); +} + +/* Move cursor from a known position */ +static void _move_relative(TERM_REC *term, int oldx, int oldy, int x, int y) +{ + if (oldx == 0 && x == 0 && y == oldy+1) { + /* move to beginning of next line - + hope this works everywhere */ + tput("\r\n"); + return; + } + + if (oldx > 0 && y == oldy) { + /* move cursor left/right */ + if (x == oldx-1 && term->TI_cub1) { + tput(tparm(term->TI_cub1)); + return; + } + if (x == oldx+1 && y == oldy && term->TI_cuf1) { + tput(tparm(term->TI_cuf1)); + return; + } + } + + /* fallback to absolute positioning */ + if (term->TI_cup) { + tput(tparm(term->TI_cup, y, x)); + return; + } + + if (oldy != y) + tput(tparm(term->TI_vpa, y)); + if (oldx != x) + tput(tparm(term->TI_hpa, x)); +} + +/* Set cursor visible/invisible */ +static void _set_cursor_visible(TERM_REC *term, int set) +{ + tput(tparm(set ? term->TI_cnorm : term->TI_civis)); +} + +#define scroll_region_setup(term, y1, y2) \ + if ((term)->TI_csr != NULL) \ + tput(tparm((term)->TI_csr, y1, y2)); \ + else if ((term)->TI_wind != NULL) \ + tput(tparm((term)->TI_wind, y1, y2, 0, (term)->width-1)); + +/* Scroll (change_scroll_region+parm_rindex+parm_index / csr+rin+indn) */ +static void _scroll_region(TERM_REC *term, int y1, int y2, int count) +{ + /* setup the scrolling region to wanted area */ + scroll_region_setup(term, y1, y2); + + term->move(term, 0, y1); + if (count > 0) { + term->move(term, 0, y2); + tput(tparm(term->TI_indn, count, count)); + } else if (count < 0) { + term->move(term, 0, y1); + tput(tparm(term->TI_rin, -count, -count)); + } + + /* reset the scrolling region to full screen */ + scroll_region_setup(term, 0, term->height-1); +} + +/* Scroll (change_scroll_region+scroll_reverse+scroll_forward / csr+ri+ind) */ +static void _scroll_region_1(TERM_REC *term, int y1, int y2, int count) +{ + int i; + + /* setup the scrolling region to wanted area */ + scroll_region_setup(term, y1, y2); + + if (count > 0) { + term->move(term, 0, y2); + for (i = 0; i < count; i++) + tput(tparm(term->TI_ind)); + } else if (count < 0) { + term->move(term, 0, y1); + for (i = count; i < 0; i++) + tput(tparm(term->TI_ri)); + tput(tparm(term->TI_rin, -count, -count)); + } + + /* reset the scrolling region to full screen */ + scroll_region_setup(term, 0, term->height-1); +} + +/* Scroll (parm_insert_line+parm_delete_line / il+dl) */ +static void _scroll_line(TERM_REC *term, int y1, int y2, int count) +{ + /* setup the scrolling region to wanted area - + this might not necessarily work with il/dl, but at least it + looks better if it does */ + scroll_region_setup(term, y1, y2); + + if (count > 0) { + term->move(term, 0, y1); + tput(tparm(term->TI_dl, count, count)); + term->move(term, 0, y2-count+1); + tput(tparm(term->TI_il, count, count)); + } else if (count < 0) { + term->move(term, 0, y2+count+1); + tput(tparm(term->TI_dl, -count, -count)); + term->move(term, 0, y1); + tput(tparm(term->TI_il, -count, -count)); + } + + /* reset the scrolling region to full screen */ + scroll_region_setup(term, 0, term->height-1); +} + +/* Scroll (insert_line+delete_line / il1+dl1) */ +static void _scroll_line_1(TERM_REC *term, int y1, int y2, int count) +{ + int i; + + if (count > 0) { + term->move(term, 0, y1); + for (i = 0; i < count; i++) + tput(tparm(term->TI_dl1)); + term->move(term, 0, y2-count+1); + for (i = 0; i < count; i++) + tput(tparm(term->TI_il1)); + } else if (count < 0) { + term->move(term, 0, y2+count+1); + for (i = count; i < 0; i++) + tput(tparm(term->TI_dl1)); + term->move(term, 0, y1); + for (i = count; i < 0; i++) + tput(tparm(term->TI_il1)); + } +} + +/* Clear screen (clear_screen / clear) */ +static void _clear_screen(TERM_REC *term) +{ + tput(tparm(term->TI_clear)); +} + +/* Clear screen (clr_eos / ed) */ +static void _clear_eos(TERM_REC *term) +{ + term->move(term, 0, 0); + tput(tparm(term->TI_ed)); +} + +/* Clear screen (parm_delete_line / dl) */ +static void _clear_del(TERM_REC *term) +{ + term->move(term, 0, 0); + tput(tparm(term->TI_dl, term->height, term->height)); +} + +/* Clear screen (delete_line / dl1) */ +static void _clear_del_1(TERM_REC *term) +{ + int i; + + term->move(term, 0, 0); + for (i = 0; i < term->height; i++) + tput(tparm(term->TI_dl1)); +} + +/* Clear to end of line (clr_eol / el) */ +static void _clrtoeol(TERM_REC *term) +{ + tput(tparm(term->TI_el)); +} + +/* Repeat character (rep / rp) */ +static void _repeat(TERM_REC *term, int chr, int count) +{ + tput(tparm(term->TI_rep, chr, count)); +} + +/* Repeat character (manual) */ +static void _repeat_manual(TERM_REC *term, int chr, int count) +{ + while (count > 0) { + putc(chr, term->out); + count--; + } +} + +/* Reset all terminal attributes */ +static void _set_normal(TERM_REC *term) +{ + tput(tparm(term->TI_normal)); +} + +/* Bold on */ +static void _set_bold(TERM_REC *term) +{ + tput(tparm(term->TI_bold)); +} + +/* Underline on/off */ +static void _set_uline(TERM_REC *term, int set) +{ + tput(tparm(set ? term->TI_smul : term->TI_rmul)); +} + +/* Standout on/off */ +static void _set_standout(TERM_REC *term, int set) +{ + tput(tparm(set ? term->TI_smso : term->TI_rmso)); +} + +/* Change foreground color */ +static void _set_fg(TERM_REC *term, int color) +{ + tput(tparm(term->TI_fg[color & 0x0f])); +} + +/* Change background color */ +static void _set_bg(TERM_REC *term, int color) +{ + tput(tparm(term->TI_bg[color & 0x0f])); +} + +/* Beep */ +static void _beep(TERM_REC *term) +{ + tput(tparm(term->TI_bel)); +} + +static void _ignore(TERM_REC *term) +{ +} + +static void _ignore_parm(TERM_REC *term, int param) +{ +} + +static void term_fill_capabilities(TERM_REC *term) +{ + int i, ival; + char *sval; + void *ptr; + +#ifndef HAVE_TERMINFO + char *tptr = term->buffer2; +#endif + for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) { + ptr = (char *) term + (int) ((char *) tcaps[i].ptr - (char *) &temp_term); + + switch (tcaps[i].type) { + case CAP_TYPE_FLAG: + ival = term_getflag(tcaps[i]); + *(int *)ptr = ival; + break; + case CAP_TYPE_INT: + ival = term_getnum(tcaps[i]); + *(int *)ptr = ival; + break; + case CAP_TYPE_STR: + sval = term_getstr(tcaps[i], tptr); + if (sval == (char *) -1) + *(char **)ptr = NULL; + else + *(char **)ptr = sval; + break; + } + } +} + +/* Terminal was resized - ask the width/height from terminfo again */ +void terminfo_resize(TERM_REC *term) +{ + /* FIXME: is this possible? */ +} + +static void terminfo_colors_deinit(TERM_REC *term) +{ + int i; + + if (terminfo_is_colors_set(term)) { + for (i = 0; i < 16; i++) { + g_free(term->TI_fg[i]); + g_free(term->TI_bg[i]); + } + + memset(term->TI_fg, 0, sizeof(term->TI_fg)); + memset(term->TI_bg, 0, sizeof(term->TI_fg)); + } +} + +/* Setup colors - if force is set, use ANSI-style colors if + terminal capabilities don't contain color codes */ +void terminfo_setup_colors(TERM_REC *term, int force) +{ + static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + const char *bold, *blink; + int i; + + terminfo_colors_deinit(term); + term->has_colors = term->TI_setf || term->TI_setaf; + + if (term->TI_setf) { + for (i = 0; i < 8; i++) + term->TI_fg[i] = g_strdup(tparm(term->TI_setf, i, 0)); + } else if (term->TI_setaf) { + for (i = 0; i < 8; i++) + term->TI_fg[i] = g_strdup(tparm(term->TI_setaf, ansitab[i], 0)); + } else if (force) { + for (i = 0; i < 8; i++) + term->TI_fg[i] = g_strdup_printf("\033[%dm", 30+ansitab[i]); + } + + if (term->TI_setb) { + for (i = 0; i < 8; i++) + term->TI_bg[i] = g_strdup(tparm(term->TI_setb, i, 0)); + } else if (term->TI_setab) { + for (i = 0; i < 8; i++) + term->TI_bg[i] = g_strdup(tparm(term->TI_setab, ansitab[i], 0)); + } else if (force) { + for (i = 0; i < 8; i++) + term->TI_bg[i] = g_strdup_printf("\033[%dm", 40+ansitab[i]); + } + + if (term->TI_setf || term->TI_setaf || force) { + term->set_fg = _set_fg; + term->set_bg = _set_bg; + + /* bold fg, blink bg */ + bold = term->TI_bold ? term->TI_bold : ""; + for (i = 0; i < 8; i++) + term->TI_fg[i+8] = g_strconcat(bold, term->TI_fg[i], NULL); + + blink = term->TI_blink ? term->TI_blink : ""; + for (i = 0; i < 8; i++) + term->TI_bg[i+8] = g_strconcat(blink, term->TI_bg[i], NULL); + } else { + /* no colors */ + term->set_fg = term->set_bg = _ignore_parm; + } +} + +static void terminfo_input_init(TERM_REC *term) +{ + tcgetattr(fileno(term->in), &term->old_tio); + memcpy(&term->tio, &term->old_tio, sizeof(term->tio)); + + term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */ + term->tio.c_cc[VMIN] = 1; /* read() is satisfied after 1 char */ + term->tio.c_cc[VTIME] = 0; /* No timer */ + + /* Disable INTR, QUIT, VDSUSP and SUSP keys */ + term->tio.c_cc[VINTR] = _POSIX_VDISABLE; + term->tio.c_cc[VQUIT] = _POSIX_VDISABLE; +#ifdef VDSUSP + term->tio.c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif +#ifdef VSUSP + term->tio.c_cc[VSUSP] = _POSIX_VDISABLE; +#endif + + tcsetattr(fileno(term->in), TCSADRAIN, &term->tio); + +} + +static void terminfo_input_deinit(TERM_REC *term) +{ + tcsetattr(fileno(term->in), TCSADRAIN, &term->old_tio); +} + +void terminfo_cont(TERM_REC *term) +{ + if (term->TI_smcup) + tput(tparm(term->TI_smcup)); + terminfo_input_init(term); +} + +void terminfo_stop(TERM_REC *term) +{ + /* reset colors */ + terminfo_set_normal(); + /* move cursor to bottom of the screen */ + terminfo_move(0, term->height-1); + + /* stop cup-mode */ + if (term->TI_rmcup) + tput(tparm(term->TI_rmcup)); + + /* reset input settings */ + terminfo_input_deinit(term); + fflush(term->out); +} + +static int term_setup(TERM_REC *term) +{ + GString *str; +#ifdef HAVE_TERMINFO + int err; +#endif + char *term_env; + + term_env = getenv("TERM"); + if (term_env == NULL) { + fprintf(term->out, "TERM environment not set\n"); + return 0; + } + +#ifdef HAVE_TERMINFO + if (setupterm(term_env, 1, &err) != 0) { + fprintf(term->out, "setupterm() failed for TERM=%s: %d\n", term_env, err); + return 0; + } +#else + if (tgetent(term->buffer1, term_env) < 1) + { + fprintf(term->out, "Termcap not found for TERM=%s\n", term_env); + return -1; + } +#endif + + term_fill_capabilities(term); + + /* Cursor movement */ + if (term->TI_cup) + term->move = _move_cup; + else if (term->TI_hpa && term->TI_vpa) + term->move = _move_pa; + else { + fprintf(term->out, "Terminal doesn't support cursor movement\n"); + return 0; + } + term->move_relative = _move_relative; + term->set_cursor_visible = term->TI_civis && term->TI_cnorm ? + _set_cursor_visible : _ignore_parm; + + /* Scrolling */ + if ((term->TI_csr || term->TI_wind) && term->TI_rin && term->TI_indn) + term->scroll = _scroll_region; + else if (term->TI_il && term->TI_dl) + term->scroll = _scroll_line; + else if ((term->TI_csr || term->TI_wind) && term->TI_ri && term->TI_ind) + term->scroll = _scroll_region_1; + else if (term->scroll == NULL && (term->TI_il1 && term->TI_dl1)) + term->scroll = _scroll_line_1; + else if (term->scroll == NULL) { + fprintf(term->out, "Terminal doesn't support scrolling\n"); + return 0; + } + + /* Clearing screen */ + if (term->TI_clear) + term->clear = _clear_screen; + else if (term->TI_ed) + term->clear = _clear_eos; + else if (term->TI_dl) + term->clear = _clear_del; + else if (term->TI_dl1) + term->clear = _clear_del_1; + else { + /* we could do this by line inserts as well, but don't + bother - if some terminal has insert line it most probably + has delete line as well, if not a regular clear screen */ + fprintf(term->out, "Terminal doesn't support clearing screen\n"); + return 0; + } + + /* Clearing to end of line */ + if (term->TI_el) + term->clrtoeol = _clrtoeol; + else { + fprintf(term->out, "Terminal doesn't support clearing to end of line\n"); + return 0; + } + + /* Repeating character */ + if (term->TI_rep) + term->repeat = _repeat; + else + term->repeat = _repeat_manual; + + /* Bold, underline, standout */ + term->set_bold = term->TI_bold ? _set_bold : _ignore; + term->set_uline = term->TI_smul && term->TI_rmul ? + _set_uline : _ignore_parm; + term->set_standout = term->TI_smso && term->TI_rmso ? + _set_standout : _ignore_parm; + + /* Create a string to set all attributes off */ + str = g_string_new(NULL); + if (term->TI_sgr0) + g_string_append(str, term->TI_sgr0); + if (term->TI_rmul && (term->TI_sgr0 == NULL || strcmp(term->TI_rmul, term->TI_sgr0) != 0)) + g_string_append(str, term->TI_rmul); + if (term->TI_rmso && (term->TI_sgr0 == NULL || strcmp(term->TI_rmso, term->TI_sgr0) != 0)) + g_string_append(str, term->TI_rmso); + term->TI_normal = str->str; + g_string_free(str, FALSE); + term->set_normal = _set_normal; + + term->beep = term->TI_bel ? _beep : _ignore; + + terminfo_setup_colors(term, FALSE); + terminfo_cont(term); + return 1; +} + +TERM_REC *terminfo_core_init(FILE *in, FILE *out) +{ + TERM_REC *old_term, *term; + + old_term = current_term; + current_term = term = g_new0(TERM_REC, 1); + + term->in = in; + term->out = out; + + if (!term_setup(term)) { + g_free(term); + term = NULL; + } + + current_term = old_term; + return term; +} + +void terminfo_core_deinit(TERM_REC *term) +{ + TERM_REC *old_term; + + old_term = current_term; + current_term = term; + term->set_normal(term); + current_term = old_term; + + terminfo_stop(term); + + g_free(term->TI_normal); + terminfo_colors_deinit(term); + + g_free(term); +} diff --git a/apps/irssi/src/fe-text/terminfo-core.h b/apps/irssi/src/fe-text/terminfo-core.h new file mode 100644 index 00000000..93afa78b --- /dev/null +++ b/apps/irssi/src/fe-text/terminfo-core.h @@ -0,0 +1,102 @@ +#ifndef __TERMINFO_CORE_H +#define __TERMINFO_CORE_H + +#include + +#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 diff --git a/apps/irssi/src/fe-text/textbuffer-commands.c b/apps/irssi/src/fe-text/textbuffer-commands.c index 92ec70bd..db5bc782 100644 --- a/apps/irssi/src/fe-text/textbuffer-commands.c +++ b/apps/irssi/src/fe-text/textbuffer-commands.c @@ -19,13 +19,17 @@ */ #include "module.h" +#include "module-formats.h" #include "signals.h" #include "commands.h" #include "misc.h" #include "levels.h" +#include "settings.h" +#include "servers.h" #include "printtext.h" #include "gui-windows.h" +#include "textbuffer-reformat.h" /* SYNTAX: CLEAR */ static void cmd_clear(const char *data) @@ -54,6 +58,32 @@ static void cmd_clear(const char *data) cmd_params_free(free_arg); } +static void cmd_window_scroll(const char *data) +{ + GUI_WINDOW_REC *gui; + + gui = WINDOW_GUI(active_win); + if (g_strcasecmp(data, "default") == 0) { + gui->use_scroll = FALSE; + } else if (g_strcasecmp(data, "on") == 0) { + gui->use_scroll = TRUE; + gui->scroll = TRUE; + } else if (g_strcasecmp(data, "off") == 0) { + gui->use_scroll = TRUE; + gui->scroll = FALSE; + } else if (*data != '\0') { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_WINDOW_SCROLL_UNKNOWN, data); + return; + } + + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_SCROLL, !gui->use_scroll ? "DEFAULT" : + gui->scroll ? "ON" : "OFF"); + textbuffer_view_set_scroll(gui->view, gui->use_scroll ? + gui->scroll : settings_get_bool("scroll")); +} + static void cmd_scrollback(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { @@ -74,13 +104,13 @@ static void scrollback_goto_line(int linenum) if (view->buffer->lines_count == 0) return; - textbuffer_view_scroll_line(view, view->buffer->lines->data); + textbuffer_view_scroll_line(view, view->buffer->first_line); gui_window_scroll(active_win, linenum); } static void scrollback_goto_time(const char *datearg, const char *timearg) { - GList *tmp; + LINE_REC *line; struct tm tm; time_t now, stamp; int day, month; @@ -145,12 +175,10 @@ static void scrollback_goto_time(const char *datearg, const char *timearg) } /* scroll to first line after timestamp */ - tmp = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view); - for (; tmp != NULL; tmp = tmp->next) { - LINE_REC *rec = tmp->data; - - if (rec->info.time >= stamp) { - gui_window_scroll_line(active_win, rec); + line = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view); + for (; line != NULL; line = line->next) { + if (line->info.time >= stamp) { + gui_window_scroll_line(active_win, line); break; } } @@ -188,7 +216,7 @@ static void cmd_scrollback_home(const char *data) buffer = WINDOW_GUI(active_win)->view->buffer; if (buffer->lines_count > 0) - gui_window_scroll_line(active_win, buffer->lines->data); + gui_window_scroll_line(active_win, buffer->first_line); } /* SYNTAX: SCROLLBACK END */ @@ -200,28 +228,28 @@ static void cmd_scrollback_end(const char *data) if (view->bottom_startline == NULL) return; - textbuffer_view_scroll_line(view, view->bottom_startline->data); + textbuffer_view_scroll_line(view, view->bottom_startline); gui_window_scroll(active_win, view->bottom_subline); } /* SYNTAX: SCROLLBACK REDRAW */ static void cmd_scrollback_redraw(void) { -#if 0 GUI_WINDOW_REC *gui; - GList *tmp, *next; + LINE_REC *line, *next; gui = WINDOW_GUI(active_win); - screen_refresh_freeze(); - for (tmp = gui->lines; tmp != NULL; tmp = next) { - next = tmp->next; - gui_window_reformat_line(active_win, tmp->data); + term_refresh_freeze(); + line = textbuffer_view_get_lines(gui->view); + while (line != NULL) { + next = line->next; + textbuffer_reformat_line(active_win, line); + line = next; } gui_window_redraw(active_win); - screen_refresh_thaw(); -#endif + term_refresh_thaw(); } static void cmd_scrollback_status(void) @@ -269,6 +297,7 @@ static void sig_away_changed(SERVER_REC *server) void textbuffer_commands_init(void) { command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear); + command_bind("window scroll", NULL, (SIGNAL_FUNC) cmd_window_scroll); command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback); command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear); command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto); @@ -285,6 +314,7 @@ void textbuffer_commands_init(void) void textbuffer_commands_deinit(void) { command_unbind("clear", (SIGNAL_FUNC) cmd_clear); + command_unbind("window scroll", (SIGNAL_FUNC) cmd_window_scroll); command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback); command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear); command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto); diff --git a/apps/irssi/src/fe-text/textbuffer-reformat.c b/apps/irssi/src/fe-text/textbuffer-reformat.c new file mode 100644 index 00000000..da75a5a8 --- /dev/null +++ b/apps/irssi/src/fe-text/textbuffer-reformat.c @@ -0,0 +1,280 @@ +/* + textbuffer-reformat.c : Reformatting lines in text buffer + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "settings.h" + +#include "formats.h" + +#include "gui-windows.h" +#include "gui-printtext.h" +#include "textbuffer.h" + +static GString *format; +static int scrollback_save_formats; + +/* Read one block between \0s */ +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 */ + break; + } + if ((*text)[1] == LINE_CMD_FORMAT_CONT) { + /* leave text at \0 */ + break; + } + (*text)++; + + if (**text == LINE_CMD_FORMAT) { + /* move text to start after \0 */ + (*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); + + /* */ + g_string_truncate(format, 0); + + g_string_append_c(format, '\0'); + g_string_append_c(format, (char)LINE_CMD_FORMAT); + + g_string_append(format, module); + + g_string_append_c(format, '\0'); + g_string_append_c(format, (char)LINE_CMD_FORMAT); + + g_string_append(format, formats[formatnum].tag); + + for (n = 0; n < formats[formatnum].params; n++) { + g_string_append_c(format, '\0'); + g_string_append_c(format, (char)LINE_CMD_FORMAT); + + if (args[n] != NULL) + g_string_append(format, args[n]); + } +} + +static void sig_gui_printtext_finished(WINDOW_REC *window) +{ + GUI_WINDOW_REC *gui; + LINE_REC *insert_after; + + if (format->len == 0) + return; + + /* save format of the line */ + gui = WINDOW_GUI(window); + insert_after = gui->use_insert_after ? + gui->insert_after : gui->view->buffer->cur_line; + + textbuffer_insert(gui->view->buffer, insert_after, + (unsigned char *) format->str, + format->len, NULL); + + g_string_truncate(format, 0); +} + +static void read_settings(void) +{ + scrollback_save_formats = settings_get_bool("scrollback_save_formats"); +} + +void textbuffer_reformat_init(void) +{ + format = g_string_new(NULL); + settings_add_bool("history", "scrollback_save_formats", FALSE); + + read_settings(); + signal_add("print format", (SIGNAL_FUNC) sig_print_format); + signal_add_first("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void textbuffer_reformat_deinit(void) +{ + g_string_free(format, TRUE); + + signal_remove("print format", (SIGNAL_FUNC) sig_print_format); + signal_remove("print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/apps/irssi/src/fe-text/textbuffer-reformat.h b/apps/irssi/src/fe-text/textbuffer-reformat.h new file mode 100644 index 00000000..2818ec68 --- /dev/null +++ b/apps/irssi/src/fe-text/textbuffer-reformat.h @@ -0,0 +1,9 @@ +#ifndef __TEXTBUFFER_REFORMAT_H +#define __TEXTBUFFER_REFORMAT_H + +void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line); + +void textbuffer_reformat_init(void); +void textbuffer_reformat_deinit(void); + +#endif diff --git a/apps/irssi/src/fe-text/textbuffer-view.c b/apps/irssi/src/fe-text/textbuffer-view.c index 449f20a9..a5d7f2ed 100644 --- a/apps/irssi/src/fe-text/textbuffer-view.c +++ b/apps/irssi/src/fe-text/textbuffer-view.c @@ -20,7 +20,7 @@ #include "module.h" #include "textbuffer-view.h" -#include "screen.h" +#include "utf8.h" typedef struct { char *name; @@ -102,21 +102,64 @@ static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache) textbuffer_cache_destroy(cache); } +#define FGATTR (ATTR_NOCOLORS | ATTR_RESETFG | ATTR_BOLD | 0x0f) +#define BGATTR (ATTR_NOCOLORS | ATTR_RESETBG | ATTR_BLINK | 0xf0) + +static void update_cmd_color(unsigned char cmd, int *color) +{ + if ((cmd & 0x80) == 0) { + if (cmd & LINE_COLOR_BG) { + /* set background color */ + *color &= FGATTR; + if ((cmd & LINE_COLOR_DEFAULT) == 0) + *color |= (cmd & 0x0f) << 4; + else { + *color |= ATTR_RESETBG; + if (cmd & LINE_COLOR_BLINK) + *color |= ATTR_BLINK; + } + } else { + /* set foreground color */ + *color &= BGATTR; + if ((cmd & LINE_COLOR_DEFAULT) == 0) + *color |= cmd & 0x0f; + else { + *color |= ATTR_RESETFG; + if (cmd & LINE_COLOR_BOLD) + *color |= ATTR_BOLD; + } + } + } else switch (cmd) { + case LINE_CMD_UNDERLINE: + *color ^= ATTR_UNDERLINE; + break; + case LINE_CMD_REVERSE: + *color ^= ATTR_REVERSE; + break; + case LINE_CMD_COLOR0: + *color &= BGATTR; + break; + } +} + static LINE_CACHE_REC * view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) { + INDENT_FUNC indent_func; LINE_CACHE_REC *rec; LINE_CACHE_SUB_REC *sub; GSList *lines; unsigned char cmd; - char *ptr, *last_space_ptr; + const unsigned char *ptr, *last_space_ptr; int xpos, pos, indent_pos, last_space, last_color, color, linecount; g_return_val_if_fail(line->text != NULL, NULL); - xpos = 0; color = 0; indent_pos = view->default_indent; + color = ATTR_RESETFG | ATTR_RESETBG; + xpos = 0; indent_pos = view->default_indent; last_space = last_color = 0; last_space_ptr = NULL; sub = NULL; + indent_func = view->default_indent_func; linecount = 1; lines = NULL; for (ptr = line->text;;) { @@ -130,49 +173,38 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) break; if (cmd == LINE_CMD_CONTINUE) { - char *tmp; + unsigned char *tmp; memcpy(&tmp, ptr, sizeof(char *)); ptr = tmp; continue; } - if ((cmd & 0x80) == 0) { - /* set color */ - color = (color & ATTR_UNDERLINE) | cmd; - } else switch (cmd) { - case LINE_CMD_UNDERLINE: - color ^= ATTR_UNDERLINE; - break; - case LINE_CMD_COLOR0: - color = color & ATTR_UNDERLINE; - break; - case LINE_CMD_COLOR8: - color &= 0xfff0; - color |= 8|ATTR_COLOR8; - break; - case LINE_CMD_BLINK: - color |= 0x80; - break; - case LINE_CMD_INDENT: + if (cmd == LINE_CMD_INDENT) { /* set indentation position here - don't do it if we're too close to right border */ if (xpos < view->width-5) indent_pos = xpos; - break; - } + } else if (cmd == LINE_CMD_INDENT_FUNC) { + memcpy(&indent_func, ptr, sizeof(INDENT_FUNC)); + ptr += sizeof(INDENT_FUNC); + if (indent_func == NULL) + indent_func = view->default_indent_func; + } else + update_cmd_color(cmd, &color); continue; } if (xpos == view->width && sub != NULL && (last_space <= indent_pos || last_space <= 10) && - !view->longword_noindent) { + view->longword_noindent) { /* long word, remove the indentation from this line */ xpos -= sub->indent; sub->indent = 0; } if (xpos == view->width) { - xpos = indent_pos; + xpos = indent_func == NULL ? indent_pos : + indent_func(view, line, -1); sub = g_new0(LINE_CACHE_SUB_REC, 1); if (last_space > indent_pos && last_space > 10) { @@ -180,7 +212,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) color = last_color; ptr = last_space_ptr; while (*ptr == ' ') ptr++; - } else if (!view->longword_noindent) { + } else if (view->longword_noindent) { /* long word, no indentation in next line */ xpos = 0; sub->continues = TRUE; @@ -188,6 +220,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) sub->start = ptr; sub->indent = xpos; + sub->indent_func = indent_func; sub->color = color; lines = g_slist_append(lines, sub); @@ -197,6 +230,9 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) continue; } + if (view->utf8) + get_utf8_char(&ptr); + xpos++; if (*ptr++ == ' ') { last_space = xpos-1; @@ -224,23 +260,76 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) return rec; } +static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, + unsigned char update_counter) +{ + LINE_CACHE_REC *cache; + + if (view->cache->update_counter == update_counter) + return; + view->cache->update_counter = update_counter; + + cache = g_hash_table_lookup(view->cache->line_cache, line); + if (cache != NULL) { + g_free(cache); + g_hash_table_remove(view->cache->line_cache, line); + } +} + +static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, + unsigned char update_counter) +{ + view_remove_cache(view, line, update_counter); + + if (view->buffer->cur_line == line) + view->cache->last_linecount = view_get_linecount(view, line); +} + +static void view_reset_cache(TEXT_BUFFER_VIEW_REC *view) +{ + GSList *tmp; + + /* destroy line caches - note that you can't do simultaneously + unrefs + cache_get()s or it will keep using the old caches */ + textbuffer_cache_unref(view->cache); + g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL); + + view->cache = textbuffer_cache_get(view->siblings, view->width); + for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) { + TEXT_BUFFER_VIEW_REC *rec = tmp->data; + + rec->cache = textbuffer_cache_get(rec->siblings, rec->width); + } +} + static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, int subline, int ypos, int max) { + INDENT_FUNC indent_func; LINE_CACHE_REC *cache; const unsigned char *text, *text_newline; - char *tmp; - int xpos, color, drawcount, first; + unsigned char *tmp; + int xpos, color, drawcount, first, need_move, need_clrtoeol; + + if (view->dirty) /* don't bother drawing anything - redraw is coming */ + return 0; cache = textbuffer_view_get_line_cache(view, line); if (subline >= cache->count) return 0; - xpos = color = drawcount = 0; first = TRUE; + color = ATTR_RESET; + need_move = TRUE; need_clrtoeol = FALSE; + xpos = drawcount = 0; first = TRUE; text_newline = text = subline == 0 ? line->text : cache->lines[subline-1].start; for (;;) { if (text == text_newline) { + if (need_clrtoeol && xpos < term_width) { + term_set_color(view->window, ATTR_RESET); + term_clrtoeol(view->window); + } + if (first) first = FALSE; else { @@ -250,21 +339,37 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, } if (subline > 0) { - xpos = cache->lines[subline-1].indent; + /* continuing previous line - indent it */ + indent_func = cache->lines[subline-1].indent_func; + xpos = indent_func != NULL ? + indent_func(view, line, ypos) : + cache->lines[subline-1].indent; color = cache->lines[subline-1].color; } - set_color(view->window, 0); - wmove(view->window, ypos, 0); - wclrtoeol(view->window); + if (xpos == 0) + need_clrtoeol = TRUE; + else { + /* line was indented - need to clear the + indented area first */ + term_set_color(view->window, ATTR_RESET); + term_move(view->window, 0, ypos); + term_clrtoeol(view->window); + } - wmove(view->window, ypos, xpos); - set_color(view->window, color); + if (need_move || xpos > 0) + term_move(view->window, xpos, ypos); - /* get the beginning of the next subline */ - text_newline = subline == cache->count-1 ? NULL : - cache->lines[subline].start; + term_set_color(view->window, color); + if (subline == cache->count-1) { + text_newline = NULL; + need_move = FALSE; + } else { + /* get the beginning of the next subline */ + text_newline = cache->lines[subline].start; + need_move = !cache->lines[subline].continues; + } drawcount++; subline++; } @@ -275,43 +380,45 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT) break; - if ((*text & 0x80) == 0) { - /* set color */ - color = (color & ATTR_UNDERLINE) | *text; - } else if (*text == LINE_CMD_CONTINUE) { + if (*text == LINE_CMD_CONTINUE) { /* jump to next block */ memcpy(&tmp, text+1, sizeof(unsigned char *)); text = tmp; continue; - } else switch (*text) { - case LINE_CMD_UNDERLINE: - color ^= ATTR_UNDERLINE; - break; - case LINE_CMD_COLOR0: - color = color & ATTR_UNDERLINE; - break; - case LINE_CMD_COLOR8: - color &= 0xfff0; - color |= 8|ATTR_COLOR8; - break; - case LINE_CMD_BLINK: - color |= 0x80; - break; + } else if (*text == LINE_CMD_INDENT_FUNC) { + text += sizeof(INDENT_FUNC); + } else { + update_cmd_color(*text, &color); + term_set_color(view->window, color); } - set_color(view->window, color); text++; continue; } - if ((*text & 127) >= 32) - waddch(view->window, *text); - else { - /* low-ascii */ - set_color(view->window, ATTR_REVERSE); - waddch(view->window, (*text & 127)+'A'-1); - set_color(view->window, color); + if (xpos < term_width) { + const unsigned char *end = text; + if (view->utf8) + get_utf8_char(&end); + + if (*text >= 32 && + (end != text || (*text & 127) >= 32)) { + for (; text < end; text++) + term_addch(view->window, *text); + term_addch(view->window, *text); + } else { + /* low-ascii */ + term_set_color(view->window, ATTR_RESET|ATTR_REVERSE); + term_addch(view->window, (*text & 127)+'A'-1); + term_set_color(view->window, color); + } } text++; + xpos++; + } + + if (need_clrtoeol && xpos < term_width) { + term_set_color(view->window, ATTR_RESET); + term_clrtoeol(view->window); } return drawcount; @@ -321,7 +428,7 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, original if possible */ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view) { - GList *tmp; + LINE_REC *line; int linecount, total; if (view->empty_linecount == 0) { @@ -331,12 +438,10 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view) } total = 0; - tmp = g_list_last(view->buffer->lines); - for (; tmp != NULL; tmp = tmp->prev) { - LINE_REC *line = tmp->data; - + line = textbuffer_line_last(view->buffer); + for (; line != NULL; line = line->prev) { linecount = view_get_linecount(view, line); - if (tmp == view->bottom_startline) { + if (line == view->bottom_startline) { /* keep the old one, make sure that subline is ok */ if (view->bottom_subline > linecount) view->bottom_subline = linecount; @@ -347,7 +452,7 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view) total += linecount; if (total >= view->height) { - view->bottom_startline = tmp; + view->bottom_startline = line; view->bottom_subline = total - view->height; view->empty_linecount = 0; return; @@ -355,27 +460,26 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view) } /* not enough lines so we must be at the beginning of the buffer */ - view->bottom_startline = view->buffer->lines; + view->bottom_startline = view->buffer->first_line; view->bottom_subline = 0; view->empty_linecount = view->height - total; } static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view) { - GList *tmp; + LINE_REC *line; g_return_if_fail(view != NULL); view->ypos = -view->subline-1; - for (tmp = view->startline; tmp != NULL; tmp = tmp->next) - view->ypos += view_get_linecount(view, tmp->data); + for (line = view->startline; line != NULL; line = line->next) + view->ypos += view_get_linecount(view, line); } /* Create new view. */ TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer, int width, int height, - int default_indent, - int longword_noindent) + int scroll, int utf8) { TEXT_BUFFER_VIEW_REC *view; @@ -388,8 +492,8 @@ TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer, view->width = width; view->height = height; - view->default_indent = default_indent; - view->longword_noindent = longword_noindent; + view->scroll = scroll; + view->utf8 = utf8; view->cache = textbuffer_cache_get(view->siblings, width); textbuffer_view_init_bottom(view); @@ -439,60 +543,130 @@ void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view) /* Change the default indent position */ void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view, int default_indent, - int longword_noindent) + int longword_noindent, + INDENT_FUNC indent_func) { - view->default_indent = default_indent; - view->longword_noindent = longword_noindent; + if (default_indent != -1) + view->default_indent = default_indent; + if (longword_noindent != -1) + view->longword_noindent = longword_noindent; + + view->default_indent_func = indent_func; } -static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, GList *lines) +static void view_unregister_indent_func(TEXT_BUFFER_VIEW_REC *view, + INDENT_FUNC indent_func) +{ + INDENT_FUNC func; + LINE_REC *line; + const unsigned char *text, *tmp; + + if (view->default_indent_func == indent_func) + view->default_indent_func = NULL; + + /* recreate cache so it won't contain references + to the indent function */ + view_reset_cache(view); + view->cache = textbuffer_cache_get(view->siblings, view->width); + + /* remove all references to the indent function from buffer */ + line = view->buffer->first_line; + while (line != NULL) { + text = line->text; + + for (text = line->text;; text++) { + if (*text != '\0') + continue; + + text++; + if (*text == LINE_CMD_EOL) + break; + + if (*text == LINE_CMD_INDENT_FUNC) { + text++; + memcpy(&func, text, sizeof(INDENT_FUNC)); + if (func == indent_func) + memset(&func, 0, sizeof(INDENT_FUNC)); + text += sizeof(INDENT_FUNC); + } else if (*text == LINE_CMD_CONTINUE) { + memcpy(&tmp, text+1, sizeof(char *)); + text = tmp-1; + } + } + + line = line->next; + } +} + +void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func) +{ + g_slist_foreach(views, (GFunc) view_unregister_indent_func, + (void *) indent_func); +} + +void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll) +{ + view->scroll = scroll; +} + +void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8) +{ + view->utf8 = utf8; +} + +static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) { int linecount; linecount = 0; - while (lines != NULL) { - linecount += view_get_linecount(view, lines->data); - lines = lines->next; + while (line != NULL) { + linecount += view_get_linecount(view, line); + line = line->next; } return linecount; } -static void view_draw(TEXT_BUFFER_VIEW_REC *view, GList *line, - int subline, int ypos, int lines) +static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, + int subline, int ypos, int lines, int fill_bottom) { int linecount; - while (line != NULL && lines > 0) { - LINE_REC *rec = line->data; + if (view->dirty) /* don't bother drawing anything - redraw is coming */ + return; - linecount = view_line_draw(view, rec, subline, ypos, lines); + while (line != NULL && lines > 0) { + linecount = view_line_draw(view, line, subline, ypos, lines); ypos += linecount; lines -= linecount; subline = 0; line = line->next; } - /* clear the rest of the view */ - while (lines > 0) { - wmove(view->window, ypos, 0); - wclrtoeol(view->window); - ypos++; lines--; + if (fill_bottom) { + /* clear the rest of the view */ + term_set_color(view->window, ATTR_RESET); + while (lines > 0) { + term_move(view->window, 0, ypos); + term_clrtoeol(view->window); + ypos++; lines--; + } } } -#define view_draw_top(view, lines) \ - view_draw(view, (view)->startline, (view)->subline, 0, lines) +#define view_draw_top(view, lines, fill_bottom) \ + view_draw(view, (view)->startline, (view)->subline, \ + 0, lines, fill_bottom) static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines) { - GList *line; + LINE_REC *line; int ypos, maxline, subline, linecount; maxline = view->height-lines; line = view->startline; ypos = -view->subline; subline = 0; while (line != NULL && ypos < maxline) { - linecount = view_get_linecount(view, line->data); + linecount = view_get_linecount(view, line); ypos += linecount; if (ypos > maxline) { subline = maxline-(ypos-linecount); @@ -501,12 +675,12 @@ static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines) line = line->next; } - view_draw(view, line, subline, maxline, lines); + view_draw(view, line, subline, maxline, lines, TRUE); } /* Returns number of lines actually scrolled */ -static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline, - int scrollcount, int draw_nonclean) +static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines, + int *subline, int scrollcount, int draw_nonclean) { int linecount, realcount, scroll_visible; @@ -520,7 +694,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline, scrollcount += *subline; *subline = 0; while (scrollcount > 0) { - linecount = view_get_linecount(view, (*lines)->data); + linecount = view_get_linecount(view, *lines); if ((scroll_visible && *lines == view->bottom_startline) && (scrollcount >= view->bottom_subline)) { @@ -545,7 +719,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline, /* scroll up */ while (scrollcount < 0 && (*lines)->prev != NULL) { *lines = (*lines)->prev; - linecount = view_get_linecount(view, (*lines)->data); + linecount = view_get_linecount(view, *lines); realcount -= linecount; scrollcount += linecount; @@ -562,18 +736,17 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline, whole view */ textbuffer_view_redraw(view); } else { - scrollok(view->window, TRUE); - wscrl(view->window, realcount); - scrollok(view->window, FALSE); + term_set_color(view->window, ATTR_RESET); + term_window_scroll(view->window, realcount); if (draw_nonclean) { if (realcount < 0) - view_draw_top(view, -realcount); + view_draw_top(view, -realcount, TRUE); else view_draw_bottom(view, realcount); } - screen_refresh(view->window); + term_refresh(view->window); } } @@ -594,20 +767,19 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height) view->cache = textbuffer_cache_get(view->siblings, width); } - view->width = width; - view->height = height; - + view->width = width > 10 ? width : 10; + view->height = height > 1 ? height : 1; - if (view->buffer->lines == NULL) { - view->empty_linecount = height; - return; - } + if (view->buffer->first_line == NULL) { + view->empty_linecount = height; + return; + } textbuffer_view_init_bottom(view); /* check that we didn't scroll lower than bottom startline.. */ - if (g_list_find(view->bottom_startline->next, - view->startline->data) != NULL) { + if (textbuffer_line_exists_after(view->bottom_startline->next, + view->startline)) { view->startline = view->bottom_startline; view->subline = view->bottom_subline; } else if (view->startline == view->bottom_startline && @@ -615,7 +787,7 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height) view->subline = view->bottom_subline; } else { /* make sure the subline is still in allowed range */ - linecount = view_get_linecount(view, view->startline->data); + linecount = view_get_linecount(view, view->startline); if (view->subline > linecount) view->subline = linecount; } @@ -640,9 +812,10 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height) view->subline; if (view->empty_linecount < view->height-linecount) view->empty_linecount = view->height-linecount; + view->more_text = FALSE; } - textbuffer_view_redraw(view); + view->dirty = TRUE; } /* Clear the view, don't actually remove any lines from buffer. */ @@ -652,12 +825,13 @@ void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view) view->ypos = -1; view->bottom_startline = view->startline = - g_list_last(view->buffer->lines); + textbuffer_line_last(view->buffer); view->bottom_subline = view->subline = view->buffer->cur_line == NULL ? 0 : view_get_linecount(view, view->buffer->cur_line); view->empty_linecount = view->height; view->bottom = TRUE; + view->more_text = FALSE; textbuffer_view_redraw(view); } @@ -673,35 +847,28 @@ void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines) lines, TRUE); view->ypos += lines < 0 ? count : -count; view->bottom = view_is_bottom(view); + if (view->bottom) view->more_text = FALSE; if (view->window != NULL) - screen_refresh(view->window); + term_refresh(view->window); } /* Scroll to specified line */ void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) { - GList *tmp; - g_return_if_fail(view != NULL); - if (g_list_find(view->bottom_startline->next, line) != NULL) { + if (textbuffer_line_exists_after(view->bottom_startline->next, line)) { view->startline = view->bottom_startline; view->subline = view->bottom_subline; } else { - for (tmp = view->buffer->lines; tmp != NULL; tmp = tmp->next) { - LINE_REC *rec = tmp->data; - - if (rec == line) { - view->startline = tmp; - view->subline = 0; - break; - } - } + view->startline = line; + view->subline = 0; } textbuffer_view_init_ypos(view); view->bottom = view_is_bottom(view); + if (view->bottom) view->more_text = FALSE; textbuffer_view_redraw(view); } @@ -724,42 +891,20 @@ LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view, return cache; } -static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, - unsigned char update_counter) -{ - LINE_CACHE_REC *cache; - - if (view->cache->update_counter == update_counter) - return; - view->cache->update_counter = update_counter; - - cache = g_hash_table_lookup(view->cache->line_cache, line); - if (cache != NULL) { - g_free(cache); - g_hash_table_remove(view->cache->line_cache, line); - } -} - -static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, - unsigned char update_counter) -{ - view_remove_cache(view, line, update_counter); - - if (view->buffer->cur_line == line) - view->cache->last_linecount = view_get_linecount(view, line); -} - static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) { int linecount, ypos, subline; + if (!view->bottom) + view->more_text = TRUE; + if (view->bottom_startline == NULL) { view->startline = view->bottom_startline = - view->buffer->lines; + view->buffer->first_line; } if (view->buffer->cur_line != line && - g_list_find(view->bottom_startline, line) == NULL) + !textbuffer_line_exists_after(view->bottom_startline, line)) return; linecount = view->cache->last_linecount; @@ -780,11 +925,13 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) } if (view->bottom) { - if (view->ypos >= view->height) { + if (view->scroll && view->ypos >= view->height) { linecount = view->ypos-view->height+1; view_scroll(view, &view->startline, &view->subline, linecount, FALSE); view->ypos -= linecount; + } else { + view->bottom = view_is_bottom(view); } if (view->window != NULL) { @@ -795,13 +942,15 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) subline = -ypos; ypos = 0; } - view_line_draw(view, line, subline, ypos, - view->height - ypos); + if (ypos < view->height) { + view_line_draw(view, line, subline, ypos, + view->height - ypos); + } } } if (view->window != NULL) - screen_refresh(view->window); + term_refresh(view->window); } /* Update some line in the buffer which has been modified using @@ -853,11 +1002,8 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) (GHFunc) bookmark_check_remove, &rec); if (rec.remove_list != NULL) { - GList *pos = g_list_find(view->buffer->lines, line); - - new_line = pos == NULL || pos->prev == NULL ? NULL : - (pos->next == NULL ? pos->prev->data : - pos->next->data); + new_line = line->prev == NULL ? NULL : + (line->next == NULL ? line->prev : line->next); for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) { g_hash_table_remove(view->bookmarks, tmp->data); if (new_line != NULL) { @@ -872,29 +1018,53 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) /* Return number of real lines `lines' list takes - stops counting when the height reaches the view height */ static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view, - GList *lines, int subline, + LINE_REC *line, int subline, LINE_REC *skip_line) { int height, linecount; height = -subline; - while (lines != NULL && height < view->height) { - LINE_REC *line = lines->data; - + while (line != NULL && height < view->height) { if (line != skip_line) { linecount = view_get_linecount(view, line); height += linecount; } - lines = lines->next; + line = line->next; } return height < view->height ? height : view->height; } +static void view_remove_line_update_startline(TEXT_BUFFER_VIEW_REC *view, + LINE_REC *line, int linecount) +{ + int scroll; + + if (view->startline == line) { + view->startline = view->startline->prev != NULL ? + view->startline->prev : view->startline->next; + view->subline = 0; + } else { + scroll = view->height - + view_get_lines_height(view, view->startline, + view->subline, line); + if (scroll > 0) { + view_scroll(view, &view->startline, + &view->subline, -scroll, FALSE); + } + } + + /* FIXME: this is slow and unnecessary, but it's easy and + really works :) */ + textbuffer_view_init_ypos(view); + if (textbuffer_line_exists_after(view->startline, line)) + view->ypos -= linecount; +} + static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, int linecount) { - int realcount, scroll; + int realcount; view_bookmarks_check(view, line); @@ -902,62 +1072,49 @@ static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, /* the last line is being removed */ LINE_REC *prevline; - prevline = view->buffer->lines->data == line ? NULL : - g_list_last(view->bottom_startline)->data; + prevline = view->buffer->first_line == line ? NULL : + textbuffer_line_last(view->buffer); view->cache->last_linecount = prevline == NULL ? 0 : view_get_linecount(view, prevline); } - if (line == view->buffer->lines->data) { + if (view->buffer->first_line == line) { /* first line in the buffer - this is the most commonly removed line.. */ - if (view->bottom_startline->data == line) { + if (view->bottom_startline == line) { /* very small scrollback.. */ view->bottom_startline = view->bottom_startline->next; view->bottom_subline = 0; } - if (view->startline->data == line) { + if (view->startline == line) { /* removing the first line in screen */ realcount = view_scroll(view, &view->startline, &view->subline, - linecount, TRUE); + linecount, FALSE); view->ypos -= realcount; view->empty_linecount += linecount-realcount; } - } else if (g_list_find(view->bottom_startline, line) != NULL) { - realcount = view_scroll(view, &view->bottom_startline, - &view->bottom_subline, - -linecount, FALSE); - if (view->bottom) { - /* we're at the bottom, remove the same amount as - from bottom_startline */ - view_scroll(view, &view->startline, - &view->subline, -linecount, TRUE); - view->ypos -= linecount-realcount; - } else { - if (view->startline->data == line) { - view->startline = - view->startline->next != NULL ? - view->startline->next : - view->startline->prev; - view->subline = 0; - } - scroll = view->height - - view_get_lines_height(view, view->startline, - view->subline, line); - if (scroll > 0) { - view_scroll(view, &view->startline, - &view->subline, -scroll, TRUE); - view->ypos -= scroll; - } + } else { + if (textbuffer_line_exists_after(view->bottom_startline, + line)) { + realcount = view_scroll(view, &view->bottom_startline, + &view->bottom_subline, + -linecount, FALSE); + view->empty_linecount += linecount-realcount; + } + + if (textbuffer_line_exists_after(view->startline, + line)) { + view_remove_line_update_startline(view, line, + linecount); } - view->empty_linecount += linecount-realcount; } view->bottom = view_is_bottom(view); + if (view->bottom) view->more_text = FALSE; if (view->window != NULL) - screen_refresh(view->window); + term_refresh(view->window); } /* Remove one line from buffer. */ @@ -986,30 +1143,25 @@ void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) textbuffer_remove(view->buffer, line); } +static int g_free_true(void *data) +{ + g_free(data); + return TRUE; +} + /* Remove all lines from buffer. */ void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view) { - GSList *tmp; - g_return_if_fail(view != NULL); textbuffer_remove_all_lines(view->buffer); - /* destroy line caches - note that you can't do simultaneously - unrefs + cache_get()s or it will keep using the old caches */ - textbuffer_cache_unref(view->cache); - g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL); + g_hash_table_foreach_remove(view->bookmarks, + (GHRFunc) g_free_true, NULL); - /* recreate caches, clear screens */ - view->cache = textbuffer_cache_get(view->siblings, view->width); + view_reset_cache(view); textbuffer_view_clear(view); - - for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) { - TEXT_BUFFER_VIEW_REC *rec = tmp->data; - - rec->cache = textbuffer_cache_get(rec->siblings, rec->width); - textbuffer_view_clear(rec); - } + g_slist_foreach(view->siblings, (GFunc) textbuffer_view_clear, NULL); } /* Set a bookmark in view */ @@ -1040,7 +1192,7 @@ void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view, g_return_if_fail(name != NULL); if (view->bottom_startline != NULL) { - line = g_list_last(view->bottom_startline)->data; + line = textbuffer_line_last(view->buffer); textbuffer_view_set_bookmark(view, name, line); } } @@ -1057,14 +1209,15 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view, /* Specify window where the changes in view should be drawn, NULL disables it. */ -void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window) +void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, + TERM_WINDOW *window) { g_return_if_fail(view != NULL); if (view->window != window) { view->window = window; - if (window != NULL) - textbuffer_view_redraw(view); + if (window != NULL) + view->dirty = TRUE; } } @@ -1074,9 +1227,9 @@ void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view) g_return_if_fail(view != NULL); if (view->window != NULL) { - werase(view->window); - view_draw_top(view, view->height); - screen_refresh(view->window); + view->dirty = FALSE; + view_draw_top(view, view->height, TRUE); + term_refresh(view->window); } } @@ -1107,6 +1260,8 @@ static int sig_check_linecache(void) (GHRFunc) line_cache_check_remove, &now); } + + g_slist_free(caches); return 1; } diff --git a/apps/irssi/src/fe-text/textbuffer-view.h b/apps/irssi/src/fe-text/textbuffer-view.h index 21ed28cf..b529ebed 100644 --- a/apps/irssi/src/fe-text/textbuffer-view.h +++ b/apps/irssi/src/fe-text/textbuffer-view.h @@ -2,11 +2,18 @@ #define __TEXTBUFFER_VIEW_H #include "textbuffer.h" -#include "screen.h" +#include "term.h" + +typedef struct _TEXT_BUFFER_VIEW_REC TEXT_BUFFER_VIEW_REC; + +/* if ypos == -1, don't print anything, just return the indent size */ +typedef int (*INDENT_FUNC) (TEXT_BUFFER_VIEW_REC *view, + LINE_REC *line, int ypos); typedef struct { - unsigned char *start; + const unsigned char *start; int indent; + INDENT_FUNC indent_func; int color; /* first word in line belong to the end of the last word in @@ -37,24 +44,27 @@ typedef struct { int last_linecount; } TEXT_BUFFER_CACHE_REC; -typedef struct { +struct _TEXT_BUFFER_VIEW_REC { TEXT_BUFFER_REC *buffer; GSList *siblings; /* other views that use the same buffer */ - WINDOW *window; + TERM_WINDOW *window; int width, height; int default_indent; - int longword_noindent:1; + INDENT_FUNC default_indent_func; + unsigned int longword_noindent:1; + unsigned int scroll:1; /* scroll down automatically when at bottom */ + unsigned int utf8:1; /* use UTF8 in this view */ TEXT_BUFFER_CACHE_REC *cache; int ypos; /* cursor position - visible area is 0..height-1 */ - GList *startline; /* line at the top of the screen */ + LINE_REC *startline; /* line at the top of the screen */ int subline; /* number of "real lines" to skip from `startline' */ /* marks the bottom of the text buffer */ - GList *bottom_startline; + LINE_REC *bottom_startline; int bottom_subline; /* how many empty lines are in screen. a screenful when started @@ -62,23 +72,31 @@ typedef struct { int empty_linecount; /* window is at the bottom of the text buffer */ unsigned int bottom:1; + /* if !bottom - new text has been printed since we were at bottom */ + unsigned int more_text:1; + /* Window needs a redraw */ + unsigned int dirty:1; /* Bookmarks to the lines in the buffer - removed automatically when the line gets removed from buffer */ GHashTable *bookmarks; -} TEXT_BUFFER_VIEW_REC; +}; /* Create new view. */ TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer, int width, int height, - int default_indent, - int longword_noindent); + int scroll, int utf8); /* Destroy the view. */ void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view); /* Change the default indent position */ void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view, int default_indent, - int longword_noindent); + int longword_noindent, + INDENT_FUNC indent_func); +void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func); + +void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll); +void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8); /* Resize the view. */ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height); @@ -86,7 +104,7 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height); void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view); #define textbuffer_view_get_lines(view) \ - ((view)->buffer->lines) + ((view)->buffer->first_line) /* Scroll the view up/down */ void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines); @@ -121,7 +139,8 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view, /* Specify window where the changes in view should be drawn, NULL disables it. */ -void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window); +void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, + TERM_WINDOW *window); /* Redraw the view */ void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view); diff --git a/apps/irssi/src/fe-text/textbuffer.c b/apps/irssi/src/fe-text/textbuffer.c index f97f46c7..8ac8bf00 100644 --- a/apps/irssi/src/fe-text/textbuffer.c +++ b/apps/irssi/src/fe-text/textbuffer.c @@ -73,7 +73,7 @@ static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer, static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer) { TEXT_CHUNK_REC *rec; - char *buf, *ptr, **pptr; + unsigned char *buf, *ptr, **pptr; rec = g_mem_chunk_alloc(text_chunk); rec->pos = 0; @@ -90,7 +90,7 @@ static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer) breaks at least NetBSD/Alpha, so don't go "optimize" it :) */ ptr = rec->buffer; pptr = &ptr; - memcpy(buf, pptr, sizeof(char *)); + memcpy(buf, pptr, sizeof(unsigned char *)); } else { /* just to be safe */ mark_temp_eol(rec); @@ -140,7 +140,7 @@ static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line) } static void text_chunk_append(TEXT_BUFFER_REC *buffer, - const char *data, int len) + const unsigned char *data, int len) { TEXT_CHUNK_REC *chunk; int left; @@ -151,7 +151,8 @@ static void text_chunk_append(TEXT_BUFFER_REC *buffer, chunk = buffer->cur_text; while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) { left = TEXT_CHUNK_USABLE_SIZE - chunk->pos; - if (data[left-1] == 0) left--; /* don't split the commands */ + if (left > 0 && data[left-1] == 0) + left--; /* don't split the commands */ memcpy(chunk->buffer + chunk->pos, data, left); chunk->pos += left; @@ -187,14 +188,22 @@ static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer, { LINE_REC *line; - line = textbuffer_line_create(buffer); - if (prev == buffer->cur_line) { - buffer->cur_line = line; - buffer->lines = g_list_append(buffer->lines, buffer->cur_line); + line = textbuffer_line_create(buffer); + line->prev = prev; + if (prev == NULL) { + line->next = buffer->first_line; + if (buffer->first_line != NULL) + buffer->first_line->prev = line; + buffer->first_line = line; } else { - buffer->lines = g_list_insert(buffer->lines, line, - g_list_index(buffer->lines, prev)+1); + line->next = prev->next; + if (line->next != NULL) + line->next->prev = line; + prev->next = line; } + + if (prev == buffer->cur_line) + buffer->cur_line = line; buffer->lines_count++; return line; @@ -224,11 +233,34 @@ void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list) g_return_if_fail(buffer != NULL); while (list != NULL) { - textbuffer_line_unref(buffer, list->data); + if (list->data != NULL) + textbuffer_line_unref(buffer, list->data); list = list->next; } } +LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer) +{ + LINE_REC *line; + + line = buffer->cur_line; + if (line != NULL) { + while (line->next != NULL) + line = line->next; + } + return line; +} + +int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search) +{ + while (line != NULL) { + if (line == search) + return TRUE; + line = line->next; + } + return FALSE; +} + LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer, const unsigned char *data, int len, LINE_INFO_REC *info) @@ -245,6 +277,9 @@ LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after, g_return_val_if_fail(buffer != NULL, NULL); g_return_val_if_fail(data != NULL, NULL); + if (len == 0) + return insert_after; + line = !buffer->last_eol ? insert_after : textbuffer_line_insert(buffer, insert_after); @@ -264,13 +299,20 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line) g_return_if_fail(buffer != NULL); g_return_if_fail(line != NULL); - buffer->lines = g_list_remove(buffer->lines, line); + if (buffer->first_line == line) + buffer->first_line = line->next; + if (line->prev != NULL) + line->prev->next = line->next; + if (line->next != NULL) + line->next->prev = line->prev; if (buffer->cur_line == line) { - buffer->cur_line = buffer->lines == NULL ? NULL : - g_list_last(buffer->lines)->data; + buffer->cur_line = line->next != NULL ? + line->next : line->prev; } + line->prev = line->next = NULL; + buffer->lines_count--; textbuffer_line_unref(buffer, line); } @@ -279,40 +321,85 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line) void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer) { GSList *tmp; + LINE_REC *line; g_return_if_fail(buffer != NULL); for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) g_mem_chunk_free(text_chunk, tmp->data); g_slist_free(buffer->text_chunks); - buffer->text_chunks = NULL; + buffer->text_chunks = NULL; - g_list_free(buffer->lines); - buffer->lines = NULL; + while (buffer->first_line != NULL) { + line = buffer->first_line->next; + g_mem_chunk_free(line_chunk, buffer->first_line); + buffer->first_line = line; + } + buffer->lines_count = 0; buffer->cur_line = NULL; - buffer->lines_count = 0; + buffer->cur_text = NULL; + + buffer->last_eol = TRUE; +} + +static void set_color(GString *str, int cmd, int *last_fg, int *last_bg) +{ + if (cmd & LINE_COLOR_DEFAULT) { + g_string_sprintfa(str, "\004%c", FORMAT_STYLE_DEFAULTS); + + /* need to reset the fg/bg color */ + if (cmd & LINE_COLOR_BG) { + *last_bg = -1; + if (*last_fg != -1) { + g_string_sprintfa(str, "\004%c%c", + *last_fg, + FORMAT_COLOR_NOCHANGE); + } + } else { + *last_fg = -1; + if (*last_bg != -1) { + g_string_sprintfa(str, "\004%c%c", + FORMAT_COLOR_NOCHANGE, + *last_bg); + } + } + return; + } + + if ((cmd & LINE_COLOR_BG) == 0) { + /* change foreground color */ + *last_fg = (cmd & 0x0f)+'0'; + g_string_sprintfa(str, "\004%c%c", *last_fg, + FORMAT_COLOR_NOCHANGE); + } else { + /* change background color */ + *last_bg = (cmd & 0x0f)+'0'; + g_string_sprintfa(str, "\004%c%c", + FORMAT_COLOR_NOCHANGE, *last_bg); + } } void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) { - unsigned char cmd; - char *ptr, *tmp; + unsigned char cmd, *ptr, *tmp; + int last_fg, last_bg; g_return_if_fail(line != NULL); g_return_if_fail(str != NULL); g_string_truncate(str, 0); + last_fg = last_bg = -1; for (ptr = line->text;;) { if (*ptr != 0) { - g_string_append_c(str, *ptr); + g_string_append_c(str, (char) *ptr); ptr++; continue; } ptr++; - cmd = (unsigned char) *ptr; + cmd = *ptr; ptr++; if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) { @@ -322,7 +409,7 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) if (cmd == LINE_CMD_CONTINUE) { /* line continues in another address.. */ - memcpy(&tmp, ptr, sizeof(char *)); + memcpy(&tmp, ptr, sizeof(unsigned char *)); ptr = tmp; continue; } @@ -334,40 +421,39 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) if ((cmd & 0x80) == 0) { /* set color */ - g_string_sprintfa(str, "\004%c%c", - (cmd & 0x0f)+'0', - ((cmd & 0xf0) >> 4)+'0'); + set_color(str, cmd, &last_fg, &last_bg); } else switch (cmd) { case LINE_CMD_UNDERLINE: g_string_append_c(str, 31); break; + case LINE_CMD_REVERSE: + g_string_append_c(str, 22); + break; case LINE_CMD_COLOR0: g_string_sprintfa(str, "\004%c%c", '0', FORMAT_COLOR_NOCHANGE); break; - case LINE_CMD_COLOR8: - g_string_sprintfa(str, "\004%c%c", - '8', FORMAT_COLOR_NOCHANGE); - break; - case LINE_CMD_BLINK: - g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK); - break; case LINE_CMD_INDENT: break; + case LINE_CMD_INDENT_FUNC: + ptr += sizeof(void *); + break; } } } GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, int level, int nolevel, const char *text, + int before, int after, int regexp, int fullword, int case_sensitive) { #ifdef HAVE_REGEX_H regex_t preg; #endif - GList *line, *tmp; + LINE_REC *line, *pre_line; GList *matches; - GString *str; + GString *str; + int i, match_after, line_matched; g_return_val_if_fail(buffer != NULL, NULL); g_return_val_if_fail(text != NULL, NULL); @@ -383,216 +469,65 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, #endif } - matches = NULL; + matches = NULL; match_after = 0; str = g_string_new(NULL); - line = g_list_find(buffer->lines, startline); - if (line == NULL) - line = buffer->lines; - - for (tmp = line; tmp != NULL; tmp = tmp->next) { - LINE_REC *rec = tmp->data; + line = startline != NULL ? startline : buffer->first_line; - if ((rec->info.level & level) == 0 || - (rec->info.level & nolevel) != 0) + for (; line != NULL; line = line->next) { + if ((line->info.level & level) == 0 || + (line->info.level & nolevel) != 0) continue; if (*text == '\0') { /* no search word, everything matches */ - textbuffer_line_ref(rec); - matches = g_list_append(matches, rec); + textbuffer_line_ref(line); + matches = g_list_append(matches, line); continue; } - textbuffer_line2text(rec, FALSE, str); + textbuffer_line2text(line, FALSE, str); - if ( + line_matched = #ifdef HAVE_REGEX_H - regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 : + regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 : #endif - fullword ? strstr_full_case(str->str, text, - !case_sensitive) != NULL : - case_sensitive ? strstr(str->str, text) != NULL : - stristr(str->str, text) != NULL) { - /* matched */ - textbuffer_line_ref(rec); - matches = g_list_append(matches, rec); - } - } -#ifdef HAVE_REGEX_H - if (regexp) regfree(&preg); -#endif - g_string_free(str, TRUE); - return matches; -} - -#if 0 /* FIXME: saving formats is broken */ -static char *line_read_format(unsigned const char **text) -{ - GString *str; - char *ret; - - str = g_string_new(NULL); - for (;;) { - if (**text == '\0') { - if ((*text)[1] == LINE_CMD_EOL) { - /* leave text at \0 */ - 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 */ - break; - } - (*text)++; - if (**text == LINE_CMD_FORMAT) { - /* move text to start after \0 */ - (*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) { diff --git a/apps/irssi/src/fe-text/textbuffer.h b/apps/irssi/src/fe-text/textbuffer.h index 21f70e26..adea3604 100644 --- a/apps/irssi/src/fe-text/textbuffer.h +++ b/apps/irssi/src/fe-text/textbuffer.h @@ -1,20 +1,21 @@ #ifndef __TEXTBUFFER_H #define __TEXTBUFFER_H -/* FIXME: Textbuffer code gets a lot faster in some points when I get rid of - GList and make prev/next pointers directly in LINE_REC. However, this - can still wait for a while until I get rid of GList entirely everywhere. */ - #define LINE_TEXT_CHUNK_SIZE 16384 +#define LINE_COLOR_BG 0x20 +#define LINE_COLOR_DEFAULT 0x10 +#define LINE_COLOR_BOLD 0x08 +#define LINE_COLOR_BLINK 0x08 + enum { LINE_CMD_EOL=0x80, /* line ends here */ LINE_CMD_CONTINUE, /* line continues in next block */ LINE_CMD_COLOR0, /* change to black, would be same as \0\0 but it breaks things.. */ - LINE_CMD_COLOR8, /* change to dark grey, normally 8 = bold black */ LINE_CMD_UNDERLINE, /* enable/disable underlining */ + LINE_CMD_REVERSE, /* enable/disable reversed text */ LINE_CMD_INDENT, /* if line is split, indent it at this position */ - LINE_CMD_BLINK, /* blinking background */ + LINE_CMD_INDENT_FUNC, /* if line is split, use the specified indentation function */ LINE_CMD_FORMAT, /* end of line, but next will come the format that was used to create the text in format - fields are separated with \0 and last argument ends with \0. \0 is allowed @@ -27,13 +28,23 @@ typedef struct { time_t time; } LINE_INFO_REC; -typedef struct { - /* text in the line. \0 means that the next char will be a - color or command. <= 127 = color or if 8. bit is set, the - first 7 bits are the command. See LINE_CMD_xxxx. +typedef struct _LINE_REC { + /* Text in the line. \0 means that the next char will be a + color or command. + + If the 7th bit is set, the lowest 7 bits are the command + (see LINE_CMD_xxxx). Otherwise they specify a color change: + + Bit: + 5 - Setting a background color + 4 - Use "default terminal color" + 3 - Bold (fg) / blink (bg) - can be used with 4th bit + 0-2 - Color DO NOT ADD BLACK WITH \0\0 - this will break things. Use LINE_CMD_COLOR0 instead. */ + struct _LINE_REC *prev, *next; + unsigned char *text; unsigned char refcount; LINE_INFO_REC info; @@ -47,7 +58,7 @@ typedef struct { typedef struct { GSList *text_chunks; - GList *lines; + LINE_REC *first_line; int lines_count; LINE_REC *cur_line; @@ -65,6 +76,9 @@ void textbuffer_line_ref(LINE_REC *line); void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line); void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list); +LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer); +int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search); + /* Append text to buffer. When \0 is found at the END OF DATA, a new line is created. You must send the EOL command before you can do anything else with the buffer. */ @@ -82,6 +96,7 @@ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer); void textbuffer_line2text(LINE_REC *line, int coloring, GString *str); GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, int level, int nolevel, const char *text, + int before, int after, int regexp, int fullword, int case_sensitive); void textbuffer_init(void); diff --git a/apps/irssi/src/fe-text/tparm.c b/apps/irssi/src/fe-text/tparm.c new file mode 100644 index 00000000..3f58e6f3 --- /dev/null +++ b/apps/irssi/src/fe-text/tparm.c @@ -0,0 +1,740 @@ +/* + * tparm.c + * + * By Ross Ridge + * Public Domain + * 92/02/01 07:30:36 + * + */ +#include +#include +#include +#include + +#ifndef MAX_PUSHED +#define MAX_PUSHED 32 +#endif + +#define ARG 1 +#define NUM 2 + +#define INTEGER 1 +#define STRING 2 + +#define MAX_LINE 640 + +typedef void* anyptr; + +typedef struct stack_str { + int type; + int argnum; + int value; +} stack; + +static stack S[MAX_PUSHED]; +static stack vars['z'-'a'+1]; +static int pos = 0; + +static struct arg_str { + int type; + int integer; + char *string; +} arg_list[10]; + +static int argcnt; + +static va_list tparm_args; + +static int pusharg(int arg) +{ + if (pos == MAX_PUSHED) + return 1; + S[pos].type = ARG; + S[pos++].argnum = arg; + return 0; +} + +static int pushnum(int num) +{ + if (pos == MAX_PUSHED) + return 1; + S[pos].type = NUM; + S[pos++].value = num; + return 0; +} + +/* VARARGS2 */ +static int getarg(int argnum, int type, anyptr p) +{ + while (argcnt < argnum) { + arg_list[argcnt].type = INTEGER; + arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); + } + if (argcnt > argnum) { + if (arg_list[argnum].type != type) + return 1; + else if (type == STRING) + *(char **)p = arg_list[argnum].string; + else + *(int *)p = arg_list[argnum].integer; + } else { + arg_list[argcnt].type = type; + if (type == STRING) + *(char **)p = arg_list[argcnt++].string + = (char *) va_arg(tparm_args, char *); + else + *(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); + } + return 0; +} + + +static int popstring(char **str) +{ + if (pos-- == 0) + return 1; + if (S[pos].type != ARG) + return 1; + return(getarg(S[pos].argnum, STRING, (anyptr) str)); +} + +static int popnum(int *num) +{ + if (pos-- == 0) + return 1; + switch (S[pos].type) { + case ARG: + return (getarg(S[pos].argnum, INTEGER, (anyptr) num)); + case NUM: + *num = S[pos].value; + return 0; + } + return 1; +} + +static int cvtchar(const char *sp, char *c) +{ + switch(*sp) { + case '\\': + switch(*++sp) { + case '\'': + case '$': + case '\\': + case '%': + *c = *sp; + return 2; + case '\0': + *c = '\\'; + return 1; + case '0': + if (sp[1] == '0' && sp[2] == '0') { + *c = '\0'; + return 4; + } + *c = '\200'; /* '\0' ???? */ + return 2; + default: + *c = *sp; + return 2; + } + default: + *c = *sp; + return 1; + } +} + +static int termcap; + +/* sigh... this has got to be the ugliest code I've ever written. + Trying to handle everything has its cost, I guess. + + It actually isn't to hard to figure out if a given % code is supposed + to be interpeted with its termcap or terminfo meaning since almost + all terminfo codes are invalid unless something has been pushed on + the stack and termcap strings will never push things on the stack + (%p isn't used by termcap). So where we have a choice we make the + decision by wether or not somthing has been pushed on the stack. + The static variable termcap keeps track of this; it starts out set + to 1 and is incremented as each argument processed by a termcap % code, + however if something is pushed on the stack it's set to 0 and the + rest of the % codes are interpeted as terminfo % codes. Another way + of putting it is that if termcap equals one we haven't decided either + way yet, if it equals zero we're looking for terminfo codes, and if + its greater than 1 we're looking for termcap codes. + + Terminfo % codes: + + %% output a '%' + %[[:][-+# ][width][.precision]][doxXs] + output pop according to the printf format + %c output pop as a char + %'c' push character constant c. + %{n} push decimal constant n. + %p[1-9] push paramter [1-9] + %g[a-z] push variable [a-z] + %P[a-z] put pop in variable [a-z] + %l push the length of pop (a string) + %+ add pop to pop and push the result + %- subtract pop from pop and push the result + %* multiply pop and pop and push the result + %& bitwise and pop and pop and push the result + %| bitwise or pop and pop and push the result + %^ bitwise xor pop and pop and push the result + %~ push the bitwise not of pop + %= compare if pop and pop are equal and push the result + %> compare if pop is less than pop and push the result + %< compare if pop is greater than pop and push the result + %A logical and pop and pop and push the result + %O logical or pop and pop and push the result + %! push the logical not of pop + %? condition %t if_true [%e if_false] %; + if condtion evaulates as true then evaluate if_true, + else evaluate if_false. elseif's can be done: +%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %; + %i add one to parameters 1 and 2. (ANSI) + + Termcap Codes: + + %% output a % + %. output parameter as a character + %d output parameter as a decimal number + %2 output parameter in printf format %02d + %3 output parameter in printf format %03d + %+x add the character x to parameter and output it as a character +(UW) %-x subtract parameter FROM the character x and output it as a char +(UW) %ax add the character x to parameter +(GNU) %a[+*-/=][cp]x + GNU arithmetic. +(UW) %sx subtract parameter FROM the character x + %>xy if parameter > character x then add character y to parameter + %B convert to BCD (parameter = (parameter/10)*16 + parameter%16) + %D Delta Data encode (parameter = parameter - 2*(paramter%16)) + %i increment the first two parameters by one + %n xor the first two parameters by 0140 +(GNU) %m xor the first two parameters by 0177 + %r swap the first two parameters +(GNU) %b backup to previous parameter +(GNU) %f skip this parameter + + Note the two definitions of %a, the GNU defintion is used if the characters + after the 'a' are valid, otherwise the UW definition is used. + + (GNU) used by GNU Emacs termcap libraries + (UW) used by the University of Waterloo (MFCF) termcap libraries + +*/ + +char *tparm(const char *str, ...) { + static char OOPS[] = "OOPS"; + static char buf[MAX_LINE]; + register const char *sp; + register char *dp; + register char *fmt; + char conv_char; + char scan_for; + int scan_depth = 0, if_depth; + static int i, j; + static char *s, c; + char fmt_buf[MAX_LINE]; + char sbuf[MAX_LINE]; + + va_start(tparm_args, str); + + sp = str; + dp = buf; + scan_for = 0; + if_depth = 0; + argcnt = 0; + pos = 0; + termcap = 1; + while (*sp != '\0') { + switch(*sp) { + case '\\': + if (scan_for) { + if (*++sp != '\0') + sp++; + break; + } + *dp++ = *sp++; + if (*sp != '\0') + *dp++ = *sp++; + break; + case '%': + sp++; + if (scan_for) { + if (*sp == scan_for && if_depth == scan_depth) { + if (scan_for == ';') + if_depth--; + scan_for = 0; + } else if (*sp == '?') + if_depth++; + else if (*sp == ';') { + if (if_depth == 0) + return OOPS; + else + if_depth--; + } + sp++; + break; + } + fmt = NULL; + switch(*sp) { + case '%': + *dp++ = *sp++; + break; + case '+': + if (!termcap) { + if (popnum(&j) || popnum(&i)) + return OOPS; + i += j; + if (pushnum(i)) + return OOPS; + sp++; + break; + } + ;/* FALLTHROUGH */ + case 'C': + if (*sp == 'C') { + if (getarg(termcap - 1, INTEGER, &i)) + return OOPS; + if (i >= 96) { + i /= 96; + if (i == '$') + *dp++ = '\\'; + *dp++ = i; + } + } + fmt = "%c"; + /* FALLTHROUGH */ + case 'a': + if (!termcap) + return OOPS; + if (getarg(termcap - 1, INTEGER, (anyptr) &i)) + return OOPS; + if (*++sp == '\0') + return OOPS; + if ((sp[1] == 'p' || sp[1] == 'c') + && sp[2] != '\0' && fmt == NULL) { + /* GNU aritmitic parameter, what they + realy need is terminfo. */ + int val, lc; + if (sp[1] == 'p' + && getarg(termcap - 1 + sp[2] - '@', + INTEGER, (anyptr) &val)) + return OOPS; + if (sp[1] == 'c') { + lc = cvtchar(sp + 2, &c) + 2; + /* Mask out 8th bit so \200 can be + used for \0 as per GNU doc's */ + val = c & 0177; + } else + lc = 2; + switch(sp[0]) { + case '=': + break; + case '+': + val = i + val; + break; + case '-': + val = i - val; + break; + case '*': + val = i * val; + break; + case '/': + val = i / val; + break; + default: + /* Not really GNU's %a after all... */ + lc = cvtchar(sp, &c); + val = c + i; + break; + } + arg_list[termcap - 1].integer = val; + sp += lc; + break; + } + sp += cvtchar(sp, &c); + arg_list[termcap - 1].integer = c + i; + if (fmt == NULL) + break; + sp--; + /* FALLTHROUGH */ + case '-': + if (!termcap) { + if (popnum(&j) || popnum(&i)) + return OOPS; + i -= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + } + fmt = "%c"; + /* FALLTHROUGH */ + case 's': + if (termcap && (fmt == NULL || *sp == '-')) { + if (getarg(termcap - 1, INTEGER, &i)) + return OOPS; + if (*++sp == '\0') + return OOPS; + sp += cvtchar(sp, &c); + arg_list[termcap - 1].integer = c - i; + if (fmt == NULL) + break; + sp--; + } + if (!termcap) + return OOPS; + ;/* FALLTHROUGH */ + case '.': + if (termcap && fmt == NULL) + fmt = "%c"; + ;/* FALLTHROUGH */ + case 'd': + if (termcap && fmt == NULL) + fmt = "%d"; + ;/* FALLTHROUGH */ + case '2': + if (termcap && fmt == NULL) + fmt = "%02d"; + ;/* FALLTHROUGH */ + case '3': + if (termcap && fmt == NULL) + fmt = "%03d"; + ;/* FALLTHROUGH */ + case ':': case ' ': case '#': case 'u': + case 'x': case 'X': case 'o': case 'c': + case '0': case '1': case '4': case '5': + case '6': case '7': case '8': case '9': + if (fmt == NULL) { + if (termcap) + return OOPS; + if (*sp == ':') + sp++; + fmt = fmt_buf; + *fmt++ = '%'; + while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') { + if (*sp == '\0') + return OOPS; + *fmt++ = *sp++; + } + *fmt++ = *sp; + *fmt = '\0'; + fmt = fmt_buf; + } + conv_char = fmt[strlen(fmt) - 1]; + if (conv_char == 's') { + if (popstring(&s)) + return OOPS; + sprintf(sbuf, fmt, s); + } else { + if (termcap) { + if (getarg(termcap++ - 1, + INTEGER, &i)) + return OOPS; + } else + if (popnum(&i)) + return OOPS; + if (i == 0 && conv_char == 'c') + *sbuf = 0; + else + sprintf(sbuf, fmt, i); + } + sp++; + fmt = sbuf; + while(*fmt != '\0') { + if (*fmt == '$') + *dp++ = '\\'; + *dp++ = *fmt++; + } + break; + case 'r': + if (!termcap || getarg(1, INTEGER, &i)) + return OOPS; + arg_list[1].integer = arg_list[0].integer; + arg_list[0].integer = i; + sp++; + break; + case 'i': + if (getarg(1, INTEGER, &i) + || arg_list[0].type != INTEGER) + return OOPS; + arg_list[1].integer++; + arg_list[0].integer++; + sp++; + break; + case 'n': + if (!termcap || getarg(1, INTEGER, &i)) + return OOPS; + arg_list[0].integer ^= 0140; + arg_list[1].integer ^= 0140; + sp++; + break; + case '>': + if (!termcap) { + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i > j); + if (pushnum(i)) + return OOPS; + sp++; + break; + } + if (getarg(termcap-1, INTEGER, &i)) + return OOPS; + sp += cvtchar(sp, &c); + if (i > c) { + sp += cvtchar(sp, &c); + arg_list[termcap-1].integer += c; + } else + sp += cvtchar(sp, &c); + sp++; + break; + case 'B': + if (!termcap || getarg(termcap-1, INTEGER, &i)) + return OOPS; + arg_list[termcap-1].integer = 16*(i/10)+i%10; + sp++; + break; + case 'D': + if (!termcap || getarg(termcap-1, INTEGER, &i)) + return OOPS; + arg_list[termcap-1].integer = i - 2 * (i % 16); + sp++; + break; + case 'p': + if (termcap > 1) + return OOPS; + if (*++sp == '\0') + return OOPS; + if (*sp == '0') + i = 9; + else + i = *sp - '1'; + if (i < 0 || i > 9) + return OOPS; + if (pusharg(i)) + return OOPS; + termcap = 0; + sp++; + break; + case 'P': + if (termcap || *++sp == '\0') + return OOPS; + i = *sp++ - 'a'; + if (i < 0 || i > 25) + return OOPS; + if (pos-- == 0) + return OOPS; + switch(vars[i].type = S[pos].type) { + case ARG: + vars[i].argnum = S[pos].argnum; + break; + case NUM: + vars[i].value = S[pos].value; + break; + } + break; + case 'g': + if (termcap || *++sp == '\0') + return OOPS; + i = *sp++ - 'a'; + if (i < 0 || i > 25) + return OOPS; + switch(vars[i].type) { + case ARG: + if (pusharg(vars[i].argnum)) + return OOPS; + break; + case NUM: + if (pushnum(vars[i].value)) + return OOPS; + break; + } + break; + case '\'': + if (termcap > 1) + return OOPS; + if (*++sp == '\0') + return OOPS; + sp += cvtchar(sp, &c); + if (pushnum(c) || *sp++ != '\'') + return OOPS; + termcap = 0; + break; + case '{': + if (termcap > 1) + return OOPS; + i = 0; + sp++; + while(isdigit((int) (unsigned char) *sp)) + i = 10 * i + *sp++ - '0'; + if (*sp++ != '}' || pushnum(i)) + return OOPS; + termcap = 0; + break; + case 'l': + if (termcap || popstring(&s)) + return OOPS; + i = strlen(s); + if (pushnum(i)) + return OOPS; + sp++; + break; + case '*': + if (termcap || popnum(&j) || popnum(&i)) + return OOPS; + i *= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '/': + if (termcap || popnum(&j) || popnum(&i)) + return OOPS; + i /= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case 'm': + if (termcap) { + if (getarg(1, INTEGER, &i)) + return OOPS; + arg_list[0].integer ^= 0177; + arg_list[1].integer ^= 0177; + sp++; + break; + } + if (popnum(&j) || popnum(&i)) + return OOPS; + i %= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '&': + if (popnum(&j) || popnum(&i)) + return OOPS; + i &= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '|': + if (popnum(&j) || popnum(&i)) + return OOPS; + i |= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '^': + if (popnum(&j) || popnum(&i)) + return OOPS; + i ^= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '=': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i == j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case '<': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i < j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case 'A': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i && j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case 'O': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i || j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case '!': + if (popnum(&i)) + return OOPS; + i = !i; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '~': + if (popnum(&i)) + return OOPS; + i = ~i; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '?': + if (termcap > 1) + return OOPS; + termcap = 0; + if_depth++; + sp++; + break; + case 't': + if (popnum(&i) || if_depth == 0) + return OOPS; + if (!i) { + scan_for = 'e'; + scan_depth = if_depth; + } + sp++; + break; + case 'e': + if (if_depth == 0) + return OOPS; + scan_for = ';'; + scan_depth = if_depth; + sp++; + break; + case ';': + if (if_depth-- == 0) + return OOPS; + sp++; + break; + case 'b': + if (--termcap < 1) + return OOPS; + sp++; + break; + case 'f': + if (!termcap++) + return OOPS; + sp++; + break; + } + break; + default: + if (scan_for) + sp++; + else + *dp++ = *sp++; + break; + } + } + va_end(tparm_args); + *dp = '\0'; + return buf; +} diff --git a/apps/irssi/src/fe-text/utf8.c b/apps/irssi/src/fe-text/utf8.c new file mode 100644 index 00000000..63a834c8 --- /dev/null +++ b/apps/irssi/src/fe-text/utf8.c @@ -0,0 +1,64 @@ + +#define UTF8_COMPUTE(Char, Mask, Len) \ + if (Char < 128) \ + { \ + Len = 1; \ + Mask = 0x7f; \ + } \ + else if ((Char & 0xe0) == 0xc0) \ + { \ + Len = 2; \ + Mask = 0x1f; \ + } \ + else if ((Char & 0xf0) == 0xe0) \ + { \ + Len = 3; \ + Mask = 0x0f; \ + } \ + else if ((Char & 0xf8) == 0xf0) \ + { \ + Len = 4; \ + Mask = 0x07; \ + } \ + else if ((Char & 0xfc) == 0xf8) \ + { \ + Len = 5; \ + Mask = 0x03; \ + } \ + else if ((Char & 0xfe) == 0xfc) \ + { \ + Len = 6; \ + Mask = 0x01; \ + } \ + else \ + Len = -1; + +#define UTF8_GET(Result, Chars, Count, Mask, Len) \ + (Result) = (Chars)[0] & (Mask); \ + for ((Count) = 1; (Count) < (Len); ++(Count)) \ + { \ + if (((Chars)[(Count)] & 0xc0) != 0x80) \ + { \ + (Result) = -1; \ + break; \ + } \ + (Result) <<= 6; \ + (Result) |= ((Chars)[(Count)] & 0x3f); \ + } + +void get_utf8_char(const unsigned char **ptr) +{ + int i, mask = 0, len; + unsigned int result; + unsigned char c = (unsigned char) **ptr; + + UTF8_COMPUTE(c, mask, len); + if (len == -1) + return; + + UTF8_GET(result, *ptr, i, mask, len); + if (result == -1) + return; + + *ptr += len-1; +} diff --git a/apps/irssi/src/fe-text/utf8.h b/apps/irssi/src/fe-text/utf8.h new file mode 100644 index 00000000..3d8f3783 --- /dev/null +++ b/apps/irssi/src/fe-text/utf8.h @@ -0,0 +1,6 @@ +#ifndef __UTF8_H +#define __UTF8_H + +void get_utf8_char(const unsigned char **ptr); + +#endif diff --git a/apps/irssi/src/lib-config/.cvsignore b/apps/irssi/src/lib-config/.cvsignore new file mode 100644 index 00000000..577aa172 --- /dev/null +++ b/apps/irssi/src/lib-config/.cvsignore @@ -0,0 +1,7 @@ +*.la +*.lo +.deps +.libs +Makefile +Makefile.in +so_locations diff --git a/apps/irssi/src/lib-config/get.c b/apps/irssi/src/lib-config/get.c index 98e2a30b..124d7101 100644 --- a/apps/irssi/src/lib-config/get.c +++ b/apps/irssi/src/lib-config/get.c @@ -153,7 +153,7 @@ int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int d str = config_get_str(rec, section, key, NULL); if (str == NULL) return def; - return toupper(*str) == 'T' || toupper(*str) == 'Y'; + return i_toupper(*str) == 'T' || i_toupper(*str) == 'Y'; } /* Return value of key `value_key' from list item where `key' is `value' */ @@ -224,8 +224,8 @@ int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def) str = config_node_get_str(parent, key, NULL); if (str == NULL) return def; - return toupper(*str) == 'T' || toupper(*str) == 'Y' || - (toupper(*str) == 'O' && toupper(str[1]) == 'N'); + return i_toupper(*str) == 'T' || i_toupper(*str) == 'Y' || + (i_toupper(*str) == 'O' && i_toupper(str[1]) == 'N'); } /* Get the value of keys `key' and `key_value' and put them to @@ -308,3 +308,23 @@ CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index) return NULL; } + +/* Returns the first non-comment node in list */ +GSList *config_node_first(GSList *list) +{ + while (list != NULL) { + CONFIG_NODE *node = list->data; + + if (node->type != NODE_TYPE_COMMENT) + break; + list = list->next; + } + return list; +} + +/* Returns the next non-comment node in list */ +GSList *config_node_next(GSList *list) +{ + list = list->next; + return config_node_first(list); +} diff --git a/apps/irssi/src/lib-config/iconfig.h b/apps/irssi/src/lib-config/iconfig.h index ea2e6c37..fc475f4e 100644 --- a/apps/irssi/src/lib-config/iconfig.h +++ b/apps/irssi/src/lib-config/iconfig.h @@ -100,6 +100,11 @@ CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const c /* Returns n'th node from list. */ CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index); +/* Returns the first non-comment node in list */ +GSList *config_node_first(GSList *list); +/* Returns the next non-comment node in list */ +GSList *config_node_next(GSList *list); + /* Setting values */ int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value); int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value); diff --git a/apps/irssi/src/lib-config/parse.c b/apps/irssi/src/lib-config/parse.c index 883a920b..02eb4255 100644 --- a/apps/irssi/src/lib-config/parse.c +++ b/apps/irssi/src/lib-config/parse.c @@ -32,7 +32,7 @@ static unsigned int g_istr_hash(gconstpointer v) unsigned int h = 0, g; while (*s != '\0') { - h = (h << 4) + toupper(*s); + h = (h << 4) + i_toupper(*s); if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; @@ -82,11 +82,7 @@ static void config_parse_get_token(GScanner *scanner, CONFIG_NODE *node) } else { if (scanner->token == G_TOKEN_INT) { scanner->token = G_TOKEN_STRING; -#undef g_strdup_printf /* This is free'd by GLib itself */ scanner->value.v_string = g_strdup_printf("%lu", scanner->value.v_int); -#ifdef MEM_DEBUG -#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b) -#endif } break; } diff --git a/apps/irssi/src/lib-config/write.c b/apps/irssi/src/lib-config/write.c index 446735cd..19447827 100644 --- a/apps/irssi/src/lib-config/write.c +++ b/apps/irssi/src/lib-config/write.c @@ -77,7 +77,7 @@ static int config_has_specials(const char *text) g_return_val_if_fail(text != NULL, FALSE); while (*text != '\0') { - if (!isalnum((int) *text) && *text != '_') + if (!i_isalnum(*text) && *text != '_') return TRUE; text++; } diff --git a/apps/irssi/src/lib-popt/popt.c b/apps/irssi/src/lib-popt/popt.c index c09a0b30..a23ae8aa 100644 --- a/apps/irssi/src/lib-popt/popt.c +++ b/apps/irssi/src/lib-popt/popt.c @@ -452,7 +452,8 @@ int poptGetNextOpt(poptContext con) { if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) - con->finalArgv[con->finalArgvCount++] = g_strdup(con->os->nextArg); + con->finalArgv[con->finalArgvCount++] = + strcpy(malloc(strlen(con->os->nextArg)+1), con->os->nextArg); } if (dup) g_free(dup); diff --git a/apps/irssi/src/lib-popt/poptconfig.c b/apps/irssi/src/lib-popt/poptconfig.c index d064297c..835798a5 100644 --- a/apps/irssi/src/lib-popt/poptconfig.c +++ b/apps/irssi/src/lib-popt/poptconfig.c @@ -17,19 +17,19 @@ static void configLine(poptContext con, char * line) { if (strncmp(line, con->appName, nameLength)) return; line += nameLength; - if (!*line || !isspace(*line)) return; - while (*line && isspace(*line)) line++; + if (!*line || !i_isspace(*line)) return; + while (*line && i_isspace(*line)) line++; entryType = line; - while (!*line || !isspace(*line)) line++; + while (!*line || !i_isspace(*line)) line++; *line++ = '\0'; - while (*line && isspace(*line)) line++; + while (*line && i_isspace(*line)) line++; if (!*line) return; opt = line; - while (!*line || !isspace(*line)) line++; + while (!*line || !i_isspace(*line)) line++; *line++ = '\0'; - while (*line && isspace(*line)) line++; + while (*line && i_isspace(*line)) line++; if (!*line) return; if (opt[0] == '-' && opt[1] == '-') @@ -92,7 +92,7 @@ int poptReadConfigFile(poptContext con, char * fn) { case '\n': *dst = '\0'; dst = buf; - while (*dst && isspace(*dst)) dst++; + while (*dst && i_isspace(*dst)) dst++; if (*dst && *dst != '#') { configLine(con, dst); } diff --git a/apps/irssi/src/lib-popt/popthelp.c b/apps/irssi/src/lib-popt/popthelp.c index c1876d74..243f868e 100644 --- a/apps/irssi/src/lib-popt/popthelp.c +++ b/apps/irssi/src/lib-popt/popthelp.c @@ -94,15 +94,15 @@ static void singleOptionHelp(FILE * f, int maxLeftCol, helpLength = strlen(help); while (helpLength > lineLength) { ch = help + lineLength - 1; - while (ch > help && !isspace(*ch)) ch--; + while (ch > help && !i_isspace(*ch)) ch--; if (ch == help) break; /* give up */ - while (ch > (help + 1) && isspace(*ch)) ch--; + while (ch > (help + 1) && i_isspace(*ch)) ch--; ch++; sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength); fprintf(f, format, help, " "); help = ch; - while (isspace(*help) && *help) help++; + while (i_isspace(*help) && *help) help++; helpLength = strlen(help); } diff --git a/apps/irssi/src/lib-popt/poptparse.c b/apps/irssi/src/lib-popt/poptparse.c index eb2a6721..135ead56 100644 --- a/apps/irssi/src/lib-popt/poptparse.c +++ b/apps/irssi/src/lib-popt/poptparse.c @@ -45,7 +45,7 @@ int poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr) { if (*src != quote) *buf++ = '\\'; } *buf++ = *src; - } else if (isspace(*src)) { + } else if (isspace((int) (unsigned char) *src)) { if (*argv[argc]) { buf++, argc++; if (argc == argvAlloced) { diff --git a/apps/irssi/src/perl/.cvsignore b/apps/irssi/src/perl/.cvsignore new file mode 100644 index 00000000..189d2fd1 --- /dev/null +++ b/apps/irssi/src/perl/.cvsignore @@ -0,0 +1,10 @@ +*.la +*.lo +*.o +.deps +.libs +Makefile +Makefile.in +so_locations +perl-signals-list.h +irssi-core.pl.h diff --git a/apps/irssi/src/perl/Makefile.am b/apps/irssi/src/perl/Makefile.am new file mode 100644 index 00000000..965605a2 --- /dev/null +++ b/apps/irssi/src/perl/Makefile.am @@ -0,0 +1,145 @@ +LIBTOOL = $(PERL_LIBTOOL) + +include $(top_srcdir)/Makefile.defines.in + +moduledir = $(silc_modulesdir) + +module_LTLIBRARIES = $(perl_module_lib) $(perl_module_fe_lib) +noinst_LTLIBRARIES = $(perl_static_lib) $(perl_static_fe_lib) +EXTRA_LTLIBRARIES = \ + libperl_core.la libfe_perl.la \ + libperl_core_static.la libfe_perl_static.la + +libperl_core_la_LDFLAGS = -avoid-version -rpath $(moduledir) +libfe_perl_la_LDFLAGS = -avoid-version -rpath $(moduledir) + +perl-core.c: perl-signals-list.h irssi-core.pl.h + +INCLUDES = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core \ + -I$(top_srcdir)/src/fe-common/core \ + -I$(top_srcdir)/src/fe-common/silc \ + $(GLIB_CFLAGS) \ + -DSCRIPTDIR=\""$(libdir)/irssi/scripts"\" \ + -DPERL_USE_LIB=\""$(PERL_USE_LIB)"\" \ + -DPERL_STATIC_LIBS=$(PERL_STATIC_LIBS) \ + $(PERL_CFLAGS) + +perl_sources = \ + perl-core.c \ + perl-common.c \ + perl-signals.c \ + perl-sources.c + +perl_fe_sources = \ + module-formats.c \ + perl-fe.c + +noinst_HEADERS = \ + module.h \ + module-fe.h \ + module-formats.h \ + perl-core.h \ + perl-common.h \ + perl-signals.h \ + perl-sources.h + +libperl_core_la_DEPENDENCIES = .libs/libperl_orig.a .libs/DynaLoader.a + +.libs/libperl_orig.a: + if [ ! -d .libs ]; then mkdir .libs; fi + rm -f .libs/libperl_orig.a + if [ x$(LIBPERL_A) = x ]; then touch .libs/libperl_orig.a; else $(LN_S) $(LIBPERL_A) .libs/libperl_orig.a; fi +.libs/DynaLoader.a: + if [ ! -d .libs ]; then mkdir .libs; fi + rm -f .libs/DynaLoader.a + $(LN_S) $(DYNALOADER_A) .libs/DynaLoader.a + +libperl_core_la_SOURCES = \ + $(perl_sources) + +libperl_core_static_la_SOURCES = \ + $(perl_sources) + +libfe_perl_la_SOURCES = \ + $(perl_fe_sources) + +libfe_perl_static_la_SOURCES = \ + $(perl_fe_sources) + +perl-signals-list.h: $(top_srcdir)/docs/signals.txt $(srcdir)/get-signals.pl + cat $(top_srcdir)/docs/signals.txt | $(perlpath) $(srcdir)/get-signals.pl > perl-signals-list.h + +irssi-core.pl.h: irssi-core.pl + $(top_srcdir)/file2header.sh $(srcdir)/irssi-core.pl irssi_core_code > irssi-core.pl.h + +common_sources = \ + common/Irssi.xs \ + common/Irssi.pm \ + common/Channel.xs \ + common/Core.xs \ + common/Ignore.xs \ + common/Log.xs \ + common/Masks.xs \ + common/Query.xs \ + common/Rawlog.xs \ + common/Server.xs \ + common/Settings.xs \ + common/Makefile.PL.in \ + common/typemap \ + common/module.h + +ui_sources = \ + ui/UI.xs \ + ui/UI.pm \ + ui/Formats.xs \ + ui/Themes.xs \ + ui/Window.xs \ + ui/Makefile.PL.in \ + ui/typemap \ + ui/module.h + +textui_sources = \ + textui/TextUI.xs \ + textui/TextUI.pm \ + textui/TextBuffer.xs \ + textui/TextBufferView.xs \ + textui/Statusbar.xs \ + textui/Makefile.PL.in \ + textui/typemap \ + textui/module.h + +EXTRA_DIST = \ + libperl_dynaloader.la \ + libperl_orig.la \ + get-signals.pl \ + irssi-core.pl \ + $(common_sources) \ + $(ui_sources) \ + $(textui_sources) + +all-local: + for dir in common ui textui; do \ + cd $$dir && \ + if [ ! -f Makefile ]; then \ + $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \ + fi && \ + ($(MAKE) || $(MAKE)) && \ + cd ..; \ + done + +install-exec-local: + for dir in common ui textui; do \ + cd $$dir && $(MAKE) install && cd ..; \ + done + +clean-generic: + rm -f common/Makefile ui/Makefile textui/Makefile + +distclean-generic: + -(cd common && $(MAKE) realclean && rm -f Makefile.PL) + -(cd ui && $(MAKE) realclean && rm -f Makefile.PL) + -(cd textui && $(MAKE) realclean && rm -f Makefile.PL) + +libperl_core_la_LIBADD = $(PERL_LDFLAGS) diff --git a/apps/irssi/src/perl/common/.cvsignore b/apps/irssi/src/perl/common/.cvsignore new file mode 100644 index 00000000..e9658495 --- /dev/null +++ b/apps/irssi/src/perl/common/.cvsignore @@ -0,0 +1,7 @@ +Makefile +Makefile.PL +Irssi.bs +*.c +*.o +pm_to_blib +blib diff --git a/apps/irssi/src/perl/common/Channel.xs b/apps/irssi/src/perl/common/Channel.xs new file mode 100644 index 00000000..f425dc2b --- /dev/null +++ b/apps/irssi/src/perl/common/Channel.xs @@ -0,0 +1,124 @@ +#include "module.h" + +MODULE = Irssi::Channel PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +channels() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = channels; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data))); + } + +Irssi::Channel +channel_find(channel) + char *channel +CODE: + RETVAL = channel_find(NULL, channel); +OUTPUT: + RETVAL + +#******************************* +MODULE = Irssi::Channel PACKAGE = Irssi::Server +#******************************* + +void +channels(server) + Irssi::Server server +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data))); + } + +void +channels_join(server, channels, automatic) + Irssi::Server server + char *channels + int automatic +CODE: + server->channels_join(server, channels, automatic); + +Irssi::Channel +channel_create(server, name, automatic) + Irssi::Server server + char *name + int automatic +CODE: + CHAT_PROTOCOL(server)->channel_create(server, name, automatic); + +Irssi::Channel +channel_find(server, name) + Irssi::Server server + char *name + +void +nicks_get_same(server, nick) + Irssi::Server server + char *nick +PREINIT: + GSList *list, *tmp; +PPCODE: + list = nicklist_get_same(server, nick); + + for (tmp = list; tmp != NULL; tmp = tmp->next->next) { + XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data))); + XPUSHs(sv_2mortal(iobject_bless((NICK_REC *) tmp->next->data))); + } + g_slist_free(list); + +#******************************* +MODULE = Irssi::Channel PACKAGE = Irssi::Channel PREFIX = channel_ +#******************************* + +void +channel_destroy(channel) + Irssi::Channel channel + +void +nick_insert(channel, nick) + Irssi::Channel channel + Irssi::Nick nick +CODE: + nicklist_insert(channel, nick); + +void +nick_remove(channel, nick) + Irssi::Channel channel + Irssi::Nick nick +CODE: + nicklist_remove(channel, nick); + +Irssi::Nick +nick_find(channel, nick) + Irssi::Channel channel + char *nick +CODE: + RETVAL = nicklist_find(channel, nick); +OUTPUT: + RETVAL + +Irssi::Nick +nick_find_mask(channel, mask) + Irssi::Channel channel + char *mask +CODE: + RETVAL = nicklist_find_mask(channel, mask); +OUTPUT: + RETVAL + +void +nicks(channel) + Irssi::Channel channel +PREINIT: + GSList *list, *tmp; +PPCODE: + list = nicklist_getnicks(channel); + + for (tmp = list; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(iobject_bless((NICK_REC *) tmp->data))); + } + g_slist_free(list); diff --git a/apps/irssi/src/perl/common/Core.xs b/apps/irssi/src/perl/common/Core.xs new file mode 100644 index 00000000..4db742c0 --- /dev/null +++ b/apps/irssi/src/perl/common/Core.xs @@ -0,0 +1,528 @@ +#include "module.h" +#include "irssi-version.h" + +#define DEFAULT_COMMAND_CATEGORY "Perl scripts' commands" + +void perl_signal_add_hash(int priority, SV *sv) +{ + HV *hv; + HE *he; + I32 len; + + if (!is_hvref(sv)) + croak("Usage: Irssi::signal_add(hash)"); + + hv = hvref(sv); + hv_iterinit(hv); + while ((he = hv_iternext(hv)) != NULL) + perl_signal_add_to(hv_iterkey(he, &len), HeVAL(he), priority); +} + +static void perl_command_bind_add_hash(int priority, SV *sv, char *category) +{ + HV *hv; + HE *he; + I32 len; + + hv = hvref(sv); + hv_iterinit(hv); + while ((he = hv_iternext(hv)) != NULL) + perl_command_bind_to(hv_iterkey(he, &len), category, HeVAL(he), priority); +} + +static void handle_command_bind(int priority, int items, SV *p0, SV *p1, SV *p2) +{ + char *category; + int hash; + + hash = items > 0 && is_hvref(p0); + if (!hash) { + if (items < 2 || items > 3) + croak("Usage: Irssi::command_bind(signal, func, category)"); + } else if (items > 2) + croak("Usage: Irssi::command_bind(signals_hash, category)"); + + if (!hash) { + category = items < 3 ? DEFAULT_COMMAND_CATEGORY : + (char *)SvPV(p2, PL_na); + perl_command_bind_to((char *)SvPV(p0, PL_na), category, p1, priority); + } else { + category = items < 2 ? DEFAULT_COMMAND_CATEGORY : + (char *)SvPV(p1, PL_na); + perl_command_bind_add_hash(priority, p0, category); + } +} + +MODULE = Irssi::Core PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +signal_emit(signal, ...) + char *signal +CODE: + void *p[SIGNAL_MAX_ARGUMENTS]; + int n; + + memset(p, 0, sizeof(p)); + for (n = 1; n < items && n < SIGNAL_MAX_ARGUMENTS+1; n++) { + if (SvPOKp(ST(n))) + p[n-1] = SvPV(ST(n), PL_na); + else if (irssi_is_ref_object(ST(n))) + p[n-1] = irssi_ref_object(ST(n)); + else if (SvROK(ST(n))) + p[n-1] = (void *) SvIV((SV*)SvRV(ST(n))); + else + p[n-1] = NULL; + } + signal_emit(signal, items-1, p[0], p[1], p[2], p[3], p[4], p[5]); + +void +signal_add(...) +CODE: + if (items != 1 && items != 2) + croak("Usage: Irssi::signal_add(signal, func)"); + if (items == 2) + perl_signal_add((char *)SvPV(ST(0),PL_na), ST(1)); + else + perl_signal_add_hash(1, ST(0)); + +void +signal_add_first(...) +CODE: + if (items != 1 && items != 2) + croak("Usage: Irssi::signal_add_first(signal, func)"); + if (items == 2) + perl_signal_add_first((char *)SvPV(ST(0),PL_na), ST(1)); + else + perl_signal_add_hash(0, ST(0)); + +void +signal_add_last(...) +CODE: + if (items != 1 && items != 2) + croak("Usage: Irssi::signal_add_last(signal, func)"); + if (items == 2) + perl_signal_add_last((char *)SvPV(ST(0),PL_na), ST(1)); + else + perl_signal_add_hash(2, ST(0)); + +void +signal_remove(signal, func) + char *signal + SV *func +CODE: + perl_signal_remove(signal, func); + +void +signal_stop() + +void +signal_stop_by_name(signal) + char *signal + +char * +signal_get_emitted() +CODE: + RETVAL = (char *) signal_get_emitted(); +OUTPUT: + RETVAL + +int +signal_get_emitted_id() + +int +timeout_add(msecs, func, data) + int msecs + SV *func + SV *data +CODE: + RETVAL = perl_timeout_add(msecs, func, data); +OUTPUT: + RETVAL + +void +timeout_remove(tag) + int tag +CODE: + perl_source_remove(tag); + + +int +INPUT_READ() +CODE: + RETVAL = G_INPUT_READ; +OUTPUT: + RETVAL + +int +INPUT_WRITE() +CODE: + RETVAL = G_INPUT_WRITE; +OUTPUT: + RETVAL + +int +input_add(source, condition, func, data) + int source + int condition + SV *func + SV *data +CODE: + RETVAL = perl_input_add(source, condition, func, data); +OUTPUT: + RETVAL + +void +input_remove(tag) + int tag +CODE: + perl_source_remove(tag); + +# maybe there's some easier way than this..? :) +int +MSGLEVEL_CRAP() +CODE: + RETVAL = MSGLEVEL_CRAP; +OUTPUT: + RETVAL + +int +MSGLEVEL_MSGS() +CODE: + RETVAL = MSGLEVEL_MSGS; +OUTPUT: + RETVAL + +int +MSGLEVEL_PUBLIC() +CODE: + RETVAL = MSGLEVEL_PUBLIC; +OUTPUT: + RETVAL + +int +MSGLEVEL_NOTICES() +CODE: + RETVAL = MSGLEVEL_NOTICES; +OUTPUT: + RETVAL + +int +MSGLEVEL_SNOTES() +CODE: + RETVAL = MSGLEVEL_SNOTES; +OUTPUT: + RETVAL + +int +MSGLEVEL_CTCPS() +CODE: + RETVAL = MSGLEVEL_CTCPS; +OUTPUT: + RETVAL + +int +MSGLEVEL_ACTIONS() +CODE: + RETVAL = MSGLEVEL_ACTIONS; +OUTPUT: + RETVAL + +int +MSGLEVEL_JOINS() +CODE: + RETVAL = MSGLEVEL_JOINS; +OUTPUT: + RETVAL + +int +MSGLEVEL_PARTS() +CODE: + RETVAL = MSGLEVEL_PARTS; +OUTPUT: + RETVAL + +int +MSGLEVEL_QUITS() +CODE: + RETVAL = MSGLEVEL_QUITS; +OUTPUT: + RETVAL + +int +MSGLEVEL_KICKS() +CODE: + RETVAL = MSGLEVEL_KICKS; +OUTPUT: + RETVAL + +int +MSGLEVEL_MODES() +CODE: + RETVAL = MSGLEVEL_MODES; +OUTPUT: + RETVAL + +int +MSGLEVEL_TOPICS() +CODE: + RETVAL = MSGLEVEL_TOPICS; +OUTPUT: + RETVAL + +int +MSGLEVEL_WALLOPS() +CODE: + RETVAL = MSGLEVEL_WALLOPS; +OUTPUT: + RETVAL + +int +MSGLEVEL_INVITES() +CODE: + RETVAL = MSGLEVEL_INVITES; +OUTPUT: + RETVAL + +int +MSGLEVEL_NICKS() +CODE: + RETVAL = MSGLEVEL_NICKS; +OUTPUT: + RETVAL + +int +MSGLEVEL_DCC() +CODE: + RETVAL = MSGLEVEL_DCC; +OUTPUT: + RETVAL + +int +MSGLEVEL_DCCMSGS() +CODE: + RETVAL = MSGLEVEL_DCCMSGS; +OUTPUT: + RETVAL + +int +MSGLEVEL_CLIENTNOTICE() +CODE: + RETVAL = MSGLEVEL_CLIENTNOTICE; +OUTPUT: + RETVAL + +int +MSGLEVEL_CLIENTCRAP() +CODE: + RETVAL = MSGLEVEL_CLIENTCRAP; +OUTPUT: + RETVAL + +int +MSGLEVEL_CLIENTERROR() +CODE: + RETVAL = MSGLEVEL_CLIENTERROR; +OUTPUT: + RETVAL + +int +MSGLEVEL_HILIGHT() +CODE: + RETVAL = MSGLEVEL_HILIGHT; +OUTPUT: + RETVAL + +int +MSGLEVEL_ALL() +CODE: + RETVAL = MSGLEVEL_ALL; +OUTPUT: + RETVAL + +int +MSGLEVEL_NOHILIGHT() +CODE: + RETVAL = MSGLEVEL_NOHILIGHT; +OUTPUT: + RETVAL + +int +MSGLEVEL_NO_ACT() +CODE: + RETVAL = MSGLEVEL_NO_ACT; +OUTPUT: + RETVAL + +int +MSGLEVEL_NEVER() +CODE: + RETVAL = MSGLEVEL_NEVER; +OUTPUT: + RETVAL + +int +MSGLEVEL_LASTLOG() +CODE: + RETVAL = MSGLEVEL_LASTLOG; +OUTPUT: + RETVAL + +int +level2bits(str) + char *str + +char * +bits2level(bits) + int bits + +int +combine_level(level, str) + int level + char *str + +void +command(cmd) + char *cmd +CODE: + perl_command(cmd, NULL, NULL); + +void +commands() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = commands; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Command"))); + } + +void +command_bind_first(...) +CODE: + handle_command_bind(0, items, ST(0), ST(1), ST(2)); + +void +command_bind(...) +CODE: + handle_command_bind(1, items, ST(0), ST(1), ST(2)); + +void +command_bind_last(...) +CODE: + handle_command_bind(2, items, ST(0), ST(1), ST(2)); + +void +command_runsub(cmd, data, server, item) + char *cmd + char *data + Irssi::Server server + Irssi::Windowitem item +CODE: + perl_command_runsub(cmd, data, server, item); + +void +command_unbind(cmd, func) + char *cmd + SV *func +CODE: + perl_command_unbind(cmd, func); + +void +command_set_options(cmd, options) + char *cmd + char *options + +void +pidwait_add(pid) + int pid + +void +pidwait_remove(pid) + int pid + +char * +parse_special(cmd, data="", flags=0) + char *cmd + char *data + int flags +PREINIT: + char *ret; +PPCODE: + ret = parse_special_string(cmd, NULL, NULL, data, NULL, flags); + XPUSHs(sv_2mortal(new_pv(ret))); + g_free_not_null(ret); + +char * +get_irssi_dir() +CODE: + RETVAL = (char *) get_irssi_dir(); +OUTPUT: + RETVAL + +char * +get_irssi_config() +CODE: + RETVAL = (char *) get_irssi_config(); +OUTPUT: + RETVAL + +char * +version() +PREINIT: + char version[100]; +CODE: + g_snprintf(version, sizeof(version), "%d.%04d", + IRSSI_VERSION_DATE, IRSSI_VERSION_TIME); + RETVAL = version; +OUTPUT: + RETVAL + +#******************************* +MODULE = Irssi::Core PACKAGE = Irssi::Server +#******************************* + +void +parse_special(server, cmd, data="", flags=0) + Irssi::Server server + char *cmd + char *data + int flags +PREINIT: + char *ret; +PPCODE: + ret = parse_special_string(cmd, server, NULL, data, NULL, flags); + XPUSHs(sv_2mortal(new_pv(ret))); + g_free_not_null(ret); + +void +command(server, cmd) + Irssi::Server server + char *cmd +CODE: + perl_command(cmd, server, NULL); + + +#******************************* +MODULE = Irssi::Core PACKAGE = Irssi::Windowitem +#******************************* + +void +parse_special(item, cmd, data="", flags=0) + Irssi::Windowitem item + char *cmd + char *data + int flags +PREINIT: + char *ret; +PPCODE: + ret = parse_special_string(cmd, item->server, item, data, NULL, flags); + XPUSHs(sv_2mortal(new_pv(ret))); + g_free_not_null(ret); + +void +command(item, cmd) + Irssi::Windowitem item + char *cmd +CODE: + perl_command(cmd, item->server, item); + diff --git a/apps/irssi/src/perl/common/Ignore.xs b/apps/irssi/src/perl/common/Ignore.xs new file mode 100644 index 00000000..e4452c78 --- /dev/null +++ b/apps/irssi/src/perl/common/Ignore.xs @@ -0,0 +1,50 @@ +#include "module.h" + +MODULE = Irssi::Ignore PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +ignores() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = ignores; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Ignore"))); + } + +int +ignore_check(nick, host, channel, text, level) + char *nick + char *host + char *channel + char *text + int level +CODE: + RETVAL = ignore_check(NULL, nick, host, channel, text, level); +OUTPUT: + RETVAL + +#******************************* +MODULE = Irssi::Ignore PACKAGE = Irssi::Server +#******************************* + +int +ignore_check(server, nick, host, channel, text, level) + Irssi::Server server + char *nick + char *host + char *channel + char *text + int level + +#******************************* +MODULE = Irssi::Ignore PACKAGE = Irssi::Ignore PREFIX = ignore_ +#******************************* + +void +ignore_add_rec(rec) + Irssi::Ignore rec + +void +ignore_update_rec(rec) + Irssi::Ignore rec diff --git a/apps/irssi/src/perl/common/Irssi.bs b/apps/irssi/src/perl/common/Irssi.bs new file mode 100644 index 00000000..e69de29b diff --git a/apps/irssi/src/perl/common/Irssi.pm b/apps/irssi/src/perl/common/Irssi.pm new file mode 100644 index 00000000..ce35c52e --- /dev/null +++ b/apps/irssi/src/perl/common/Irssi.pm @@ -0,0 +1,48 @@ +# +# Perl interface to irssi functions. +# + +package Irssi; + +use strict; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +sub VERSION { + my $version = $_[1]; + die "This script requires irssi version $version or later" + if ($version > version()); +} + +sub EXPORT_ALL () { + no strict 'refs'; + @EXPORT_OK = grep { /[a-z]/ && defined *{$_}{CODE} } keys %Irssi::; +} + +$VERSION = "0.9"; + +require Exporter; +require DynaLoader; + +@ISA = qw(Exporter DynaLoader); +@EXPORT = qw(INPUT_READ INPUT_WRITE + MSGLEVEL_CRAP MSGLEVEL_MSGS MSGLEVEL_PUBLIC MSGLEVEL_NOTICES + MSGLEVEL_SNOTES MSGLEVEL_CTCPS MSGLEVEL_ACTIONS MSGLEVEL_JOINS + MSGLEVEL_PARTS MSGLEVEL_QUITS MSGLEVEL_KICKS MSGLEVEL_MODES + MSGLEVEL_TOPICS MSGLEVEL_WALLOPS MSGLEVEL_INVITES MSGLEVEL_NICKS + MSGLEVEL_DCC MSGLEVEL_DCCMSGS MSGLEVEL_CLIENTNOTICE MSGLEVEL_CLIENTCRAP + MSGLEVEL_CLIENTERROR MSGLEVEL_HILIGHT MSGLEVEL_ALL MSGLEVEL_NOHILIGHT + MSGLEVEL_NO_ACT MSGLEVEL_NEVER MSGLEVEL_LASTLOG +); +@EXPORT_OK = qw(); + +bootstrap Irssi $VERSION if (!Irssi::Core::is_static()); + +@Irssi::Channel::ISA = qw(Irssi::Windowitem); +@Irssi::Query::ISA = qw(Irssi::Windowitem); + +Irssi::init(); + +Irssi::EXPORT_ALL(); + +1; + diff --git a/apps/irssi/src/perl/common/Irssi.xs b/apps/irssi/src/perl/common/Irssi.xs new file mode 100644 index 00000000..67d5e96c --- /dev/null +++ b/apps/irssi/src/perl/common/Irssi.xs @@ -0,0 +1,33 @@ +#include "module.h" + +static int initialized = FALSE; + +MODULE = Irssi PACKAGE = Irssi + +PROTOTYPES: ENABLE + +void +init() +CODE: + if (initialized) return; + perl_api_version_check("Irssi"); + initialized = TRUE; + + perl_settings_init(); + +void +deinit() +CODE: + if (!initialized) return; + perl_settings_deinit(); + +BOOT: + irssi_boot(Channel); + irssi_boot(Core); + irssi_boot(Ignore); + irssi_boot(Log); + irssi_boot(Masks); + irssi_boot(Query); + irssi_boot(Rawlog); + irssi_boot(Server); + irssi_boot(Settings); diff --git a/apps/irssi/src/perl/common/Log.xs b/apps/irssi/src/perl/common/Log.xs new file mode 100644 index 00000000..c87ee45b --- /dev/null +++ b/apps/irssi/src/perl/common/Log.xs @@ -0,0 +1,67 @@ +#include "module.h" + +MODULE = Irssi::Log PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +logs() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = logs; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Log"))); + } + +Irssi::Log +log_create_rec(fname, level) + char *fname + int level + +Irssi::Log +log_find(fname) + char *fname + +#******************************* +MODULE = Irssi::Log PACKAGE = Irssi::Log PREFIX = log_ +#******************************* + +void +log_item_add(log, type, name, servertag) + Irssi::Log log + int type + char *name + char *servertag + +void +log_item_destroy(log, item) + Irssi::Log log + Irssi::Logitem item + +Irssi::Logitem +log_item_find(log, type, item, servertag) + Irssi::Log log + int type + char *item + char *servertag + +void +log_update(log) + Irssi::Log log + +void +log_close(log) + Irssi::Log log + +void +log_write_rec(log, str, level) + Irssi::Log log + char *str + int level + +void +log_start_logging(log) + Irssi::Log log + +void +log_stop_logging(log) + Irssi::Log log diff --git a/apps/irssi/src/perl/common/Makefile.PL.in b/apps/irssi/src/perl/common/Makefile.PL.in new file mode 100644 index 00000000..4e545e7c --- /dev/null +++ b/apps/irssi/src/perl/common/Makefile.PL.in @@ -0,0 +1,7 @@ +use ExtUtils::MakeMaker; + +WriteMakefile('NAME' => 'Irssi', + 'LIBS' => '', + 'OBJECT' => '$(O_FILES)', + 'INC' => '-I../../.. -I@top_srcdir@ -I@top_srcdir@/src -I@top_srcdir@/src/core @GLIB_CFLAGS@', + 'VERSION_FROM' => '@srcdir@/Irssi.pm'); diff --git a/apps/irssi/src/perl/common/Masks.xs b/apps/irssi/src/perl/common/Masks.xs new file mode 100644 index 00000000..1ea13969 --- /dev/null +++ b/apps/irssi/src/perl/common/Masks.xs @@ -0,0 +1,61 @@ +#include "module.h" + +MODULE = Irssi::Masks PACKAGE = Irssi +PROTOTYPES: ENABLE + +int +mask_match(mask, nick, user, host) + char *mask + char *nick + char *user + char *host +CODE: + RETVAL = mask_match(NULL, mask, nick, user, host); +OUTPUT: + RETVAL + +int +mask_match_address(mask, nick, address) + char *mask + char *nick + char *address +CODE: + RETVAL = mask_match_address(NULL, mask, nick, address); +OUTPUT: + RETVAL + +int +masks_match(masks, nick, address) + char *masks + char *nick + char *address +CODE: + RETVAL = masks_match(NULL, masks, nick, address); +OUTPUT: + RETVAL + +#******************************* +MODULE = Irssi::Masks PACKAGE = Irssi::Server +#******************************* + +int +mask_match(server, mask, nick, user, host) + Irssi::Server server + char *mask + char *nick + char *user + char *host + +int +mask_match_address(server, mask, nick, address) + Irssi::Server server + char *mask + char *nick + char *address + +int +masks_match(server, masks, nick, address) + Irssi::Server server + char *masks + char *nick + char *address diff --git a/apps/irssi/src/perl/common/Query.xs b/apps/irssi/src/perl/common/Query.xs new file mode 100644 index 00000000..54a0582c --- /dev/null +++ b/apps/irssi/src/perl/common/Query.xs @@ -0,0 +1,57 @@ +#include "module.h" + +MODULE = Irssi::Query PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +queries() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = queries; tmp != NULL; tmp = tmp->next) { + QUERY_REC *rec = tmp->data; + + XPUSHs(sv_2mortal(iobject_bless(rec))); + } + +Irssi::Query +query_find(nick) + char *nick +CODE: + RETVAL = query_find(NULL, nick); +OUTPUT: + RETVAL + +#******************************* +MODULE = Irssi::Query PACKAGE = Irssi::Server +#******************************* + +void +queries(server) + Irssi::Server server +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = server->queries; tmp != NULL; tmp = tmp->next) { + QUERY_REC *rec = tmp->data; + + XPUSHs(sv_2mortal(iobject_bless(rec))); + } + +Irssi::Query +query_find(server, nick) + Irssi::Server server + char *nick + +#******************************* +MODULE = Irssi::Query PACKAGE = Irssi::Query PREFIX = query_ +#******************************* + +void +query_destroy(query) + Irssi::Query query + +void +query_change_server(query, server) + Irssi::Query query + Irssi::Server server diff --git a/apps/irssi/src/perl/common/Rawlog.xs b/apps/irssi/src/perl/common/Rawlog.xs new file mode 100644 index 00000000..dd95ce50 --- /dev/null +++ b/apps/irssi/src/perl/common/Rawlog.xs @@ -0,0 +1,58 @@ +#include "module.h" + +MODULE = Irssi::Rawlog PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +rawlog_set_size(lines) + int lines + +Irssi::Rawlog +rawlog_create() + +#******************************* +MODULE = Irssi::Rawlog PACKAGE = Irssi::Rawlog PREFIX = rawlog_ +#******************************* + +void +rawlog_get_lines(rawlog) + Irssi::Rawlog rawlog +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = rawlog->lines; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(new_pv(tmp->data))); + } + +void +rawlog_destroy(rawlog) + Irssi::Rawlog rawlog + +void +rawlog_input(rawlog, str) + Irssi::Rawlog rawlog + char *str + +void +rawlog_output(rawlog, str) + Irssi::Rawlog rawlog + char *str + +void +rawlog_redirect(rawlog, str) + Irssi::Rawlog rawlog + char *str + +void +rawlog_open(rawlog, fname) + Irssi::Rawlog rawlog + char *fname + +void +rawlog_close(rawlog) + Irssi::Rawlog rawlog + +void +rawlog_save(rawlog, fname) + Irssi::Rawlog rawlog + char *fname diff --git a/apps/irssi/src/perl/common/Server.xs b/apps/irssi/src/perl/common/Server.xs new file mode 100644 index 00000000..adcabb16 --- /dev/null +++ b/apps/irssi/src/perl/common/Server.xs @@ -0,0 +1,104 @@ +#include "module.h" + +MODULE = Irssi::Server PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +servers() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(iobject_bless((SERVER_REC *) tmp->data))); + } + +void +reconnects() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = reconnects; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Reconnect"))); + } + +void +chatnets() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = chatnets; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(iobject_bless((CHATNET_REC *) tmp->data))); + } + +Irssi::Connect +server_create_conn(chat_type, dest, port=6667, chatnet=NULL, password=NULL, nick=NULL) + int chat_type + char *dest + int port + char *chatnet + char *password + char *nick + +Irssi::Server +server_find_tag(tag) + char *tag + +Irssi::Server +server_find_chatnet(chatnet) + char *chatnet + +Irssi::Chatnet +chatnet_find(name) + char *name + +#******************************* +MODULE = Irssi::Server PACKAGE = Irssi::Server PREFIX = server_ +#******************************* + +void +server_disconnect(server) + Irssi::Server server + +void +server_ref(server) + Irssi::Server server + +void +server_unref(server) + Irssi::Server server + +int +isnickflag(server, flag) + Irssi::Server server + char flag +CODE: + RETVAL = server->isnickflag(flag); +OUTPUT: + RETVAL + +int +ischannel(server, data) + Irssi::Server server + char *data +CODE: + RETVAL = server->ischannel(server, data); +OUTPUT: + RETVAL + +char * +get_nick_flags(server) + Irssi::Server server +CODE: + RETVAL = (char *) server->get_nick_flags(); +OUTPUT: + RETVAL + +void +send_message(server, target, msg, target_type) + Irssi::Server server + char *target + char *msg + int target_type +CODE: + server->send_message(server, target, msg, target_type); + diff --git a/apps/irssi/src/perl/common/Settings.xs b/apps/irssi/src/perl/common/Settings.xs new file mode 100644 index 00000000..a7c5e6f6 --- /dev/null +++ b/apps/irssi/src/perl/common/Settings.xs @@ -0,0 +1,134 @@ +#include "module.h" +#include "misc.h" + +static GHashTable *perl_settings; + +static void perl_settings_add(const char *key) +{ + PERL_SCRIPT_REC *script; + GSList *list; + + script = perl_script_find_package(perl_get_package()); + g_return_if_fail(script != NULL); + + list = g_hash_table_lookup(perl_settings, script); + list = g_slist_append(list, g_strdup(key)); + g_hash_table_insert(perl_settings, script, list); +} + +static void perl_settings_remove(const char *key) +{ + PERL_SCRIPT_REC *script; + GSList *list, *pos; + + script = perl_script_find_package(perl_get_package()); + g_return_if_fail(script != NULL); + + list = g_hash_table_lookup(perl_settings, script); + pos = gslist_find_icase_string(list, key); + if (pos != NULL) { + list = g_slist_remove(list, pos->data); + g_hash_table_insert(perl_settings, script, list); + } +} + +static void perl_settings_free(PERL_SCRIPT_REC *script, GSList *list) +{ + g_slist_foreach(list, (GFunc) g_free, NULL); + g_slist_free(list); +} + +static void sig_script_destroyed(PERL_SCRIPT_REC *script) +{ + GSList *list; + + list = g_hash_table_lookup(perl_settings, script); + if (list != NULL) { + g_slist_foreach(list, (GFunc) settings_remove, NULL); + perl_settings_free(script, list); + g_hash_table_remove(perl_settings, script); + } +} + +void perl_settings_init(void) +{ + perl_settings = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + signal_add("script destroyed", (SIGNAL_FUNC) sig_script_destroyed); +} + +void perl_settings_deinit(void) +{ + signal_remove("script destroyed", (SIGNAL_FUNC) sig_script_destroyed); + + g_hash_table_foreach(perl_settings, (GHFunc) perl_settings_free, NULL); + g_hash_table_destroy(perl_settings); +} + +MODULE = Irssi::Settings PACKAGE = Irssi +PROTOTYPES: ENABLE + +char * +settings_get_str(key) + char *key +CODE: + RETVAL = (char *) settings_get_str(key); +OUTPUT: + RETVAL + +int +settings_get_int(key) + char *key + +int +settings_get_bool(key) + char *key + +void +settings_set_str(key, value) + char *key + char *value + +void +settings_set_int(key, value) + char *key + int value + +void +settings_set_bool(key, value) + char *key + int value + +void +settings_add_str(section, key, def) + char *section + char *key + char *def +CODE: + perl_settings_add(key); + settings_add_str_module(MODULE_NAME"/scripts", section, key, def); + +void +settings_add_int(section, key, def) + char *section + char *key + int def +CODE: + perl_settings_add(key); + settings_add_int_module(MODULE_NAME"/scripts", section, key, def); + +void +settings_add_bool(section, key, def) + char *section + char *key + int def +CODE: + perl_settings_add(key); + settings_add_bool_module(MODULE_NAME"/scripts", section, key, def); + +void +settings_remove(key) + char *key +CODE: + perl_settings_remove(key); + settings_remove(key); diff --git a/apps/irssi/src/perl/common/module.h b/apps/irssi/src/perl/common/module.h new file mode 100644 index 00000000..5456f630 --- /dev/null +++ b/apps/irssi/src/perl/common/module.h @@ -0,0 +1,44 @@ +#define NEED_PERL_H +#define HAVE_CONFIG_H +#include "../module.h" +#include + +#include "network.h" +#include "levels.h" +#include "commands.h" +#include "log.h" +#include "rawlog.h" +#include "ignore.h" +#include "settings.h" +#include "masks.h" +#include "special-vars.h" +#include "window-item-def.h" + +#include "chat-protocols.h" +#include "chatnets.h" +#include "servers.h" +#include "servers-reconnect.h" +#include "servers-setup.h" +#include "channels.h" +#include "queries.h" +#include "nicklist.h" + +#include "perl/perl-core.h" +#include "perl/perl-common.h" +#include "perl/perl-signals.h" + +typedef COMMAND_REC *Irssi__Command; +typedef LOG_REC *Irssi__Log; +typedef LOG_ITEM_REC *Irssi__Logitem; +typedef RAWLOG_REC *Irssi__Rawlog; +typedef IGNORE_REC *Irssi__Ignore; +typedef MODULE_REC *Irssi__Module; +typedef WI_ITEM_REC *Irssi__Windowitem; + +typedef CHATNET_REC *Irssi__Chatnet; +typedef SERVER_REC *Irssi__Server; +typedef SERVER_CONNECT_REC *Irssi__Connect; +typedef RECONNECT_REC *Irssi__Reconnect; +typedef CHANNEL_REC *Irssi__Channel; +typedef QUERY_REC *Irssi__Query; +typedef NICK_REC *Irssi__Nick; diff --git a/apps/irssi/src/perl/common/typemap b/apps/irssi/src/perl/common/typemap new file mode 100644 index 00000000..5b7e0c32 --- /dev/null +++ b/apps/irssi/src/perl/common/typemap @@ -0,0 +1,32 @@ +TYPEMAP +Irssi::Chatnet T_IrssiObj +Irssi::Server T_IrssiObj +Irssi::Connect T_IrssiObj +Irssi::Reconnect T_PlainObj +Irssi::Channel T_IrssiObj +Irssi::Query T_IrssiObj +Irssi::Command T_PlainObj +Irssi::Nick T_IrssiObj +Irssi::Ignore T_PlainObj +Irssi::Log T_PlainObj +Irssi::Logitem T_PlainObj +Irssi::Rawlog T_PlainObj +Irssi::Module T_PlainObj +Irssi::Windowitem T_IrssiObj + +INPUT + +T_IrssiObj + $var = irssi_ref_object($arg) + +T_PlainObj + $var = irssi_ref_object($arg) + +OUTPUT + +T_IrssiObj + $arg = iobject_bless((SERVER_REC *)$var); + +T_PlainObj + $arg = plain_bless($var, \"$type\"); + diff --git a/apps/irssi/src/perl/get-signals.pl b/apps/irssi/src/perl/get-signals.pl new file mode 100755 index 00000000..df4667ce --- /dev/null +++ b/apps/irssi/src/perl/get-signals.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +print "static PERL_SIGNAL_ARGS_REC perl_signal_args[] =\n{\n"; + +while () { + chomp; + + next if (!/^ "([^"]*)"(<.*>)?,\s*(.*)/); + next if (/\.\.\./); + next if (/\(/); + + $signal = $1; + $_ = $3; + + s/GList \* of ([^,]*)/glistptr_\1/g; + s/GSList of (\w+)s/gslist_\1/g; + + s/char \*[^,]*/string/g; + s/ulong \*[^,]*/ulongptr/g; + s/int \*[^,]*/intptr/g; + s/int [^,]*/int/g; + + # core + s/CHATNET_REC[^,]*/iobject/g; + s/SERVER_REC[^,]*/iobject/g; + s/RECONNECT_REC[^,]*/iobject/g; + s/CHANNEL_REC[^,]*/iobject/g; + s/QUERY_REC[^,]*/iobject/g; + s/COMMAND_REC[^,]*/Irssi::Command/g; + s/NICK_REC[^,]*/iobject/g; + s/LOG_REC[^,]*/Irssi::Log/g; + s/RAWLOG_REC[^,]*/Irssi::Rawlog/g; + s/IGNORE_REC[^,]*/Irssi::Ignore/g; + s/MODULE_REC[^,]*/Irssi::Module/g; + + # irc + s/BAN_REC[^,]*/Irssi::Irc::Ban/g; + s/NETSPLIT_REC[^,]*/Irssi::Irc::Netsplit/g; + s/NETSPLIT_SERVER_REC[^,]*/Irssi::Irc::Netsplitserver/g; + + # irc modules + s/DCC_REC[^,]*/siobject/g; + s/AUTOIGNORE_REC[^,]*/Irssi::Irc::Autoignore/g; + s/NOTIFYLIST_REC[^,]*/Irssi::Irc::Notifylist/g; + + # fe-common + s/THEME_REC[^,]*/Irssi::UI::Theme/g; + s/KEYINFO_REC[^,]*/Irssi::UI::Keyinfo/g; + s/PROCESS_REC[^,]*/Irssi::UI::Process/g; + s/TEXT_DEST_REC[^,]*/Irssi::UI::TextDest/g; + s/WINDOW_REC[^,]*/Irssi::UI::Window/g; + s/WI_ITEM_REC[^,]*/iobject/g; + + s/([\w\*:]+)(,|$)/"\1"\2/g; + print " { \"$signal\", { $_, NULL } },\n"; +} + +print "\n { NULL }\n};\n"; diff --git a/apps/irssi/src/perl/irssi-core.pl b/apps/irssi/src/perl/irssi-core.pl new file mode 100644 index 00000000..31fbe484 --- /dev/null +++ b/apps/irssi/src/perl/irssi-core.pl @@ -0,0 +1,47 @@ +# NOTE: this is printed through printf()-like function, +# so no extra percent characters. + +# %%d : must be first - 1 if perl libraries are to be linked +# statically with irssi binary, 0 if not +# %%s : must be second - use Irssi; use Irssi::Irc; etc.. +package Irssi::Core; + +use Symbol qw(delete_package); + +sub is_static { + return %d; +} + +sub destroy { + delete_package($_[0]); +} + +sub eval_data { + my ($data, $id) = @_; + destroy("Irssi::Script::$id"); + + my $package = "Irssi::Script::$id"; + my $eval = qq{package $package; %s sub handler { $data; }}; + { + # hide our variables within this block + my ($filename, $package, $data); + eval $eval; + } + die $@ if $@; + + eval {$package->handler;}; + die $@ if $@; +} + +sub eval_file { + my ($filename, $id) = @_; + + local *FH; + open FH, $filename or die "File not found: $filename"; + local($/) = undef; + my $data = ; + close FH; + local($/) = "\n"; + + eval_data($data, $id); +} diff --git a/apps/irssi/src/perl/irssi-core.pl.h b/apps/irssi/src/perl/irssi-core.pl.h new file mode 100644 index 00000000..3e4ec9a8 --- /dev/null +++ b/apps/irssi/src/perl/irssi-core.pl.h @@ -0,0 +1,49 @@ +const char *irssi_core_code = +"# NOTE: this is printed through printf()-like function,\n" +"# so no extra percent characters.\n" +"\n" +"# %%d : must be first - 1 if perl libraries are to be linked \n" +"# statically with irssi binary, 0 if not\n" +"# %%s : must be second - use Irssi; use Irssi::Irc; etc..\n" +"package Irssi::Core;\n" +"\n" +"use Symbol qw(delete_package);\n" +"\n" +"sub is_static {\n" +" return %d;\n" +"}\n" +"\n" +"sub destroy {\n" +" delete_package($_[0]);\n" +"}\n" +"\n" +"sub eval_data {\n" +" my ($data, $id) = @_;\n" +" destroy(\"Irssi::Script::$id\");\n" +"\n" +" my $package = \"Irssi::Script::$id\";\n" +" my $eval = qq{package $package; %s sub handler { $data; }};\n" +" {\n" +" # hide our variables within this block\n" +" my ($filename, $package, $data);\n" +" eval $eval;\n" +" }\n" +" die $@ if $@;\n" +"\n" +" eval {$package->handler;};\n" +" die $@ if $@;\n" +"}\n" +"\n" +"sub eval_file {\n" +" my ($filename, $id) = @_;\n" +"\n" +" local *FH;\n" +" open FH, $filename or die \"File not found: $filename\";\n" +" local($/) = undef;\n" +" my $data = ;\n" +" close FH;\n" +" local($/) = \"\\n\";\n" +"\n" +" eval_data($data, $id);\n" +"}\n" +; diff --git a/apps/irssi/src/perl/libperl_dynaloader.la b/apps/irssi/src/perl/libperl_dynaloader.la new file mode 100644 index 00000000..9117cdbd --- /dev/null +++ b/apps/irssi/src/perl/libperl_dynaloader.la @@ -0,0 +1,25 @@ +# libsilc.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.3.5 (1.385.2.206 2000/05/27 11:12:27) + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='DynaLoader.a' + +# Libraries that this one depends upon. +dependency_libs='' + +# Version information for libsilc. +current=0 +age=0 +revision=0 + +# Is this an already installed library? +installed=no + +# Directory that this library needs to be installed in: +libdir='' diff --git a/apps/irssi/src/perl/libperl_orig.la b/apps/irssi/src/perl/libperl_orig.la new file mode 100644 index 00000000..c83ffc42 --- /dev/null +++ b/apps/irssi/src/perl/libperl_orig.la @@ -0,0 +1,25 @@ +# libsilc.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.3.5 (1.385.2.206 2000/05/27 11:12:27) + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='libperl_orig.a' + +# Libraries that this one depends upon. +dependency_libs='' + +# Version information for libsilc. +current=0 +age=0 +revision=0 + +# Is this an already installed library? +installed=no + +# Directory that this library needs to be installed in: +libdir='' diff --git a/apps/irssi/src/perl/module-fe.h b/apps/irssi/src/perl/module-fe.h new file mode 100644 index 00000000..2dc8c283 --- /dev/null +++ b/apps/irssi/src/perl/module-fe.h @@ -0,0 +1,4 @@ +#include "module.h" + +#undef MODULE_NAME +#define MODULE_NAME "fe-common/perl" diff --git a/apps/irssi/src/perl/module-formats.c b/apps/irssi/src/perl/module-formats.c new file mode 100644 index 00000000..5c2e1c66 --- /dev/null +++ b/apps/irssi/src/perl/module-formats.c @@ -0,0 +1,41 @@ +/* + module-formats.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "formats.h" + +FORMAT_REC feperl_formats[] = { + { MODULE_NAME, "Core", 0 }, + + /* ---- */ + { NULL, "Perl", 0 }, + + { "script_not_found", "Script {hilight $0} not found", 1, { 0 } }, + { "script_not_loaded", "Script {hilight $0} is not loaded", 1, { 0 } }, + { "script_loaded", "Loaded script {hilight $0}", 2, { 0, 0 } }, + { "script_unloaded", "Unloaded script {hilight $0}", 1, { 0 } }, + { "no_scripts_loaded", "No scripts are loaded", 0 }, + { "script_list_header", "Loaded scripts:", 0 }, + { "script_list_line", "$[!15]0 $1", 2, { 0, 0 } }, + { "script_list_footer", "", 0 }, + { "script_error", "{error Error in script {hilight $0}:}", 1, { 0 } }, + + { NULL, NULL, 0 } +}; diff --git a/apps/irssi/src/perl/module-formats.h b/apps/irssi/src/perl/module-formats.h new file mode 100644 index 00000000..74d2e13b --- /dev/null +++ b/apps/irssi/src/perl/module-formats.h @@ -0,0 +1,19 @@ +#include "formats.h" + +enum { + IRCTXT_MODULE_NAME, + + IRCTXT_FILL_1, + + TXT_SCRIPT_NOT_FOUND, + TXT_SCRIPT_NOT_LOADED, + TXT_SCRIPT_LOADED, + TXT_SCRIPT_UNLOADED, + TXT_NO_SCRIPTS_LOADED, + TXT_SCRIPT_LIST_HEADER, + TXT_SCRIPT_LIST_LINE, + TXT_SCRIPT_LIST_FOOTER, + TXT_SCRIPT_ERROR +}; + +extern FORMAT_REC feperl_formats[]; diff --git a/apps/irssi/src/perl/module.h b/apps/irssi/src/perl/module.h new file mode 100644 index 00000000..e12482ab --- /dev/null +++ b/apps/irssi/src/perl/module.h @@ -0,0 +1,25 @@ +#ifdef NEED_PERL_H +# include +# ifndef _SEM_SEMUN_UNDEFINED +# define HAS_UNION_SEMUN +# endif +# include + +# undef _ +# undef PACKAGE + +/* For compatibility with perl 5.004 and older */ +# ifndef ERRSV +# define ERRSV GvSV(errgv) +# endif + +extern PerlInterpreter *my_perl; /* must be called my_perl or some perl implementations won't work */ +#endif + +#include "common.h" + +#define MODULE_NAME "perl/core" + +/* Change this every time when some API changes between irssi's perl module + (or irssi itself) and irssi's perl libraries. */ +#define IRSSI_PERL_API_VERSION 20011214 diff --git a/apps/irssi/src/perl/perl-common.c b/apps/irssi/src/perl/perl-common.c new file mode 100644 index 00000000..76fa3f3f --- /dev/null +++ b/apps/irssi/src/perl/perl-common.c @@ -0,0 +1,647 @@ +/* + perl-common.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define NEED_PERL_H +#include "module.h" +#include "modules.h" +#include "signals.h" +#include "core.h" +#include "misc.h" +#include "settings.h" + +#include "commands.h" +#include "ignore.h" +#include "log.h" +#include "rawlog.h" +#include "servers-reconnect.h" + +#include "window-item-def.h" +#include "chat-protocols.h" +#include "chatnets.h" +#include "servers.h" +#include "channels.h" +#include "queries.h" +#include "nicklist.h" + +#include "perl-common.h" + +typedef struct { + char *stash; + PERL_OBJECT_FUNC fill_func; +} PERL_OBJECT_REC; + +#ifndef HAVE_PL_PERL +STRLEN PL_na; +#endif + +static GHashTable *iobject_stashes, *plain_stashes; +static GSList *use_protocols; + +/* returns the package who called us */ +const char *perl_get_package(void) +{ + return SvPV(perl_eval_pv("caller", TRUE), PL_na); +} + +/* Parses the package part from function name */ +char *perl_function_get_package(const char *function) +{ + const char *p; + int pos; + + pos = 0; + for (p = function; *p != '\0'; p++) { + if (*p == ':' && p[1] == ':') { + if (++pos == 3) + return g_strndup(function, (int) (p-function)); + } + } + + return NULL; +} + +SV *perl_func_sv_inc(SV *func, const char *package) +{ + char *name; + + if (SvPOK(func)) { + /* prefix with package name */ + name = g_strdup_printf("%s::%s", package, + (char *) SvPV(func, PL_na)); + func = new_pv(name); + g_free(name); + } else { + SvREFCNT_inc(func); + } + + return func; +} + +SV *irssi_bless_iobject(int type, int chat_type, void *object) +{ + PERL_OBJECT_REC *rec; + HV *stash, *hv; + + g_return_val_if_fail((type & ~0xffff) == 0, NULL); + g_return_val_if_fail((chat_type & ~0xffff) == 0, NULL); + + rec = g_hash_table_lookup(iobject_stashes, + GINT_TO_POINTER(type | (chat_type << 16))); + if (rec == NULL) { + /* unknown iobject */ + return newSViv(GPOINTER_TO_INT(object)); + } + + stash = gv_stashpv(rec->stash, 1); + + hv = newHV(); + hv_store(hv, "_irssi", 6, newSViv(GPOINTER_TO_INT(object)), 0); + rec->fill_func(hv, object); + return sv_bless(newRV_noinc((SV*)hv), stash); +} + +SV *irssi_bless_plain(const char *stash, void *object) +{ + PERL_OBJECT_FUNC fill_func; + HV *hv; + + fill_func = g_hash_table_lookup(plain_stashes, stash); + + hv = newHV(); + hv_store(hv, "_irssi", 6, newSViv(GPOINTER_TO_INT(object)), 0); + if (fill_func != NULL) + fill_func(hv, object); + return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 1)); +} + +int irssi_is_ref_object(SV *o) +{ + SV **sv; + HV *hv; + + hv = hvref(o); + if (hv != NULL) { + sv = hv_fetch(hv, "_irssi", 6, 0); + if (sv != NULL) + return TRUE; + } + + return FALSE; +} + +void *irssi_ref_object(SV *o) +{ + SV **sv; + HV *hv; + + hv = hvref(o); + if (hv == NULL) + return NULL; + + sv = hv_fetch(hv, "_irssi", 6, 0); + if (sv == NULL) + croak("variable is damaged"); + return GINT_TO_POINTER(SvIV(*sv)); +} + +void irssi_add_object(int type, int chat_type, const char *stash, + PERL_OBJECT_FUNC func) +{ + PERL_OBJECT_REC *rec; + void *hash; + + g_return_if_fail((type & ~0xffff) == 0); + g_return_if_fail((chat_type & ~0xffff) == 0); + + hash = GINT_TO_POINTER(type | (chat_type << 16)); + rec = g_hash_table_lookup(iobject_stashes, hash); + if (rec == NULL) { + rec = g_new(PERL_OBJECT_REC, 1); + rec->stash = g_strdup(stash); + g_hash_table_insert(iobject_stashes, hash, rec); + } + rec->fill_func = func; +} + +void irssi_add_plain(const char *stash, PERL_OBJECT_FUNC func) +{ + if (g_hash_table_lookup(plain_stashes, stash) == NULL) + g_hash_table_insert(plain_stashes, g_strdup(stash), func); +} + +void irssi_add_plains(PLAIN_OBJECT_INIT_REC *objects) +{ + while (objects->name != NULL) { + irssi_add_plain(objects->name, objects->fill_func); + objects++; + } +} + +char *perl_get_use_list(void) +{ + GString *str; + GSList *tmp; + char *ret; + const char *use_lib; + + str = g_string_new(NULL); + + use_lib = settings_get_str("perl_use_lib"); + g_string_sprintf(str, "use lib qw(%s/scripts "SCRIPTDIR" %s);", + get_irssi_dir(), use_lib); + + g_string_append(str, "use Irssi;"); + if (irssi_gui != IRSSI_GUI_NONE) + g_string_append(str, "use Irssi::UI;"); + + for (tmp = use_protocols; tmp != NULL; tmp = tmp->next) + g_string_sprintfa(str, "use Irssi::%s;", (char *) tmp->data); + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +void irssi_callXS(void (*subaddr)(CV* cv), CV *cv, SV **mark) +{ + dSP; + + PUSHMARK(mark); + (*subaddr)(cv); + + PUTBACK; +} + +void perl_chatnet_fill_hash(HV *hv, CHATNET_REC *chatnet) +{ + char *type, *chat_type; + + g_return_if_fail(hv != NULL); + g_return_if_fail(chatnet != NULL); + + type = "CHATNET"; + chat_type = (char *) chat_protocol_find_id(chatnet->chat_type)->name; + + hv_store(hv, "type", 4, new_pv(type), 0); + hv_store(hv, "chat_type", 9, new_pv(chat_type), 0); + + hv_store(hv, "name", 4, new_pv(chatnet->name), 0); + + hv_store(hv, "nick", 4, new_pv(chatnet->nick), 0); + hv_store(hv, "username", 8, new_pv(chatnet->username), 0); + hv_store(hv, "realname", 8, new_pv(chatnet->realname), 0); + + hv_store(hv, "own_host", 8, new_pv(chatnet->own_host), 0); + hv_store(hv, "autosendcmd", 11, new_pv(chatnet->autosendcmd), 0); +} + +void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn) +{ + char *type, *chat_type; + + g_return_if_fail(hv != NULL); + g_return_if_fail(conn != NULL); + + type = "SERVER CONNECT"; + chat_type = (char *) chat_protocol_find_id(conn->chat_type)->name; + + hv_store(hv, "type", 4, new_pv(type), 0); + hv_store(hv, "chat_type", 9, new_pv(chat_type), 0); + + hv_store(hv, "address", 7, new_pv(conn->address), 0); + hv_store(hv, "port", 4, newSViv(conn->port), 0); + hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0); + + hv_store(hv, "password", 8, new_pv(conn->password), 0); + hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0); + hv_store(hv, "username", 8, new_pv(conn->username), 0); + hv_store(hv, "realname", 8, new_pv(conn->realname), 0); +} + +void perl_server_fill_hash(HV *hv, SERVER_REC *server) +{ + char *type; + HV *stash; + + g_return_if_fail(hv != NULL); + g_return_if_fail(server != NULL); + + perl_connect_fill_hash(hv, server->connrec); + + type = "SERVER"; + hv_store(hv, "type", 4, new_pv(type), 0); + + hv_store(hv, "connect_time", 12, newSViv(server->connect_time), 0); + hv_store(hv, "real_connect_time", 17, newSViv(server->real_connect_time), 0); + + hv_store(hv, "tag", 3, new_pv(server->tag), 0); + hv_store(hv, "nick", 4, new_pv(server->nick), 0); + + hv_store(hv, "connected", 9, newSViv(server->connected), 0); + hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0); + + stash = gv_stashpv("Irssi::Rawlog", 0); + hv_store(hv, "rawlog", 6, sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(server->rawlog))), stash), 0); + + hv_store(hv, "version", 7, new_pv(server->version), 0); + hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0); + hv_store(hv, "last_invite", 11, new_pv(server->last_invite), 0); + hv_store(hv, "server_operator", 15, newSViv(server->server_operator), 0); + hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0); + hv_store(hv, "banned", 6, newSViv(server->banned), 0); + + hv_store(hv, "lag", 3, newSViv(server->lag), 0); +} + +void perl_window_item_fill_hash(HV *hv, WI_ITEM_REC *item) +{ + char *type, *chat_type; + + g_return_if_fail(hv != NULL); + g_return_if_fail(item != NULL); + + type = (char *) module_find_id_str("WINDOW ITEM TYPE", item->type); + chat_type = (char *) chat_protocol_find_id(item->chat_type)->name; + + hv_store(hv, "type", 4, new_pv(type), 0); + hv_store(hv, "chat_type", 9, new_pv(chat_type), 0); + + if (item->server != NULL) { + hv_store(hv, "server", 6, iobject_bless(item->server), 0); + } + hv_store(hv, "name", 4, new_pv(item->name), 0); + + hv_store(hv, "createtime", 10, newSViv(item->createtime), 0); + hv_store(hv, "data_level", 8, newSViv(item->data_level), 0); + hv_store(hv, "hilight_color", 10, new_pv(item->hilight_color), 0); +} + +void perl_channel_fill_hash(HV *hv, CHANNEL_REC *channel) +{ + g_return_if_fail(hv != NULL); + g_return_if_fail(channel != NULL); + + perl_window_item_fill_hash(hv, (WI_ITEM_REC *) channel); + + hv_store(hv, "topic", 5, new_pv(channel->topic), 0); + hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0); + hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0); + + hv_store(hv, "no_modes", 8, newSViv(channel->no_modes), 0); + hv_store(hv, "mode", 4, new_pv(channel->mode), 0); + hv_store(hv, "limit", 5, newSViv(channel->limit), 0); + hv_store(hv, "key", 3, new_pv(channel->key), 0); + + hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0); + hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0); + hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0); + hv_store(hv, "synced", 6, newSViv(channel->synced), 0); + + hv_store(hv, "joined", 6, newSViv(channel->joined), 0); + hv_store(hv, "left", 4, newSViv(channel->left), 0); + hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0); +} + +void perl_query_fill_hash(HV *hv, QUERY_REC *query) +{ + g_return_if_fail(hv != NULL); + g_return_if_fail(query != NULL); + + perl_window_item_fill_hash(hv, (WI_ITEM_REC *) query); + + hv_store(hv, "address", 7, new_pv(query->address), 0); + hv_store(hv, "server_tag", 10, new_pv(query->server_tag), 0); + hv_store(hv, "unwanted", 8, newSViv(query->unwanted), 0); +} + +void perl_nick_fill_hash(HV *hv, NICK_REC *nick) +{ + char *type, *chat_type; + + g_return_if_fail(hv != NULL); + g_return_if_fail(nick != NULL); + + type = "NICK"; + chat_type = (char *) chat_protocol_find_id(nick->chat_type)->name; + + hv_store(hv, "type", 4, new_pv(type), 0); + hv_store(hv, "chat_type", 9, new_pv(chat_type), 0); + + hv_store(hv, "nick", 4, new_pv(nick->nick), 0); + hv_store(hv, "host", 4, new_pv(nick->host), 0); + hv_store(hv, "realname", 8, new_pv(nick->realname), 0); + hv_store(hv, "hops", 4, newSViv(nick->hops), 0); + + hv_store(hv, "gone", 4, newSViv(nick->gone), 0); + hv_store(hv, "serverop", 8, newSViv(nick->serverop), 0); + + hv_store(hv, "op", 2, newSViv(nick->op), 0); + hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0); + hv_store(hv, "voice", 5, newSViv(nick->voice), 0); + + hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0); + hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0); +} + +static void perl_command_fill_hash(HV *hv, COMMAND_REC *cmd) +{ + hv_store(hv, "category", 8, new_pv(cmd->category), 0); + hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0); +} + +static void perl_ignore_fill_hash(HV *hv, IGNORE_REC *ignore) +{ + AV *av; + char **tmp; + + hv_store(hv, "mask", 4, new_pv(ignore->mask), 0); + hv_store(hv, "servertag", 9, new_pv(ignore->servertag), 0); + av = newAV(); + for (tmp = ignore->channels; *tmp != NULL; tmp++) { + av_push(av, new_pv(*tmp)); + } + hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0); + hv_store(hv, "pattern", 7, new_pv(ignore->pattern), 0); + + hv_store(hv, "level", 5, newSViv(ignore->level), 0); + + hv_store(hv, "exception", 6, newSViv(ignore->exception), 0); + hv_store(hv, "regexp", 6, newSViv(ignore->regexp), 0); + hv_store(hv, "fullword", 8, newSViv(ignore->fullword), 0); +} + +static void perl_log_fill_hash(HV *hv, LOG_REC *log) +{ + AV *av; + GSList *tmp; + + hv_store(hv, "fname", 5, new_pv(log->fname), 0); + hv_store(hv, "real_fname", 10, new_pv(log->real_fname), 0); + hv_store(hv, "opened", 6, newSViv(log->opened), 0); + hv_store(hv, "level", 5, newSViv(log->level), 0); + hv_store(hv, "last", 4, newSViv(log->last), 0); + hv_store(hv, "autoopen", 8, newSViv(log->autoopen), 0); + hv_store(hv, "failed", 6, newSViv(log->failed), 0); + hv_store(hv, "temp", 4, newSViv(log->temp), 0); + + av = newAV(); + for (tmp = log->items; tmp != NULL; tmp = tmp->next) { + av_push(av, plain_bless(tmp->data, "Irssi::Logitem")); + } + hv_store(hv, "items", 5, newRV_noinc((SV*)av), 0); +} + +static void perl_log_item_fill_hash(HV *hv, LOG_ITEM_REC *item) +{ + hv_store(hv, "type", 4, newSViv(item->type), 0); + hv_store(hv, "name", 4, new_pv(item->name), 0); + hv_store(hv, "servertag", 9, new_pv(item->servertag), 0); +} + +static void perl_rawlog_fill_hash(HV *hv, RAWLOG_REC *rawlog) +{ + hv_store(hv, "logging", 7, newSViv(rawlog->logging), 0); + hv_store(hv, "nlines", 6, newSViv(rawlog->nlines), 0); +} + +static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect) +{ + char *type; + + perl_connect_fill_hash(hv, reconnect->conn); + + type = "RECONNECT"; + hv_store(hv, "type", 4, new_pv(type), 0); + + hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0); + hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0); +} + +void perl_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item) +{ + const char *cmdchars; + char *sendcmd = (char *) cmd; + + if (*cmd == '\0') + return; + + cmdchars = settings_get_str("cmdchars"); + if (strchr(cmdchars, *cmd) == NULL) { + /* no command char - let's put it there.. */ + sendcmd = g_strdup_printf("%c%s", *cmdchars, cmd); + } + + signal_emit("send command", 3, sendcmd, server, item); + if (sendcmd != cmd) g_free(sendcmd); +} + +static void perl_register_protocol(CHAT_PROTOCOL_REC *rec) +{ + static char *items[] = { + "Chatnet", + "Server", "ServerConnect", "ServerSetup", + "Channel", "Query", + "Nick" + }; + static char *find_use_code = + "my $pkg = Irssi::%s; $pkg =~ s/::/\\//;\n" + "foreach my $i (@INC) {\n" + " return 1 if (-f \"$i/$pkg.pm\");\n" + "}\n" + "return 0;\n"; + + char *name, stash[100], code[100], *pcode; + int type, chat_type, n; + SV *sv; + + chat_type = chat_protocol_lookup(rec->name); + g_return_if_fail(chat_type >= 0); + + name = g_strdup(rec->name); + g_strdown(name+1); + + /* window items: channel, query */ + type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL"); + g_snprintf(stash, sizeof(stash), "Irssi::%s::Channel", name); + irssi_add_object(type, chat_type, stash, + (PERL_OBJECT_FUNC) perl_channel_fill_hash); + + type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY"); + g_snprintf(stash, sizeof(stash), "Irssi::%s::Query", name); + irssi_add_object(type, chat_type, stash, + (PERL_OBJECT_FUNC) perl_query_fill_hash); + + /* channel nicks */ + type = module_get_uniq_id("NICK", 0); + g_snprintf(stash, sizeof(stash), "Irssi::%s::Nick", name); + irssi_add_object(type, chat_type, stash, + (PERL_OBJECT_FUNC) perl_nick_fill_hash); + + /* chatnets */ + type = module_get_uniq_id("CHATNET", 0); + g_snprintf(stash, sizeof(stash), "Irssi::%s::Chatnet", name); + irssi_add_object(type, chat_type, stash, + (PERL_OBJECT_FUNC) perl_chatnet_fill_hash); + + /* server specific */ + type = module_get_uniq_id("SERVER", 0); + g_snprintf(stash, sizeof(stash), "Irssi::%s::Server", name); + irssi_add_object(type, chat_type, stash, + (PERL_OBJECT_FUNC) perl_server_fill_hash); + + type = module_get_uniq_id("SERVER CONNECT", 0); + g_snprintf(stash, sizeof(stash), "Irssi::%s::Connect", name); + irssi_add_object(type, chat_type, stash, + (PERL_OBJECT_FUNC) perl_connect_fill_hash); + + /* register ISAs */ + for (n = 0; n < sizeof(items)/sizeof(items[0]); n++) { + g_snprintf(code, sizeof(code), + "@Irssi::%s::%s::ISA = qw(Irssi::%s);", + name, items[n], items[n]); + perl_eval_pv(code, TRUE); + } + + pcode = g_strdup_printf(find_use_code, name); + sv = perl_eval_pv(pcode, TRUE); + g_free(pcode); + + if (SvIV(sv)) { + use_protocols = + g_slist_append(use_protocols, g_strdup(name)); + } + + g_free(name); +} + +static void free_iobject_hash(void *key, PERL_OBJECT_REC *rec) +{ + g_free(rec->stash); + g_free(rec); +} + +static int free_iobject_proto(void *key, void *value, void *chat_type) +{ + if ((GPOINTER_TO_INT(key) >> 16) == GPOINTER_TO_INT(chat_type)) { + free_iobject_hash(key, value); + return TRUE; + } + + return FALSE; +} + +static void perl_unregister_protocol(CHAT_PROTOCOL_REC *rec) +{ + GSList *item; + + item = gslist_find_icase_string(use_protocols, rec->name); + if (item != NULL) { + g_free(item->data); + use_protocols = + g_slist_remove(use_protocols, item->data); + } + g_hash_table_foreach_remove(iobject_stashes, + (GHRFunc) free_iobject_proto, + GINT_TO_POINTER(rec->id)); +} + +void perl_common_start(void) +{ + static PLAIN_OBJECT_INIT_REC core_plains[] = { + { "Irssi::Command", (PERL_OBJECT_FUNC) perl_command_fill_hash }, + { "Irssi::Ignore", (PERL_OBJECT_FUNC) perl_ignore_fill_hash }, + { "Irssi::Log", (PERL_OBJECT_FUNC) perl_log_fill_hash }, + { "Irssi::Logitem", (PERL_OBJECT_FUNC) perl_log_item_fill_hash }, + { "Irssi::Rawlog", (PERL_OBJECT_FUNC) perl_rawlog_fill_hash }, + { "Irssi::Reconnect", (PERL_OBJECT_FUNC) perl_reconnect_fill_hash }, + + { NULL, NULL } + }; + + iobject_stashes = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + plain_stashes = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + irssi_add_plains(core_plains); + + use_protocols = NULL; + g_slist_foreach(chat_protocols, (GFunc) perl_register_protocol, NULL); + + signal_add("chat protocol created", (SIGNAL_FUNC) perl_register_protocol); + signal_add("chat protocol destroyed", (SIGNAL_FUNC) perl_unregister_protocol); +} + +void perl_common_stop(void) +{ + g_hash_table_foreach(iobject_stashes, (GHFunc) free_iobject_hash, NULL); + g_hash_table_destroy(iobject_stashes); + iobject_stashes = NULL; + + g_hash_table_foreach(plain_stashes, (GHFunc) g_free, NULL); + g_hash_table_destroy(plain_stashes); + plain_stashes = NULL; + + g_slist_foreach(use_protocols, (GFunc) g_free, NULL); + g_slist_free(use_protocols); + use_protocols = NULL; + + signal_remove("chat protocol created", (SIGNAL_FUNC) perl_register_protocol); + signal_remove("chat protocol destroyed", (SIGNAL_FUNC) perl_unregister_protocol); +} diff --git a/apps/irssi/src/perl/perl-common.h b/apps/irssi/src/perl/perl-common.h new file mode 100644 index 00000000..9fa85f07 --- /dev/null +++ b/apps/irssi/src/perl/perl-common.h @@ -0,0 +1,68 @@ +#ifndef __PERL_COMMON_H +#define __PERL_COMMON_H + +/* helper defines */ +#define new_pv(a) \ + (newSVpv((a) == NULL ? "" : (a), (a) == NULL ? 0 : strlen(a))) + +#define is_hvref(o) \ + ((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVHV)) + +#define hvref(o) \ + (is_hvref(o) ? (HV *)SvRV(o) : NULL) + +typedef void (*PERL_OBJECT_FUNC) (HV *hv, void *object); + +typedef struct { + char *name; + PERL_OBJECT_FUNC fill_func; +} PLAIN_OBJECT_INIT_REC; + +/* Returns the package who called us */ +const char *perl_get_package(void); +/* Parses the package part from function name */ +char *perl_function_get_package(const char *function); +/* If SV is a string, prefix it with given package. + Increases the reference counter for the return value. */ +SV *perl_func_sv_inc(SV *func, const char *package); + +/* For compatibility with perl 5.004 and older */ +#ifndef HAVE_PL_PERL +# define PL_sv_undef sv_undef +extern STRLEN PL_na; +#endif + +#define iobject_bless(object) \ + ((object) == NULL ? &PL_sv_undef : \ + irssi_bless_iobject((object)->type, (object)->chat_type, object)) + +#define simple_iobject_bless(object) \ + ((object) == NULL ? &PL_sv_undef : \ + irssi_bless_iobject((object)->type, 0, object)) + +#define plain_bless(object, stash) \ + ((object) == NULL ? &PL_sv_undef : \ + irssi_bless_plain(stash, object)) + +SV *irssi_bless_iobject(int type, int chat_type, void *object); +SV *irssi_bless_plain(const char *stash, void *object); +int irssi_is_ref_object(SV *o); +void *irssi_ref_object(SV *o); + +void irssi_add_object(int type, int chat_type, const char *stash, + PERL_OBJECT_FUNC func); +void irssi_add_plain(const char *stash, PERL_OBJECT_FUNC func); +void irssi_add_plains(PLAIN_OBJECT_INIT_REC *objects); + +char *perl_get_use_list(void); + +#define irssi_boot(x) { \ + extern void boot_Irssi__##x(CV *cv); \ + irssi_callXS(boot_Irssi__##x, cv, mark); \ + } +void irssi_callXS(void (*subaddr)(CV* cv), CV *cv, SV **mark); + +void perl_common_start(void); +void perl_common_stop(void); + +#endif diff --git a/apps/irssi/src/perl/perl-core.c b/apps/irssi/src/perl/perl-core.c new file mode 100644 index 00000000..1ed95c37 --- /dev/null +++ b/apps/irssi/src/perl/perl-core.c @@ -0,0 +1,469 @@ +/* + perl-core.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define NEED_PERL_H +#include "module.h" +#include "modules.h" +#include "core.h" +#include "signals.h" +#include "misc.h" +#include "settings.h" +#include "lib-config/iconfig.h" /* FIXME: remove before .99 */ + +#include "perl-core.h" +#include "perl-common.h" +#include "perl-signals.h" +#include "perl-sources.h" + +#include "XSUB.h" +#include "irssi-core.pl.h" + +/* For compatibility with perl 5.004 and older */ +#ifndef HAVE_PL_PERL +# define PL_perl_destruct_level perl_destruct_level +#endif + +GSList *perl_scripts; +PerlInterpreter *my_perl; + +static int print_script_errors; + +#define IS_PERL_SCRIPT(file) \ + (strlen(file) > 3 && strcmp(file+strlen(file)-3, ".pl") == 0) + +static void perl_script_destroy_package(PERL_SCRIPT_REC *script) +{ + dSP; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(new_pv(script->package))); + PUTBACK; + + perl_call_pv("Irssi::Core::destroy", G_VOID|G_EVAL|G_DISCARD); + + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; +} + +static void perl_script_destroy(PERL_SCRIPT_REC *script) +{ + perl_scripts = g_slist_remove(perl_scripts, script); + + signal_emit("script destroyed", 1, script); + + perl_signal_remove_script(script); + perl_source_remove_script(script); + + g_free(script->name); + g_free(script->package); + g_free_not_null(script->path); + g_free_not_null(script->data); + g_free(script); +} + +extern void boot_DynaLoader(CV* cv); + +#if PERL_STATIC_LIBS == 1 +extern void boot_Irssi(CV *cv); + +XS(boot_Irssi_Core) +{ + dXSARGS; + + irssi_callXS(boot_Irssi, cv, mark); + irssi_boot(Irc); + irssi_boot(UI); + irssi_boot(TextUI); + XSRETURN_YES; +} +#endif + +static void xs_init(void) +{ + dXSUB_SYS; + +#if PERL_STATIC_LIBS == 1 + newXS("Irssi::Core::boot_Irssi_Core", boot_Irssi_Core, __FILE__); +#endif + + /* boot the dynaloader too, if we want to use some + other dynamic modules.. */ + newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__); +} + +/* Initialize perl interpreter */ +void perl_scripts_init(void) +{ + char *args[] = {"", "-e", "0"}; + char *code, *use_code; + + perl_scripts = NULL; + perl_sources_start(); + perl_signals_start(); + + my_perl = perl_alloc(); + perl_construct(my_perl); + + perl_parse(my_perl, xs_init, 3, args, NULL); +#if PERL_STATIC_LIBS == 1 + perl_eval_pv("Irssi::Core::boot_Irssi_Core();", TRUE); +#endif + + perl_common_start(); + + use_code = perl_get_use_list(); + code = g_strdup_printf(irssi_core_code, PERL_STATIC_LIBS, use_code); + perl_eval_pv(code, TRUE); + + g_free(code); + g_free(use_code); +} + +/* Destroy all perl scripts and deinitialize perl interpreter */ +void perl_scripts_deinit(void) +{ + if (my_perl == NULL) + return; + + /* unload all scripts */ + while (perl_scripts != NULL) + perl_script_unload(perl_scripts->data); + + signal_emit("perl scripts deinit", 0); + + perl_signals_stop(); + perl_sources_stop(); + perl_common_stop(); + + /* Unload all perl libraries loaded with dynaloader */ + perl_eval_pv("foreach my $lib (@DynaLoader::dl_modules) { if ($lib =~ /^Irssi\\b/) { $lib .= '::deinit();'; eval $lib; } }", TRUE); + perl_eval_pv("eval { foreach my $lib (@DynaLoader::dl_librefs) { DynaLoader::dl_unload_file($lib); } }", TRUE); + + /* perl interpreter */ + perl_destruct(my_perl); + perl_free(my_perl); + my_perl = NULL; +} + +/* Modify the script name so that all non-alphanumeric characters are + translated to '_' */ +void script_fix_name(char *name) +{ + char *p; + + p = strrchr(name, '.'); + if (p != NULL) *p = '\0'; + + while (*name != '\0') { + if (*name != '_' && !i_isalnum(*name)) + *name = '_'; + name++; + } +} + +static char *script_file_get_name(const char *path) +{ + char *name; + + name = g_strdup(g_basename(path)); + script_fix_name(name); + return name; +} + +static char *script_data_get_name(void) +{ + GString *name; + char *ret; + int n; + + name = g_string_new(NULL); + n = 1; + do { + g_string_sprintf(name, "data%d", n); + n++; + } while (perl_script_find(name->str) != NULL); + + ret = name->str; + g_string_free(name, FALSE); + return ret; +} + +static int perl_script_eval(PERL_SCRIPT_REC *script) +{ + dSP; + char *error; + int retcount; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(new_pv(script->path != NULL ? script->path : + script->data))); + XPUSHs(sv_2mortal(new_pv(script->name))); + PUTBACK; + + retcount = perl_call_pv(script->path != NULL ? + "Irssi::Core::eval_file" : + "Irssi::Core::eval_data", + G_EVAL|G_SCALAR); + SPAGAIN; + + error = NULL; + if (SvTRUE(ERRSV)) { + error = SvPV(ERRSV, PL_na); + } else if (retcount > 0) { + error = POPp; + } + + if (error != NULL) { + if (*error == '\0') + error = NULL; + else { + error = g_strdup(error); + signal_emit("script error", 2, script, error); + g_free(error); + } + } + + PUTBACK; + FREETMPS; + LEAVE; + + return error == NULL; +} + +/* NOTE: name must not be free'd */ +static PERL_SCRIPT_REC *script_load(char *name, const char *path, + const char *data) +{ + PERL_SCRIPT_REC *script; + + /* if there's a script with a same name, destroy it */ + script = perl_script_find(name); + if (script != NULL) + perl_script_destroy(script); + + script = g_new0(PERL_SCRIPT_REC, 1); + script->name = name; + script->package = g_strdup_printf("Irssi::Script::%s", name); + script->path = g_strdup(path); + script->data = g_strdup(data); + + perl_scripts = g_slist_append(perl_scripts, script); + signal_emit("script created", 1, script); + + if (!perl_script_eval(script)) + script = NULL; /* the script is destroyed in "script error" signal */ + return script; +} + +/* Load a perl script, path must be a full path. */ +PERL_SCRIPT_REC *perl_script_load_file(const char *path) +{ + char *name; + + g_return_val_if_fail(path != NULL, NULL); + + name = script_file_get_name(path); + return script_load(name, path, NULL); +} + +/* Load a perl script from given data */ +PERL_SCRIPT_REC *perl_script_load_data(const char *data) +{ + char *name; + + g_return_val_if_fail(data != NULL, NULL); + + name = script_data_get_name(); + return script_load(name, NULL, data); +} + +/* Unload perl script */ +void perl_script_unload(PERL_SCRIPT_REC *script) +{ + g_return_if_fail(script != NULL); + + perl_script_destroy_package(script); + perl_script_destroy(script); +} + +/* Find loaded script by name */ +PERL_SCRIPT_REC *perl_script_find(const char *name) +{ + GSList *tmp; + + g_return_val_if_fail(name != NULL, NULL); + + for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) { + PERL_SCRIPT_REC *rec = tmp->data; + + if (strcmp(rec->name, name) == 0) + return rec; + } + + return NULL; +} + +/* Find loaded script by package */ +PERL_SCRIPT_REC *perl_script_find_package(const char *package) +{ + GSList *tmp; + + g_return_val_if_fail(package != NULL, NULL); + + for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) { + PERL_SCRIPT_REC *rec = tmp->data; + + if (strcmp(rec->package, package) == 0) + return rec; + } + + return NULL; +} + +/* Returns full path for the script */ +char *perl_script_get_path(const char *name) +{ + struct stat statbuf; + char *file, *path; + + if (g_path_is_absolute(name) || (name[0] == '~' && name[1] == '/')) { + /* full path specified */ + return convert_home(name); + } + + /* add .pl suffix if it's missing */ + file = IS_PERL_SCRIPT(name) ? g_strdup(name) : + g_strdup_printf("%s.pl", name); + + /* check from ~/.irssi/scripts/ */ + path = g_strdup_printf("%s/scripts/%s", get_irssi_dir(), file); + if (stat(path, &statbuf) != 0) { + /* check from SCRIPTDIR */ + g_free(path); + path = g_strdup_printf(SCRIPTDIR"/%s", file); + if (stat(path, &statbuf) != 0) + path = NULL; + } + g_free(file); + return path; +} + +/* If core should handle printing script errors */ +void perl_core_print_script_error(int print) +{ + print_script_errors = print; +} + +/* Returns the perl module's API version. */ +int perl_get_api_version(void) +{ + return IRSSI_PERL_API_VERSION; +} + +static void perl_scripts_autorun(void) +{ + DIR *dirp; + struct dirent *dp; + struct stat statbuf; + char *path, *fname; + + /* run *.pl scripts from ~/.irssi/scripts/autorun/ */ + path = g_strdup_printf("%s/scripts/autorun", get_irssi_dir()); + dirp = opendir(path); + if (dirp == NULL) { + g_free(path); + return; + } + + while ((dp = readdir(dirp)) != NULL) { + if (!IS_PERL_SCRIPT(dp->d_name)) + continue; + + fname = g_strdup_printf("%s/%s", path, dp->d_name); + if (stat(fname, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode)) + perl_script_load_file(fname); + g_free(fname); + } + closedir(dirp); + g_free(path); +} + +static void sig_script_error(PERL_SCRIPT_REC *script, const char *error) +{ + char *str; + + if (print_script_errors) { + str = g_strdup_printf("Script '%s' error:", + script == NULL ? "??" : script->name); + signal_emit("gui dialog", 2, "error", str); + signal_emit("gui dialog", 2, "error", error); + g_free(str); + } + + if (script != NULL) { + perl_script_unload(script); + signal_stop(); + } +} + +static void sig_autorun(void) +{ + signal_remove("irssi init finished", (SIGNAL_FUNC) sig_autorun); + + perl_scripts_autorun(); +} + +void perl_core_init(void) +{ + print_script_errors = 1; + settings_add_str("perl", "perl_use_lib", PERL_USE_LIB); + + PL_perl_destruct_level = 1; + perl_signals_init(); + signal_add_last("script error", (SIGNAL_FUNC) sig_script_error); + + perl_scripts_init(); + + if (irssi_init_finished) + perl_scripts_autorun(); + else { + signal_add("irssi init finished", (SIGNAL_FUNC) sig_autorun); + settings_check(); + } + + module_register("perl", "core"); +} + +void perl_core_deinit(void) +{ + perl_signals_deinit(); + perl_scripts_deinit(); + + signal_remove("script error", (SIGNAL_FUNC) sig_script_error); +} diff --git a/apps/irssi/src/perl/perl-core.h b/apps/irssi/src/perl/perl-core.h new file mode 100644 index 00000000..b451cc5c --- /dev/null +++ b/apps/irssi/src/perl/perl-core.h @@ -0,0 +1,56 @@ +#ifndef __PERL_CORE_H +#define __PERL_CORE_H + +typedef struct { + char *name; /* unique name */ + char *package; /* package name */ + + /* Script can be loaded from a file, or from some data in memory */ + char *path; /* FILE: full path for file */ + char *data; /* DATA: data used for the script */ +} PERL_SCRIPT_REC; + +extern GSList *perl_scripts; + +/* Initialize perl interpreter */ +void perl_scripts_init(void); +/* Destroy all perl scripts and deinitialize perl interpreter */ +void perl_scripts_deinit(void); + +/* Load a perl script, path must be a full path. */ +PERL_SCRIPT_REC *perl_script_load_file(const char *path); +/* Load a perl script from given data */ +PERL_SCRIPT_REC *perl_script_load_data(const char *data); +/* Unload perl script */ +void perl_script_unload(PERL_SCRIPT_REC *script); + +/* Find loaded script by name */ +PERL_SCRIPT_REC *perl_script_find(const char *name); +/* Find loaded script by package */ +PERL_SCRIPT_REC *perl_script_find_package(const char *package); + +/* Returns full path for the script */ +char *perl_script_get_path(const char *name); +/* Modify the script name so that all non-alphanumeric characters are + translated to '_' */ +void script_fix_name(char *name); + +/* If core should handle printing script errors */ +void perl_core_print_script_error(int print); + +/* Returns the perl module's API version. */ +int perl_get_api_version(void); + +/* Checks that the API version is correct. */ +#define perl_api_version_check(library) \ + if (perl_get_api_version() != IRSSI_PERL_API_VERSION) { \ + die("Version of perl module (%d) doesn't match the " \ + "version of "library" library (%d)", \ + perl_get_api_version(), IRSSI_PERL_API_VERSION); \ + return; \ + } + +void perl_core_init(void); +void perl_core_deinit(void); + +#endif diff --git a/apps/irssi/src/perl/perl-fe.c b/apps/irssi/src/perl/perl-fe.c new file mode 100644 index 00000000..3c47a6d7 --- /dev/null +++ b/apps/irssi/src/perl/perl-fe.c @@ -0,0 +1,257 @@ +/* + perl-core.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module-fe.h" +#include "modules.h" +#include "module-formats.h" +#include "signals.h" +#include "commands.h" +#include "levels.h" + +#include "printtext.h" +#include "completion.h" + +#include "perl-core.h" + +static void cmd_script(const char *data, SERVER_REC *server, void *item) +{ + if (*data == '\0') + data = "list"; + + command_runsub("script", data, server, item); +} + +static void cmd_script_exec(const char *data) +{ + PERL_SCRIPT_REC *script; + GHashTable *optlist; + char *code; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST, + "script exec", &optlist, &code)) + return; + + if (*code == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + script = perl_script_load_data(code); + if (script != NULL && + g_hash_table_lookup(optlist, "permanent") == NULL) { + /* not a permanent script, unload immediately */ + perl_script_unload(script); + } + + + cmd_params_free(free_arg); +} + +static void cmd_script_load(const char *data) +{ + PERL_SCRIPT_REC *script; + char *fname, *path; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1, &path)) + return; + + if (*path == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + fname = perl_script_get_path(path); + if (fname == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_SCRIPT_NOT_FOUND, data); + } else { + script = perl_script_load_file(fname); + if (script != NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_SCRIPT_LOADED, + script->name, script->path); + } + g_free(fname); + } + cmd_params_free(free_arg); +} + +static void cmd_script_unload(const char *data) +{ + PERL_SCRIPT_REC *script; + char *name; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1, &name)) + return; + + if (*name == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + script_fix_name(name); + script = perl_script_find(name); + if (script == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_SCRIPT_NOT_LOADED, name); + } else { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_SCRIPT_UNLOADED, script->name); + perl_script_unload(script); + } + cmd_params_free(free_arg); +} + +static void cmd_script_reset(const char *data) +{ + perl_scripts_deinit(); + perl_scripts_init(); +} + +static void cmd_script_list(void) +{ + GSList *tmp; + GString *data; + + if (perl_scripts == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_NO_SCRIPTS_LOADED); + return; + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_SCRIPT_LIST_HEADER); + + data = g_string_new(NULL); + for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) { + PERL_SCRIPT_REC *rec = tmp->data; + + if (rec->path != NULL) + g_string_assign(data, rec->path); + else { + g_string_assign(data, rec->data); + if (data->len > 50) { + g_string_truncate(data, 50); + g_string_append(data, " ..."); + } + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_SCRIPT_LIST_LINE, rec->name, data->str); + } + g_string_free(data, TRUE); + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_SCRIPT_LIST_FOOTER); +} + +static void sig_script_error(PERL_SCRIPT_REC *script, const char *error) +{ + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_SCRIPT_ERROR, script == NULL ? "??" : script->name); + + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%[-s]%s", error); +} + +static void sig_complete_load(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + char *user_dir; + + if (*line != '\0') + return; + + /* completing filename parameter for /SCRIPT LOAD */ + user_dir = g_strdup_printf("%s/scripts", get_irssi_dir()); + *list = filename_complete(word, user_dir); + *list = g_list_concat(*list, filename_complete(word, SCRIPTDIR)); + g_free(user_dir); + + if (*list != NULL) { + *want_space = FALSE; + signal_stop(); + } +} + +static GList *script_complete(const char *name) +{ + GSList *tmp; + GList *list; + int len; + + list = NULL; + len = strlen(name); + for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) { + PERL_SCRIPT_REC *rec = tmp->data; + + if (strncmp(rec->name, name, len) == 0) + list = g_list_append(list, g_strdup(rec->name)); + } + + return list; +} + +static void sig_complete_unload(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + if (*line != '\0') + return; + + /* completing script parameter for /SCRIPT UNLOAD */ + *list = script_complete(word); + if (*list != NULL) + signal_stop(); +} + +void fe_perl_init(void) +{ + theme_register(feperl_formats); + + command_bind("script", NULL, (SIGNAL_FUNC) cmd_script); + command_bind("script exec", NULL, (SIGNAL_FUNC) cmd_script_exec); + command_bind("script load", NULL, (SIGNAL_FUNC) cmd_script_load); + command_bind("script unload", NULL, (SIGNAL_FUNC) cmd_script_unload); + command_bind("script reset", NULL, (SIGNAL_FUNC) cmd_script_reset); + command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list); + command_set_options("script exec", "permanent"); + + signal_add("script error", (SIGNAL_FUNC) sig_script_error); + signal_add("complete command script load", (SIGNAL_FUNC) sig_complete_load); + signal_add("complete command script unload", (SIGNAL_FUNC) sig_complete_unload); + + perl_core_print_script_error(FALSE); + module_register("perl", "fe"); +} + +void fe_perl_deinit(void) +{ + command_unbind("script", (SIGNAL_FUNC) cmd_script); + command_unbind("script exec", (SIGNAL_FUNC) cmd_script_exec); + command_unbind("script load", (SIGNAL_FUNC) cmd_script_load); + command_unbind("script unload", (SIGNAL_FUNC) cmd_script_unload); + command_unbind("script reset", (SIGNAL_FUNC) cmd_script_reset); + command_unbind("script list", (SIGNAL_FUNC) cmd_script_list); + + signal_remove("script error", (SIGNAL_FUNC) sig_script_error); + signal_remove("complete command script load", (SIGNAL_FUNC) sig_complete_load); + signal_remove("complete command script unload", (SIGNAL_FUNC) sig_complete_unload); + + perl_core_print_script_error(TRUE); +} diff --git a/apps/irssi/src/perl/perl-signals-list.h b/apps/irssi/src/perl/perl-signals-list.h new file mode 100644 index 00000000..4c1c9ad6 --- /dev/null +++ b/apps/irssi/src/perl/perl-signals-list.h @@ -0,0 +1,178 @@ +static PERL_SIGNAL_ARGS_REC perl_signal_args[] = +{ + { "gui dialog", { "string", "string", NULL } }, + { "send command", { "string", "iobject", "iobject", NULL } }, + { "chat protocol created", { "CHAT_PROTOCOL_REC", NULL } }, + { "chat protocol updated", { "CHAT_PROTOCOL_REC", NULL } }, + { "chat protocol destroyed", { "CHAT_PROTOCOL_REC", NULL } }, + { "channel created", { "iobject", "int", NULL } }, + { "channel destroyed", { "iobject", NULL } }, + { "chatnet created", { "iobject", NULL } }, + { "chatnet destroyed", { "iobject", NULL } }, + { "commandlist new", { "Irssi::Command", NULL } }, + { "commandlist remove", { "Irssi::Command", NULL } }, + { "error command", { "int", "string", NULL } }, + { "send command", { "string", "iobject", "iobject", NULL } }, + { "send text", { "string", "iobject", "iobject", NULL } }, + { "command ", { "string", "iobject", "iobject", NULL } }, + { "default command", { "string", "iobject", "iobject", NULL } }, + { "ignore created", { "Irssi::Ignore", NULL } }, + { "ignore destroyed", { "Irssi::Ignore", NULL } }, + { "ignore changed", { "Irssi::Ignore", NULL } }, + { "log new", { "Irssi::Log", NULL } }, + { "log remove", { "Irssi::Log", NULL } }, + { "log create failed", { "Irssi::Log", NULL } }, + { "log locked", { "Irssi::Log", NULL } }, + { "log started", { "Irssi::Log", NULL } }, + { "log stopped", { "Irssi::Log", NULL } }, + { "log rotated", { "Irssi::Log", NULL } }, + { "log written", { "Irssi::Log", "string", NULL } }, + { "module loaded", { "Irssi::Module", "MODULE_FILE_REC", NULL } }, + { "module unloaded", { "Irssi::Module", "MODULE_FILE_REC", NULL } }, + { "module error", { "int", "string", "string", "string", NULL } }, + { "nicklist new", { "iobject", "iobject", NULL } }, + { "nicklist remove", { "iobject", "iobject", NULL } }, + { "nicklist changed", { "iobject", "iobject", "string", NULL } }, + { "nicklist host changed", { "iobject", "iobject", NULL } }, + { "nicklist gone changed", { "iobject", "iobject", NULL } }, + { "nicklist serverop changed", { "iobject", "iobject", NULL } }, + { "pidwait", { "int", "int", NULL } }, + { "query created", { "iobject", "int", NULL } }, + { "query destroyed", { "iobject", NULL } }, + { "query nick changed", { "iobject", "string", NULL } }, + { "query address changed", { "iobject", NULL } }, + { "query server changed", { "iobject", "iobject", NULL } }, + { "rawlog", { "RAWIrssi::Log", "string", NULL } }, + { "server looking", { "iobject", NULL } }, + { "server connected", { "iobject", NULL } }, + { "server connecting", { "iobject", "ulongptr", NULL } }, + { "server connect failed", { "iobject", NULL } }, + { "server disconnected", { "iobject", NULL } }, + { "server quit", { "iobject", "string", NULL } }, + { "setup reread", { "string", NULL } }, + { "setup saved", { "string", "int", NULL } }, + { "ban type changed", { "string", NULL } }, + { "channel joined", { "iobject", NULL } }, + { "channel wholist", { "iobject", NULL } }, + { "channel sync", { "iobject", NULL } }, + { "channel topic changed", { "iobject", NULL } }, + { "ctcp msg", { "iobject", "string", "string", "string", "string", NULL } }, + { "ctcp msg ", { "iobject", "string", "string", "string", "string", NULL } }, + { "default ctcp msg", { "iobject", "string", "string", "string", "string", NULL } }, + { "ctcp reply", { "iobject", "string", "string", "string", "string", NULL } }, + { "ctcp reply ", { "iobject", "string", "string", "string", "string", NULL } }, + { "default ctcp reply", { "iobject", "string", "string", "string", "string", NULL } }, + { "ctcp action", { "iobject", "string", "string", "string", "string", NULL } }, + { "awaylog show", { "Irssi::Log", "int", "int", NULL } }, + { "server nick changed", { "iobject", NULL } }, + { "event connected", { "iobject", NULL } }, + { "server event", { "iobject", "string", "string", "string", NULL } }, + { "event ", { "iobject", "string", "string", "string", NULL } }, + { "default event", { "iobject", "string", "string", "string", NULL } }, + { "server incoming", { "iobject", "string", NULL } }, + { "redir ", { "iobject", "string", "string", "string", NULL } }, + { "server lag", { "iobject", NULL } }, + { "server lag disconnect", { "iobject", NULL } }, + { "massjoin", { "iobject", "gslist_iobject", NULL } }, + { "ban new", { "iobject", "Irssi::Irc::Ban", NULL } }, + { "ban remove", { "iobject", "Irssi::Irc::Ban", NULL } }, + { "channel mode changed", { "iobject", NULL } }, + { "nick mode changed", { "iobject", "iobject", NULL } }, + { "user mode changed", { "iobject", "string", NULL } }, + { "away mode changed", { "iobject", NULL } }, + { "netsplit server new", { "iobject", "NETSPLIT_iobject", NULL } }, + { "netsplit server remove", { "iobject", "NETSPLIT_iobject", NULL } }, + { "netsplit new", { "Irssi::Irc::Netsplit", NULL } }, + { "netsplit remove", { "Irssi::Irc::Netsplit", NULL } }, + { "dcc ctcp ", { "string", "siobject", NULL } }, + { "default dcc ctcp", { "string", "siobject", NULL } }, + { "dcc unknown ctcp", { "string", "string", "string", NULL } }, + { "dcc reply ", { "string", "siobject", NULL } }, + { "default dcc reply", { "string", "siobject", NULL } }, + { "dcc unknown reply", { "string", "string", "string", NULL } }, + { "dcc chat message", { "siobject", "string", NULL } }, + { "dcc created", { "siobject", NULL } }, + { "dcc destroyed", { "siobject", NULL } }, + { "dcc connected", { "siobject", NULL } }, + { "dcc rejecting", { "siobject", NULL } }, + { "dcc closed", { "siobject", NULL } }, + { "dcc request", { "siobject", "string", NULL } }, + { "dcc request send", { "siobject", NULL } }, + { "dcc chat message", { "siobject", "string", NULL } }, + { "dcc transfer update", { "siobject", NULL } }, + { "dcc get receive", { "siobject", NULL } }, + { "dcc error connect", { "siobject", NULL } }, + { "dcc error file create", { "siobject", "string", NULL } }, + { "dcc error file open", { "string", "string", "int", NULL } }, + { "dcc error get not found", { "string", NULL } }, + { "dcc error send exists", { "string", "string", NULL } }, + { "dcc error unknown type", { "string", NULL } }, + { "dcc error close not found", { "string", "string", "string", NULL } }, + { "autoignore new", { "iobject", "AUTOIrssi::Ignore", NULL } }, + { "autoignore remove", { "iobject", "AUTOIrssi::Ignore", NULL } }, + { "flood", { "iobject", "string", "string", "int", "string", NULL } }, + { "notifylist new", { "Irssi::Irc::Notifylist", NULL } }, + { "notifylist remove", { "Irssi::Irc::Notifylist", NULL } }, + { "notifylist joined", { "iobject", "string", "string", "string", "string", "string", NULL } }, + { "notifylist away changed", { "iobject", "string", "string", "string", "string", "string", NULL } }, + { "notifylist unidle", { "iobject", "string", "string", "string", "string", "string", NULL } }, + { "notifylist left", { "iobject", "string", "string", "string", "string", "string", NULL } }, + { "proxy client connected", { "CLIENT_REC", NULL } }, + { "proxy client disconnected", { "CLIENT_REC", NULL } }, + { "gui print text", { "Irssi::UI::Window", "int", "int", "int", "string", "int", NULL } }, + { "gui print text finished", { "Irssi::UI::Window", NULL } }, + { "complete word", { "glistptr_char*", "Irssi::UI::Window", "string", "string", "intptr", NULL } }, + { "exec new", { "Irssi::UI::Process", NULL } }, + { "exec remove", { "Irssi::UI::Process", "int", NULL } }, + { "exec input", { "Irssi::UI::Process", "string", NULL } }, + { "message public", { "iobject", "string", "string", "string", "string", NULL } }, + { "message private", { "iobject", "string", "string", "string", NULL } }, + { "message own_public", { "iobject", "string", "string", NULL } }, + { "message own_private", { "iobject", "string", "string", "string", NULL } }, + { "message join", { "iobject", "string", "string", "string", NULL } }, + { "message part", { "iobject", "string", "string", "string", "string", NULL } }, + { "message quit", { "iobject", "string", "string", "string", NULL } }, + { "message kick", { "iobject", "string", "string", "string", "string", "string", NULL } }, + { "message nick", { "iobject", "string", "string", "string", NULL } }, + { "message own_nick", { "iobject", "string", "string", "string", NULL } }, + { "message invite", { "iobject", "string", "string", "string", NULL } }, + { "message topic", { "iobject", "string", "string", "string", "string", NULL } }, + { "keyinfo created", { "Irssi::UI::Keyinfo", NULL } }, + { "keyinfo destroyed", { "Irssi::UI::Keyinfo", NULL } }, + { "print text", { "Irssi::UI::TextDest", "string", "string", NULL } }, + { "theme created", { "Irssi::UI::Theme", NULL } }, + { "theme destroyed", { "Irssi::UI::Theme", NULL } }, + { "window hilight", { "Irssi::UI::Window", NULL } }, + { "window activity", { "Irssi::UI::Window", "int", NULL } }, + { "window item hilight", { "iobject", NULL } }, + { "window item activity", { "iobject", "int", NULL } }, + { "window item new", { "Irssi::UI::Window", "iobject", NULL } }, + { "window item remove", { "Irssi::UI::Window", "iobject", NULL } }, + { "window item changed", { "Irssi::UI::Window", "iobject", NULL } }, + { "window item server changed", { "Irssi::UI::Window", "iobject", NULL } }, + { "window created", { "Irssi::UI::Window", NULL } }, + { "window destroyed", { "Irssi::UI::Window", NULL } }, + { "window changed", { "Irssi::UI::Window", "Irssi::UI::Window", NULL } }, + { "window changed automatic", { "Irssi::UI::Window", NULL } }, + { "window server changed", { "Irssi::UI::Window", "iobject", NULL } }, + { "window refnum changed", { "Irssi::UI::Window", "int", NULL } }, + { "window name changed", { "Irssi::UI::Window", NULL } }, + { "window history changed", { "Irssi::UI::Window", "string", NULL } }, + { "window level changed", { "Irssi::UI::Window", NULL } }, + { "message irc op_public", { "iobject", "string", "string", "string", "string", NULL } }, + { "message irc own_wall", { "iobject", "string", "string", NULL } }, + { "message irc own_action", { "iobject", "string", "string", NULL } }, + { "message irc action", { "iobject", "string", "string", "string", "string", NULL } }, + { "message irc own_notice", { "iobject", "string", "string", NULL } }, + { "message irc notice", { "iobject", "string", "string", "string", "string", NULL } }, + { "message irc own_ctcp", { "iobject", "string", "string", "string", NULL } }, + { "message irc ctcp", { "iobject", "string", "string", "string", "string", NULL } }, + { "message dcc own", { "siobject", "string", NULL } }, + { "message dcc own_action", { "siobject", "string", NULL } }, + { "message dcc own_ctcp", { "siobject", "string", "string", NULL } }, + { "message dcc", { "siobject", "string", NULL } }, + { "message dcc action", { "siobject", "string", NULL } }, + { "message dcc ctcp", { "siobject", "string", "string", NULL } }, + + { NULL } +}; diff --git a/apps/irssi/src/perl/perl-signals.c b/apps/irssi/src/perl/perl-signals.c new file mode 100644 index 00000000..eb3a8b48 --- /dev/null +++ b/apps/irssi/src/perl/perl-signals.c @@ -0,0 +1,484 @@ +/* + perl-signals.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define NEED_PERL_H +#include "module.h" +#include "modules.h" +#include "signals.h" +#include "commands.h" +#include "servers.h" + +#include "perl-core.h" +#include "perl-common.h" +#include "perl-signals.h" + +typedef struct { + PERL_SCRIPT_REC *script; + int signal_id; + char *signal; + + SV *func; + int priority; +} PERL_SIGNAL_REC; + +typedef struct { + char *signal; + char *args[7]; +} PERL_SIGNAL_ARGS_REC; + +#include "perl-signals-list.h" + +static GHashTable *signals[3]; +static GHashTable *perl_signal_args_hash; +static GSList *perl_signal_args_partial; + +static PERL_SIGNAL_ARGS_REC *perl_signal_args_find(int signal_id) +{ + PERL_SIGNAL_ARGS_REC *rec; + GSList *tmp; + const char *signame; + + rec = g_hash_table_lookup(perl_signal_args_hash, + GINT_TO_POINTER(signal_id)); + if (rec != NULL) return rec; + + /* try to find by name */ + signame = signal_get_id_str(signal_id); + for (tmp = perl_signal_args_partial; tmp != NULL; tmp = tmp->next) { + rec = tmp->data; + + if (strncmp(rec->signal, signame, strlen(rec->signal)) == 0) + return rec; + } + + return NULL; +} + +static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func, + int signal_id, gconstpointer *args) +{ + dSP; + + PERL_SIGNAL_ARGS_REC *rec; + SV *sv, *perlarg, *saved_args[SIGNAL_MAX_ARGUMENTS]; + AV *av; + void *arg; + int n; + + + ENTER; + SAVETMPS; + + PUSHMARK(sp); + + /* push signal argument to perl stack */ + rec = perl_signal_args_find(signal_id); + + memset(saved_args, 0, sizeof(saved_args)); + for (n = 0; n < SIGNAL_MAX_ARGUMENTS && + rec != NULL && rec->args[n] != NULL; n++) { + arg = (void *) args[n]; + + if (strcmp(rec->args[n], "string") == 0) + perlarg = new_pv(arg); + else if (strcmp(rec->args[n], "int") == 0) + perlarg = newSViv(GPOINTER_TO_INT(arg)); + else if (strcmp(rec->args[n], "ulongptr") == 0) + perlarg = newSViv(*(unsigned long *) arg); + else if (strcmp(rec->args[n], "intptr") == 0) + saved_args[n] = perlarg = newRV_noinc(newSViv(*(int *) arg)); + else if (strncmp(rec->args[n], "glistptr_", 9) == 0) { + /* pointer to linked list - push as AV */ + GList *tmp, **ptr; + int is_iobject, is_str; + + is_iobject = strcmp(rec->args[n]+9, "iobject") == 0; + is_str = strcmp(rec->args[n]+9, "char*") == 0; + av = newAV(); + + ptr = arg; + for (tmp = *ptr; tmp != NULL; tmp = tmp->next) { + sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) : + is_str ? new_pv(tmp->data) : + irssi_bless_plain(rec->args[n]+9, tmp->data); + av_push(av, sv); + } + + saved_args[n] = perlarg = newRV_noinc((SV *) av); + } else if (strncmp(rec->args[n], "gslist_", 7) == 0) { + /* linked list - push as AV */ + GSList *tmp; + int is_iobject; + + is_iobject = strcmp(rec->args[n]+7, "iobject") == 0; + av = newAV(); + for (tmp = arg; tmp != NULL; tmp = tmp->next) { + sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) : + irssi_bless_plain(rec->args[n]+7, tmp->data); + av_push(av, sv); + } + + perlarg = newRV_noinc((SV *) av); + } else if (arg == NULL) { + /* don't bless NULL arguments */ + perlarg = newSViv(0); + } else if (strcmp(rec->args[n], "iobject") == 0) { + /* "irssi object" - any struct that has + "int type; int chat_type" as it's first + variables (server, channel, ..) */ + perlarg = iobject_bless((SERVER_REC *) arg); + } else if (strcmp(rec->args[n], "siobject") == 0) { + /* "simple irssi object" - any struct that has + int type; as it's first variable (dcc) */ + perlarg = simple_iobject_bless((SERVER_REC *) arg); + } else { + /* blessed object */ + perlarg = plain_bless(arg, rec->args[n]); + } + XPUSHs(sv_2mortal(perlarg)); + } + + PUTBACK; + perl_call_sv(func, G_EVAL|G_DISCARD); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + char *error = g_strdup(SvPV(ERRSV, PL_na)); + signal_emit("script error", 2, script, error); + g_free(error); + rec = NULL; + } + + /* restore arguments the perl script modified */ + for (n = 0; n < SIGNAL_MAX_ARGUMENTS && + rec != NULL && rec->args[n] != NULL; n++) { + arg = (void *) args[n]; + + if (saved_args[n] == NULL) + continue; + + if (strcmp(rec->args[n], "intptr") == 0) { + int *val = arg; + *val = SvIV(SvRV(saved_args[n])); + } else if (strncmp(rec->args[n], "glistptr_", 9) == 0) { + GList **ret = arg; + GList *out = NULL; + void *val; + STRLEN len; + int count; + + av = (AV *) SvRV(saved_args[n]); + count = av_len(av); + while (count-- >= 0) { + sv = av_shift(av); + if (SvPOKp(sv)) + val = g_strdup(SvPV(sv, len)); + else + val = GINT_TO_POINTER(SvIV(sv)); + + out = g_list_append(out, val); + } + + if (strcmp(rec->args[n]+9, "char*") == 0) + g_list_foreach(*ret, (GFunc) g_free, NULL); + g_list_free(*ret); + *ret = out; + } + } + + PUTBACK; + FREETMPS; + LEAVE; +} + +static void sig_func(int priority, gconstpointer *args) +{ + GSList **list, *tmp, *next; + int signal_id; + + signal_id = signal_get_emitted_id(); + list = g_hash_table_lookup(signals[priority], + GINT_TO_POINTER(signal_id)); + for (tmp = list == NULL ? NULL : *list; tmp != NULL; tmp = next) { + PERL_SIGNAL_REC *rec = tmp->data; + + next = tmp->next; + perl_call_signal(rec->script, rec->func, signal_id, args); + if (signal_is_stopped(signal_id)) + break; + } +} + +#define SIG_FUNC_DECL(priority, priority_name) \ +static void sig_func_##priority_name(gconstpointer p1, gconstpointer p2, \ + gconstpointer p3, gconstpointer p4, \ + gconstpointer p5, gconstpointer p6) \ +{ \ + gconstpointer args[6]; \ + args[0] = p1; args[1] = p2; args[2] = p3; \ + args[3] = p4; args[4] = p5; args[5] = p6; \ + sig_func(priority, args); \ +} + +SIG_FUNC_DECL(0, first); +SIG_FUNC_DECL(1, default); +SIG_FUNC_DECL(2, last); + +#define priority_get_func(priority) \ + (priority == 0 ? sig_func_first : \ + priority == 1 ? sig_func_default : sig_func_last) + +#define perl_signal_get_func(rec) \ + (priority_get_func((rec)->priority)) + +static void perl_signal_add_to_int(const char *signal, SV *func, + int priority, int command) +{ + PERL_SCRIPT_REC *script; + PERL_SIGNAL_REC *rec; + GHashTable *table; + GSList **siglist; + void *signal_idp; + + g_return_if_fail(signal != NULL); + g_return_if_fail(func != NULL); + g_return_if_fail(priority >= 0 && priority <= 2); + + script = perl_script_find_package(perl_get_package()); + g_return_if_fail(script != NULL); + + if (!command && strncmp(signal, "command ", 8) == 0) { + /* we used Irssi::signal_add() instead of + Irssi::command_bind() - oh well, allow this.. */ + command_bind_to(MODULE_NAME, priority, signal+8, -1, + NULL, priority_get_func(priority)); + command = TRUE; + } + + rec = g_new(PERL_SIGNAL_REC, 1); + rec->script = script; + rec->signal_id = signal_get_uniq_id(signal); + rec->signal = g_strdup(signal); + rec->func = perl_func_sv_inc(func, perl_get_package()); + rec->priority = priority; + + table = signals[priority]; + signal_idp = GINT_TO_POINTER(rec->signal_id); + + siglist = g_hash_table_lookup(table, signal_idp); + if (siglist == NULL) { + siglist = g_new0(GSList *, 1); + g_hash_table_insert(table, signal_idp, siglist); + + if (!command) { + signal_add_to_id(MODULE_NAME, priority, rec->signal_id, + perl_signal_get_func(rec)); + } + } + + *siglist = g_slist_append(*siglist, rec); +} + +void perl_signal_add_to(const char *signal, SV *func, int priority) +{ + perl_signal_add_to_int(signal, func, priority, FALSE); +} + +static void perl_signal_destroy(PERL_SIGNAL_REC *rec) +{ + if (strncmp(rec->signal, "command ", 8) == 0) + command_unbind(rec->signal+8, perl_signal_get_func(rec)); + + SvREFCNT_dec(rec->func); + g_free(rec->signal); + g_free(rec); +} + +static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec) +{ + void *signal_idp; + + g_return_if_fail(rec != NULL); + + signal_idp = GINT_TO_POINTER(rec->signal_id); + + *siglist = g_slist_remove(*siglist, rec); + if (*siglist == NULL) { + signal_remove_id(rec->signal_id, perl_signal_get_func(rec)); + g_free(siglist); + g_hash_table_remove(signals[rec->priority], signal_idp); + } + + perl_signal_destroy(rec); +} + +#define sv_func_cmp(f1, f2, len) \ + (f1 == f2 || (SvPOK(f1) && SvPOK(f2) && \ + strcmp((char *) SvPV(f1, len), (char *) SvPV(f2, len)) == 0)) + +static void perl_signal_remove_list(GSList **list, SV *func) +{ + GSList *tmp; + + g_return_if_fail(list != NULL); + + for (tmp = *list; tmp != NULL; tmp = tmp->next) { + PERL_SIGNAL_REC *rec = tmp->data; + + if (sv_func_cmp(rec->func, func, PL_na)) { + perl_signal_remove_list_one(list, rec); + break; + } + } +} + +void perl_signal_remove(const char *signal, SV *func) +{ + GSList **list; + void *signal_idp; + int n; + + signal_idp = GINT_TO_POINTER(signal_get_uniq_id(signal)); + + func = perl_func_sv_inc(func, perl_get_package()); + for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) { + list = g_hash_table_lookup(signals[n], signal_idp); + if (list != NULL) + perl_signal_remove_list(list, func); + } + SvREFCNT_dec(func); +} + +void perl_command_bind_to(const char *cmd, const char *category, + SV *func, int priority) +{ + char *signal; + + command_bind_to(MODULE_NAME, priority, cmd, -1, + category, priority_get_func(priority)); + + signal = g_strconcat("command ", cmd, NULL); + perl_signal_add_to_int(signal, func, priority, TRUE); + g_free(signal); +} + +void perl_command_runsub(const char *cmd, const char *data, + SERVER_REC *server, WI_ITEM_REC *item) +{ + command_runsub(cmd, data, server, item); +} + +void perl_command_unbind(const char *cmd, SV *func) +{ + char *signal; + + /* perl_signal_remove() calls command_unbind() */ + signal = g_strconcat("command ", cmd, NULL); + perl_signal_remove(signal, func); + g_free(signal); +} + +static int signal_destroy_hash(void *key, GSList **list, PERL_SCRIPT_REC *script) +{ + GSList *tmp, *next; + + for (tmp = *list; tmp != NULL; tmp = next) { + PERL_SIGNAL_REC *rec = tmp->data; + + next = tmp->next; + if (script == NULL || rec->script == script) { + *list = g_slist_remove(*list, rec); + if (*list == NULL) { + signal_remove_id(rec->signal_id, + perl_signal_get_func(rec)); + } + perl_signal_destroy(rec); + } + } + + if (*list != NULL) + return FALSE; + + g_free(list); + return TRUE; +} + +/* destroy all signals used by script */ +void perl_signal_remove_script(PERL_SCRIPT_REC *script) +{ + int n; + + for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) { + g_hash_table_foreach_remove(signals[n], + (GHRFunc) signal_destroy_hash, + script); + } +} + +void perl_signals_start(void) +{ + int n; + + for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) { + signals[n] = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + } +} + +void perl_signals_stop(void) +{ + int n; + + for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) { + g_hash_table_foreach(signals[n], + (GHFunc) signal_destroy_hash, NULL); + g_hash_table_destroy(signals[n]); + signals[n] = NULL; + } +} + +void perl_signals_init(void) +{ + int n; + + perl_signal_args_hash = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + perl_signal_args_partial = NULL; + + for (n = 0; perl_signal_args[n].signal != NULL; n++) { + PERL_SIGNAL_ARGS_REC *rec = &perl_signal_args[n]; + + if (rec->signal[strlen(rec->signal)-1] == ' ') { + perl_signal_args_partial = + g_slist_append(perl_signal_args_partial, rec); + } else { + int signal_id = signal_get_uniq_id(rec->signal); + g_hash_table_insert(perl_signal_args_hash, + GINT_TO_POINTER(signal_id), + rec); + } + } +} + +void perl_signals_deinit(void) +{ + g_slist_free(perl_signal_args_partial); + g_hash_table_destroy(perl_signal_args_hash); +} diff --git a/apps/irssi/src/perl/perl-signals.h b/apps/irssi/src/perl/perl-signals.h new file mode 100644 index 00000000..33e0c1b8 --- /dev/null +++ b/apps/irssi/src/perl/perl-signals.h @@ -0,0 +1,33 @@ +#ifndef __PERL_SIGNALS_H +#define __PERL_SIGNALS_H + +void perl_signal_add_to(const char *signal, SV *func, int priority); +#define perl_signal_add_first(signal, func) \ + perl_signal_add_to(signal, func, 0) +#define perl_signal_add(signal, func) \ + perl_signal_add_to(signal, func, 1) +#define perl_signal_add_last(signal, func) \ + perl_signal_add_to(signal, func, 2) + +void perl_signal_remove(const char *signal, SV *func); +/* remove all signals used by script */ +void perl_signal_remove_script(PERL_SCRIPT_REC *script); + +void perl_command_bind_to(const char *cmd, const char *category, + SV *func, int priority); +#define perl_command_bind_first(cmd, category, func) \ + perl_command_bind_to(cmd, category, func, 0) +#define perl_command_bind(cmd, category, func) \ + perl_command_bind_to(cmd, category, func, 1) +#define perl_command_bind_last(cmd, category, func) \ + perl_command_bind_to(cmd, category, func, 2) + +void perl_command_unbind(const char *cmd, SV *func); + +void perl_signals_start(void); +void perl_signals_stop(void); + +void perl_signals_init(void); +void perl_signals_deinit(void); + +#endif diff --git a/apps/irssi/src/perl/perl-sources.c b/apps/irssi/src/perl/perl-sources.c new file mode 100644 index 00000000..be1a4188 --- /dev/null +++ b/apps/irssi/src/perl/perl-sources.c @@ -0,0 +1,179 @@ +/* + perl-sources.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define NEED_PERL_H +#include "module.h" +#include "signals.h" + +#include "perl-core.h" +#include "perl-common.h" + +typedef struct { + PERL_SCRIPT_REC *script; + int tag; + int refcount; + + SV *func; + SV *data; +} PERL_SOURCE_REC; + +static GSList *perl_sources; + +static void perl_source_ref(PERL_SOURCE_REC *rec) +{ + rec->refcount++; +} + +static void perl_source_unref(PERL_SOURCE_REC *rec) +{ + if (--rec->refcount != 0) + return; + + SvREFCNT_dec(rec->data); + SvREFCNT_dec(rec->func); + g_free(rec); +} + +static void perl_source_destroy(PERL_SOURCE_REC *rec) +{ + perl_sources = g_slist_remove(perl_sources, rec); + + g_source_remove(rec->tag); + rec->tag = -1; + + perl_source_unref(rec); +} + +static int perl_source_event(PERL_SOURCE_REC *rec) +{ + dSP; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(sv_mortalcopy(rec->data)); + PUTBACK; + + perl_source_ref(rec); + perl_call_sv(rec->func, G_EVAL|G_DISCARD); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + char *error = g_strdup(SvPV(ERRSV, PL_na)); + signal_emit("script error", 2, rec->script, error); + g_free(error); + } + perl_source_unref(rec); + + PUTBACK; + FREETMPS; + LEAVE; + + return 1; +} + +int perl_timeout_add(int msecs, SV *func, SV *data) +{ + PERL_SCRIPT_REC *script; + PERL_SOURCE_REC *rec; + const char *pkg; + + pkg = perl_get_package(); + script = perl_script_find_package(pkg); + g_return_val_if_fail(script != NULL, -1); + + rec = g_new0(PERL_SOURCE_REC, 1); + perl_source_ref(rec); + + rec->script = script; + rec->func = perl_func_sv_inc(func, pkg); + rec->data = SvREFCNT_inc(data); + rec->tag = g_timeout_add(msecs, (GSourceFunc) perl_source_event, rec); + + perl_sources = g_slist_append(perl_sources, rec); + return rec->tag; +} + +int perl_input_add(int source, int condition, SV *func, SV *data) +{ + PERL_SCRIPT_REC *script; + PERL_SOURCE_REC *rec; + GIOChannel *channel; + const char *pkg; + + pkg = perl_get_package(); + script = perl_script_find_package(pkg); + g_return_val_if_fail(script != NULL, -1); + + rec = g_new0(PERL_SOURCE_REC, 1); + perl_source_ref(rec); + + rec->script =script; + rec->func = perl_func_sv_inc(func, pkg); + rec->data = SvREFCNT_inc(data); + + channel = g_io_channel_unix_new(source); + rec->tag = g_input_add(channel, condition, + (GInputFunction) perl_source_event, rec); + g_io_channel_unref(channel); + + perl_sources = g_slist_append(perl_sources, rec); + return rec->tag; +} + +void perl_source_remove(int tag) +{ + GSList *tmp; + + for (tmp = perl_sources; tmp != NULL; tmp = tmp->next) { + PERL_SOURCE_REC *rec = tmp->data; + + if (rec->tag == tag) { + perl_source_destroy(rec); + break; + } + } +} + +void perl_source_remove_script(PERL_SCRIPT_REC *script) +{ + GSList *tmp, *next; + + for (tmp = perl_sources; tmp != NULL; tmp = next) { + PERL_SOURCE_REC *rec = tmp->data; + + next = tmp->next; + if (rec->script == script) + perl_source_destroy(rec); + } +} + +void perl_sources_start(void) +{ + perl_sources = NULL; +} + +void perl_sources_stop(void) +{ + /* timeouts and input waits */ + while (perl_sources != NULL) + perl_source_destroy(perl_sources->data); +} diff --git a/apps/irssi/src/perl/perl-sources.h b/apps/irssi/src/perl/perl-sources.h new file mode 100644 index 00000000..db165689 --- /dev/null +++ b/apps/irssi/src/perl/perl-sources.h @@ -0,0 +1,14 @@ +#ifndef __PERL_SOURCES_H +#define __PERL_SOURCES_H + +int perl_timeout_add(int msecs, SV *func, SV *data); +int perl_input_add(int source, int condition, SV *func, SV *data); + +void perl_source_remove(int tag); +/* remove all sources used by script */ +void perl_source_remove_script(PERL_SCRIPT_REC *script); + +void perl_sources_start(void); +void perl_sources_stop(void); + +#endif diff --git a/apps/irssi/src/perl/textui/.cvsignore b/apps/irssi/src/perl/textui/.cvsignore new file mode 100644 index 00000000..517db94b --- /dev/null +++ b/apps/irssi/src/perl/textui/.cvsignore @@ -0,0 +1,7 @@ +Makefile +Makefile.PL +TextUI.bs +*.c +*.o +pm_to_blib +blib diff --git a/apps/irssi/src/perl/textui/Makefile.PL.in b/apps/irssi/src/perl/textui/Makefile.PL.in new file mode 100644 index 00000000..9e80274b --- /dev/null +++ b/apps/irssi/src/perl/textui/Makefile.PL.in @@ -0,0 +1,8 @@ +use ExtUtils::MakeMaker; + +WriteMakefile('NAME' => 'Irssi::TextUI', + 'LIBS' => '', + 'OBJECT' => '$(O_FILES)', + 'TYPEMAPS' => ['../common/typemap', '../ui/typemap'], + 'INC' => '-I../../.. -I@top_srcdir@/src -I@top_srcdir@/src/core -I@top_srcdir@/src/fe-common/core -I@top_srcdir@/src/fe-text @GLIB_CFLAGS@', + 'VERSION_FROM' => '@srcdir@/TextUI.pm'); diff --git a/apps/irssi/src/perl/textui/Statusbar.xs b/apps/irssi/src/perl/textui/Statusbar.xs new file mode 100644 index 00000000..67b88e40 --- /dev/null +++ b/apps/irssi/src/perl/textui/Statusbar.xs @@ -0,0 +1,164 @@ +#include "module.h" + +static GHashTable *perl_sbar_defs; + +static int check_sbar_destroy(char *key, char *value, char *script) +{ + if (strncmp(value, script, strlen(script)) == 0 && + value[strlen(script)] == ':') { + statusbar_item_unregister(key); + g_free(key); + g_free(value); + return TRUE; + } + + return FALSE; +} + +static void script_unregister_statusbars(PERL_SCRIPT_REC *script) +{ + g_hash_table_foreach_remove(perl_sbar_defs, + (GHRFunc) check_sbar_destroy, + script->package); +} + +void perl_statusbar_init(void) +{ + perl_sbar_defs = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + signal_add("script destroyed", (SIGNAL_FUNC) script_unregister_statusbars); +} + +static void statusbar_item_def_destroy(void *key, void *value) +{ + g_free(key); + g_free(value); +} + +void perl_statusbar_deinit(void) +{ + signal_remove("script destroyed", (SIGNAL_FUNC) script_unregister_statusbars); + + g_hash_table_foreach(perl_sbar_defs, + (GHFunc) statusbar_item_def_destroy, NULL); + g_hash_table_destroy(perl_sbar_defs); +} + +static void perl_statusbar_event(char *function, SBAR_ITEM_REC *item, + int get_size_only) +{ + dSP; + int retcount; + SV *item_sv, **sv; + HV *hv; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + item_sv = plain_bless(item, "Irssi::TextUI::StatusbarItem"); + XPUSHs(sv_2mortal(item_sv)); + XPUSHs(sv_2mortal(newSViv(get_size_only))); + PUTBACK; + + retcount = perl_call_pv(function, G_EVAL|G_DISCARD); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + PERL_SCRIPT_REC *script; + char *package; + + package = perl_function_get_package(function); + script = perl_script_find_package(package); + g_free(package); + + if (script != NULL) { + /* make sure we don't get back here */ + script_unregister_statusbars(script); + } + signal_emit("script error", 2, script, SvPV(ERRSV, PL_na)); + } else { + /* min_size and max_size can be changed, move them to SBAR_ITEM_REC */ + hv = hvref(item_sv); + if (hv != NULL) { + sv = hv_fetch(hv, "min_size", 8, 0); + if (sv != NULL) item->min_size = SvIV(*sv); + sv = hv_fetch(hv, "max_size", 8, 0); + if (sv != NULL) item->max_size = SvIV(*sv); + } + } + + PUTBACK; + FREETMPS; + LEAVE; +} + + +static void sig_perl_statusbar(SBAR_ITEM_REC *item, int get_size_only) +{ + char *function; + + function = g_hash_table_lookup(perl_sbar_defs, item->config->name); + if (function != NULL) + perl_statusbar_event(function, item, get_size_only); + else { + /* use default function - this shouldn't actually happen.. */ + statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE); + } +} + +MODULE = Irssi::TextUI::Statusbar PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +statusbar_item_register(name, value, func = NULL) + char *name + char *value + char *func +CODE: + statusbar_item_register(name, value, func == NULL || *func == '\0' ? NULL : sig_perl_statusbar); + if (func != NULL) { + g_hash_table_insert(perl_sbar_defs, g_strdup(name), + g_strdup_printf("%s::%s", perl_get_package(), func)); + } + +void +statusbar_item_unregister(name) + char *name +PREINIT: + gpointer key, value; +CODE: + if (g_hash_table_lookup_extended(perl_sbar_defs, name, &key, &value)) { + g_hash_table_remove(perl_sbar_defs, name); + g_free(key); + g_free(value); + } + statusbar_item_unregister(name); + +void +statusbar_items_redraw(name) + char *name + +void +statusbars_recreate_items() + +#******************************* +MODULE = Irssi::TextUI::Statusbar PACKAGE = Irssi::TextUI::StatusbarItem PREFIX = statusbar_item_ +#******************************* + +void +statusbar_item_default_handler(item, get_size_only, str, data, escape_vars = TRUE) + Irssi::TextUI::StatusbarItem item + int get_size_only + char *str + char *data + int escape_vars +PREINIT: + HV *hv; +CODE: + statusbar_item_default_handler(item, get_size_only, + *str == '\0' ? NULL : str, + data, escape_vars); + hv = hvref(ST(0)); + hv_store(hv, "min_size", 8, newSViv(item->min_size), 0); + hv_store(hv, "max_size", 8, newSViv(item->max_size), 0); diff --git a/apps/irssi/src/perl/textui/TextBuffer.xs b/apps/irssi/src/perl/textui/TextBuffer.xs new file mode 100644 index 00000000..4fb92f11 --- /dev/null +++ b/apps/irssi/src/perl/textui/TextBuffer.xs @@ -0,0 +1,83 @@ +#include "module.h" + +MODULE = Irssi::TextUI::TextBuffer PACKAGE = Irssi +PROTOTYPES: ENABLE + +Irssi::TextUI::TextBuffer +textbuffer_create() + +#******************************* +MODULE = Irssi::TextUI::TextBuffer PACKAGE = Irssi::TextUI::TextBuffer PREFIX = textbuffer_ +#******************************* + +void +textbuffer_destroy(buffer) + Irssi::TextUI::TextBuffer buffer + +Irssi::TextUI::Line +textbuffer_append(buffer, data, len, info) + Irssi::TextUI::TextBuffer buffer + char *data + int len + Irssi::TextUI::LineInfo info + +Irssi::TextUI::Line +textbuffer_insert(buffer, insert_after, data, len, info) + Irssi::TextUI::TextBuffer buffer + Irssi::TextUI::Line insert_after + char *data + int len + Irssi::TextUI::LineInfo info + +void +textbuffer_remove(buffer, line) + Irssi::TextUI::TextBuffer buffer + Irssi::TextUI::Line line + +void +textbuffer_remove_all_lines(buffer) + Irssi::TextUI::TextBuffer buffer + +#******************************* +MODULE = Irssi::TextUI::TextBuffer PACKAGE = Irssi::TextUI::Line PREFIX = textbuffer_line_ +#******************************* + +Irssi::TextUI::Line +textbuffer_line_prev(line) + Irssi::TextUI::Line line +CODE: + RETVAL = line->prev; +OUTPUT: + RETVAL + +Irssi::TextUI::Line +textbuffer_line_next(line) + Irssi::TextUI::Line line +CODE: + RETVAL = line->next; +OUTPUT: + RETVAL + +void +textbuffer_line_ref(line) + Irssi::TextUI::Line line + +void +textbuffer_line_unref(line, buffer) + Irssi::TextUI::Line line + Irssi::TextUI::TextBuffer buffer +CODE: + textbuffer_line_unref(buffer, line); + +void +textbuffer_line_get_text(line, coloring) + Irssi::TextUI::Line line + int coloring +PREINIT: + GString *str; +PPCODE: + str = g_string_new(NULL); + textbuffer_line2text(line, coloring, str); + XPUSHs(sv_2mortal(new_pv(str->str))); + g_string_free(str, TRUE); + diff --git a/apps/irssi/src/perl/textui/TextBufferView.xs b/apps/irssi/src/perl/textui/TextBufferView.xs new file mode 100644 index 00000000..5b9b1cce --- /dev/null +++ b/apps/irssi/src/perl/textui/TextBufferView.xs @@ -0,0 +1,108 @@ +#include "module.h" + +MODULE = Irssi::TextUI::TextBufferView PACKAGE = Irssi::TextUI::TextBuffer PREFIX = textbuffer_ +PROTOTYPES: ENABLE + +Irssi::TextUI::TextBufferView +textbuffer_view_create(buffer, width, height, scroll, utf8) + Irssi::TextUI::TextBuffer buffer + int width + int height + int scroll + int utf8 + +#******************************* +MODULE = Irssi::TextUI::TextBufferView PACKAGE = Irssi::TextUI::TextBufferView PREFIX = textbuffer_view_ +#******************************* + +void +textbuffer_view_destroy(view) + Irssi::TextUI::TextBufferView view + +void +textbuffer_view_set_default_indent(view, default_indent, longword_noindent) + Irssi::TextUI::TextBufferView view + int default_indent + int longword_noindent +CODE: + textbuffer_view_set_default_indent(view, default_indent, longword_noindent, NULL); + +void +textbuffer_view_set_scroll(view, scroll) + Irssi::TextUI::TextBufferView view + int scroll + +void +textbuffer_view_resize(view, width, height) + Irssi::TextUI::TextBufferView view + int width + int height + +void +textbuffer_view_clear(view) + Irssi::TextUI::TextBufferView view + +Irssi::TextUI::Line +textbuffer_view_get_lines(view) + Irssi::TextUI::TextBufferView view + +void +textbuffer_view_scroll(view, lines) + Irssi::TextUI::TextBufferView view + int lines + +void +textbuffer_view_scroll_line(view, line) + Irssi::TextUI::TextBufferView view + Irssi::TextUI::Line line + +Irssi::TextUI::LineCache +textbuffer_view_get_line_cache(view, line) + Irssi::TextUI::TextBufferView view + Irssi::TextUI::Line line + +void +textbuffer_view_insert_line(view, line) + Irssi::TextUI::TextBufferView view + Irssi::TextUI::Line line + +void +textbuffer_view_remove_line(view, line) + Irssi::TextUI::TextBufferView view + Irssi::TextUI::Line line + +void +textbuffer_view_remove_all_lines(view) + Irssi::TextUI::TextBufferView view + +void +textbuffer_view_set_bookmark(view, name, line) + Irssi::TextUI::TextBufferView view + char *name + Irssi::TextUI::Line line + +void +textbuffer_view_set_bookmark_bottom(view, name) + Irssi::TextUI::TextBufferView view + char *name + +Irssi::TextUI::Line +textbuffer_view_get_bookmark(view, name) + Irssi::TextUI::TextBufferView view + char *name + +void +textbuffer_view_redraw(view) + Irssi::TextUI::TextBufferView view + +#******************************* +MODULE = Irssi::TextUI::TextBufferView PACKAGE = Irssi::UI::Window +#******************************* + +Irssi::TextUI::TextBufferView +view(window) + Irssi::UI::Window window +CODE: + RETVAL = WINDOW_GUI(window)->view; +OUTPUT: + RETVAL diff --git a/apps/irssi/src/perl/textui/TextUI.pm b/apps/irssi/src/perl/textui/TextUI.pm new file mode 100644 index 00000000..50f247c7 --- /dev/null +++ b/apps/irssi/src/perl/textui/TextUI.pm @@ -0,0 +1,26 @@ +# +# Perl interface to irssi functions. +# + +package Irssi::TextUI; + +use strict; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +$VERSION = "0.9"; + +require Exporter; +require DynaLoader; + +@ISA = qw(Exporter DynaLoader); +@EXPORT = qw(); +@EXPORT_OK = qw(); + +bootstrap Irssi::TextUI $VERSION if (!Irssi::Core::is_static()); + +Irssi::TextUI::init(); + +Irssi::EXPORT_ALL(); + +1; + diff --git a/apps/irssi/src/perl/textui/TextUI.xs b/apps/irssi/src/perl/textui/TextUI.xs new file mode 100644 index 00000000..8d49ac44 --- /dev/null +++ b/apps/irssi/src/perl/textui/TextUI.xs @@ -0,0 +1,160 @@ +#include "module.h" + +static int initialized = FALSE; + +static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window) +{ + hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0); + + hv_store(hv, "first_line", 10, newSViv(window->first_line), 0); + hv_store(hv, "last_line", 9, newSViv(window->last_line), 0); + hv_store(hv, "width", 5, newSViv(window->width), 0); + hv_store(hv, "height", 6, newSViv(window->height), 0); + + hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 0); +} + +static void perl_text_buffer_fill_hash(HV *hv, TEXT_BUFFER_REC *buffer) +{ + hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0); + hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0); + hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0); + hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0); +} + +static void perl_text_buffer_view_fill_hash(HV *hv, TEXT_BUFFER_VIEW_REC *view) +{ + hv_store(hv, "buffer", 6, plain_bless(view->buffer, "Irssi::TextUI::TextBuffer"), 0); + hv_store(hv, "width", 5, newSViv(view->width), 0); + hv_store(hv, "height", 6, newSViv(view->height), 0); + + hv_store(hv, "default_indent", 14, newSViv(view->default_indent), 0); + hv_store(hv, "longword_noindent", 17, newSViv(view->longword_noindent), 0); + hv_store(hv, "scroll", 6, newSViv(view->scroll), 0); + + hv_store(hv, "ypos", 4, newSViv(view->ypos), 0); + + hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0); + hv_store(hv, "subline", 7, newSViv(view->subline), 0); + + hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0); + hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0); + + hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0); + hv_store(hv, "bottom", 6, newSViv(view->bottom), 0); +} + +static void perl_line_fill_hash(HV *hv, LINE_REC *line) +{ + hv_store(hv, "refcount", 8, newSViv(line->refcount), 0); + hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0); +} + +static void perl_line_cache_fill_hash(HV *hv, LINE_CACHE_REC *cache) +{ + hv_store(hv, "last_access", 11, newSViv(cache->last_access), 0); + hv_store(hv, "count", 5, newSViv(cache->count), 0); + /*LINE_CACHE_SUB_REC lines[1];*/ +} + +static void perl_line_info_fill_hash(HV *hv, LINE_INFO_REC *info) +{ + hv_store(hv, "level", 5, newSViv(info->level), 0); + hv_store(hv, "time", 4, newSViv(info->time), 0); +} + +static void perl_statusbar_item_fill_hash(HV *hv, SBAR_ITEM_REC *item) +{ + hv_store(hv, "min_size", 8, newSViv(item->min_size), 0); + hv_store(hv, "max_size", 8, newSViv(item->max_size), 0); + hv_store(hv, "xpos", 4, newSViv(item->xpos), 0); + hv_store(hv, "size", 4, newSViv(item->size), 0); +} + +static PLAIN_OBJECT_INIT_REC textui_plains[] = { + { "Irssi::TextUI::MainWindow", (PERL_OBJECT_FUNC) perl_main_window_fill_hash }, + { "Irssi::TextUI::TextBuffer", (PERL_OBJECT_FUNC) perl_text_buffer_fill_hash }, + { "Irssi::TextUI::TextBufferView", (PERL_OBJECT_FUNC) perl_text_buffer_view_fill_hash }, + { "Irssi::TextUI::Line", (PERL_OBJECT_FUNC) perl_line_fill_hash }, + { "Irssi::TextUI::LineCache", (PERL_OBJECT_FUNC) perl_line_cache_fill_hash }, + { "Irssi::TextUI::LineInfo", (PERL_OBJECT_FUNC) perl_line_info_fill_hash }, + { "Irssi::TextUI::StatusbarItem", (PERL_OBJECT_FUNC) perl_statusbar_item_fill_hash }, + + { NULL, NULL } +}; + +MODULE = Irssi::TextUI PACKAGE = Irssi::TextUI + +PROTOTYPES: ENABLE + +void +init() +CODE: + if (initialized) return; + perl_api_version_check("Irssi::TextUI"); + initialized = TRUE; + + irssi_add_plains(textui_plains); + perl_statusbar_init(); + +void +deinit() +CODE: + if (!initialized) return; + perl_statusbar_deinit(); + +MODULE = Irssi::TextUI PACKAGE = Irssi + +void +gui_printtext(xpos, ypos, str) + int xpos + int ypos + char *str + +MODULE = Irssi::TextUI PACKAGE = Irssi::UI::Window + +void +gui_printtext_after(window, prev, level, str) + Irssi::UI::Window window + Irssi::TextUI::Line prev + int level + char *str +PREINIT: + TEXT_DEST_REC dest; +CODE: + format_create_dest(&dest, NULL, NULL, level, window); + gui_printtext_after(&dest, prev, str); + +Irssi::TextUI::Line +last_line_insert(window) + Irssi::UI::Window window +CODE: + RETVAL = WINDOW_GUI(window)->insert_after; +OUTPUT: + RETVAL + +MODULE = Irssi::TextUI PACKAGE = Irssi::UI::Server + +void +gui_printtext_after(server, target, prev, level, str) + Irssi::Server server + char *target + Irssi::TextUI::Line prev + int level + char *str +PREINIT: + TEXT_DEST_REC dest; +CODE: + format_create_dest(&dest, server, target, level, NULL); + gui_printtext_after(&dest, prev, str); + +BOOT: + irssi_boot(TextUI__Statusbar); + irssi_boot(TextUI__TextBuffer); + irssi_boot(TextUI__TextBufferView); + +void +term_refresh_freeze() + +void +term_refresh_thaw() diff --git a/apps/irssi/src/perl/textui/module.h b/apps/irssi/src/perl/textui/module.h new file mode 100644 index 00000000..9123afb2 --- /dev/null +++ b/apps/irssi/src/perl/textui/module.h @@ -0,0 +1,16 @@ +#include "../ui/module.h" + +#include "mainwindows.h" +#include "gui-windows.h" +#include "gui-printtext.h" +#include "statusbar.h" +#include "textbuffer.h" +#include "textbuffer-view.h" + +typedef MAIN_WINDOW_REC *Irssi__TextUI__MainWindow; +typedef TEXT_BUFFER_REC *Irssi__TextUI__TextBuffer; +typedef TEXT_BUFFER_VIEW_REC *Irssi__TextUI__TextBufferView; +typedef LINE_REC *Irssi__TextUI__Line; +typedef LINE_CACHE_REC *Irssi__TextUI__LineCache; +typedef LINE_INFO_REC *Irssi__TextUI__LineInfo; +typedef SBAR_ITEM_REC *Irssi__TextUI__StatusbarItem; diff --git a/apps/irssi/src/perl/textui/typemap b/apps/irssi/src/perl/textui/typemap new file mode 100644 index 00000000..364cdf32 --- /dev/null +++ b/apps/irssi/src/perl/textui/typemap @@ -0,0 +1,19 @@ +TYPEMAP +Irssi::TextUI::MainWindow T_PlainObj +Irssi::TextUI::TextBuffer T_PlainObj +Irssi::TextUI::TextBufferView T_PlainObj +Irssi::TextUI::Line T_PlainObj +Irssi::TextUI::LineCache T_PlainObj +Irssi::TextUI::LineInfo T_PlainObj +Irssi::TextUI::StatusbarItem T_PlainObj + +INPUT + +T_PlainObj + $var = irssi_ref_object($arg) + +OUTPUT + +T_PlainObj + $arg = plain_bless($var, \"$type\"); + diff --git a/apps/irssi/src/perl/ui/.cvsignore b/apps/irssi/src/perl/ui/.cvsignore new file mode 100644 index 00000000..335ef887 --- /dev/null +++ b/apps/irssi/src/perl/ui/.cvsignore @@ -0,0 +1,7 @@ +Makefile +Makefile.PL +UI.bs +*.c +*.o +pm_to_blib +blib diff --git a/apps/irssi/src/perl/ui/Formats.xs b/apps/irssi/src/perl/ui/Formats.xs new file mode 100644 index 00000000..0ad8c86a --- /dev/null +++ b/apps/irssi/src/perl/ui/Formats.xs @@ -0,0 +1,27 @@ +#include "module.h" + +MODULE = Irssi::UI::Formats PACKAGE = Irssi::UI::Window +PROTOTYPES: ENABLE + +void +format_get_text(window, module, server, target, formatnum, ...) + Irssi::UI::Window window + char *module + Irssi::Server server + char *target + int formatnum +PREINIT: + char **charargs; + char *ret; + int n; +PPCODE: + charargs = g_new0(char *, items-5+1); + charargs[items-5] = NULL; + for (n = 5; n < items; n++) { + charargs[n-5] = (char *)SvPV(ST(n), PL_na); + } + ret = format_get_text(module, window, server, target, formatnum, charargs); + g_free(charargs); + + XPUSHs(sv_2mortal(new_pv(ret))); + g_free_not_null(ret); diff --git a/apps/irssi/src/perl/ui/Makefile.PL.in b/apps/irssi/src/perl/ui/Makefile.PL.in new file mode 100644 index 00000000..a349918e --- /dev/null +++ b/apps/irssi/src/perl/ui/Makefile.PL.in @@ -0,0 +1,8 @@ +use ExtUtils::MakeMaker; + +WriteMakefile('NAME' => 'Irssi::UI', + 'LIBS' => '', + 'OBJECT' => '$(O_FILES)', + 'TYPEMAPS' => ['../common/typemap'], + 'INC' => '-I../../.. -I@top_srcdir@/src -I@top_srcdir@/src/core -I@top_srcdir@/src/fe-common/core @GLIB_CFLAGS@', + 'VERSION_FROM' => '@srcdir@/UI.pm'); diff --git a/apps/irssi/src/perl/ui/Themes.xs b/apps/irssi/src/perl/ui/Themes.xs new file mode 100644 index 00000000..fc3165e9 --- /dev/null +++ b/apps/irssi/src/perl/ui/Themes.xs @@ -0,0 +1,225 @@ +#include "module.h" + +void printformat_perl(TEXT_DEST_REC *dest, char *format, char **arglist) +{ + THEME_REC *theme; + char *module, *str; + int formatnum; + + module = g_strdup(perl_get_package()); + formatnum = format_find_tag(module, format); + if (formatnum < 0) { + die("printformat(): unregistered format '%s'", format); + g_free(module); + return; + } + + theme = dest->window->theme == NULL ? current_theme : + dest->window->theme; + signal_emit("print format", 5, theme, module, + dest, GINT_TO_POINTER(formatnum), arglist); + + str = format_get_text_theme_charargs(theme, module, dest, formatnum, arglist); + if (*str != '\0') printtext_dest(dest, "%s", str); + g_free(str); + g_free(module); +} + +static void perl_unregister_theme(const char *package) +{ + FORMAT_REC *formats; + int n; + + formats = g_hash_table_lookup(default_formats, package); + if (formats == NULL) return; + + for (n = 0; formats[n].def != NULL; n++) { + g_free(formats[n].tag); + g_free(formats[n].def); + } + g_free(formats); + theme_unregister_module(package); +} + +static void sig_script_destroyed(PERL_SCRIPT_REC *script) +{ + perl_unregister_theme(script->package); +} + +void perl_themes_init(void) +{ + signal_add("script destroyed", (SIGNAL_FUNC) sig_script_destroyed); +} + +void perl_themes_deinit(void) +{ + signal_remove("script destroyed", (SIGNAL_FUNC) sig_script_destroyed); +} + +MODULE = Irssi::UI::Themes PACKAGE = Irssi +PROTOTYPES: ENABLE + +Irssi::UI::Theme +current_theme() +CODE: + RETVAL = current_theme; +OUTPUT: + RETVAL + +int +EXPAND_FLAG_IGNORE_REPLACES() +CODE: + RETVAL = EXPAND_FLAG_IGNORE_REPLACES; +OUTPUT: + RETVAL + +int +EXPAND_FLAG_IGNORE_EMPTY() +CODE: + RETVAL = EXPAND_FLAG_IGNORE_EMPTY; +OUTPUT: + RETVAL + +int +EXPAND_FLAG_RECURSIVE_MASK() +CODE: + RETVAL = EXPAND_FLAG_RECURSIVE_MASK; +OUTPUT: + RETVAL + +void +theme_register(formats) + SV *formats +PREINIT: + AV *av; + FORMAT_REC *formatrecs; + char *key, *value; + int len, n, fpos; +CODE: + + if (!SvROK(formats)) + croak("formats is not a reference to list"); + av = (AV *) SvRV(formats); + len = av_len(av)+1; + if (len == 0 || (len & 1) != 0) + croak("formats list is invalid - not divisible by 2 (%d)", len); + + formatrecs = g_new0(FORMAT_REC, len/2+2); + formatrecs[0].tag = g_strdup(perl_get_package()); + formatrecs[0].def = g_strdup("Perl script"); + + for (fpos = 1, n = 0; n < len; n++, fpos++) { + key = SvPV(*av_fetch(av, n, 0), PL_na); n++; + value = SvPV(*av_fetch(av, n, 0), PL_na); + + formatrecs[fpos].tag = g_strdup(key); + formatrecs[fpos].def = g_strdup(value); + formatrecs[fpos].params = MAX_FORMAT_PARAMS; + } + + theme_register_module(perl_get_package(), formatrecs); + +void +printformat(level, format, ...) + int level + char *format +PREINIT: + TEXT_DEST_REC dest; + char *arglist[MAX_FORMAT_PARAMS+1]; + int n; +CODE: + format_create_dest(&dest, NULL, NULL, level, NULL); + memset(arglist, 0, sizeof(arglist)); + for (n = 2; n < items && n < MAX_FORMAT_PARAMS+2; n++) { + arglist[n-2] = SvPV(ST(n), PL_na); + } + + printformat_perl(&dest, format, arglist); + +#******************************* +MODULE = Irssi::UI::Themes PACKAGE = Irssi::Server +#******************************* + +void +printformat(server, target, level, format, ...) + Irssi::Server server + char *target + int level + char *format +PREINIT: + TEXT_DEST_REC dest; + char *arglist[MAX_FORMAT_PARAMS+1]; + int n; +CODE: + format_create_dest(&dest, server, target, level, NULL); + memset(arglist, 0, sizeof(arglist)); + for (n = 4; n < items && n < MAX_FORMAT_PARAMS+4; n++) { + arglist[n-4] = SvPV(ST(n), PL_na); + } + + printformat_perl(&dest, format, arglist); + +#******************************* +MODULE = Irssi::UI::Themes PACKAGE = Irssi::UI::Window +#******************************* + +void +printformat(window, level, format, ...) + Irssi::UI::Window window + int level + char *format +PREINIT: + TEXT_DEST_REC dest; + char *arglist[MAX_FORMAT_PARAMS+1]; + int n; +CODE: + format_create_dest(&dest, NULL, NULL, level, window); + memset(arglist, 0, sizeof(arglist)); + for (n = 3; n < items && n < MAX_FORMAT_PARAMS+3; n++) { + arglist[n-3] = SvPV(ST(n), PL_na); + } + + printformat_perl(&dest, format, arglist); + +#******************************* +MODULE = Irssi::UI::Themes PACKAGE = Irssi::Windowitem +#******************************* + +void +printformat(item, level, format, ...) + Irssi::Windowitem item + int level + char *format +PREINIT: + TEXT_DEST_REC dest; + char *arglist[MAX_FORMAT_PARAMS+1]; + int n; +CODE: + format_create_dest(&dest, item->server, item->name, level, NULL); + memset(arglist, 0, sizeof(arglist)); + for (n = 3; n < items && n < MAX_FORMAT_PARAMS+3; n++) { + arglist[n-3] = SvPV(ST(n), PL_na); + } + + printformat_perl(&dest, format, arglist); + +#******************************* +MODULE = Irssi::UI::Themes PACKAGE = Irssi::UI::Theme PREFIX = theme_ +#******************************* + +void +theme_format_expand(theme, format, flags=0) + Irssi::UI::Theme theme + char *format + int flags +PREINIT: + char *ret; +PPCODE: + if (flags == 0) { + ret = theme_format_expand(theme, format); + } else { + ret = theme_format_expand_data(theme, (const char **) &format, 'n', 'n', + NULL, NULL, EXPAND_FLAG_ROOT | flags); + } + XPUSHs(sv_2mortal(new_pv(ret))); + g_free_not_null(ret); diff --git a/apps/irssi/src/perl/ui/UI.pm b/apps/irssi/src/perl/ui/UI.pm new file mode 100644 index 00000000..5bab7b6b --- /dev/null +++ b/apps/irssi/src/perl/ui/UI.pm @@ -0,0 +1,25 @@ +# +# Perl interface to irssi functions. +# + +package Irssi::UI; + +use strict; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +$VERSION = "0.9"; + +require Exporter; +require DynaLoader; + +@ISA = qw(Exporter DynaLoader); +@EXPORT = qw(); +@EXPORT_OK = qw(); + +bootstrap Irssi::UI $VERSION if (!Irssi::Core::is_static()); + +Irssi::UI::init(); + +Irssi::EXPORT_ALL(); + +1; diff --git a/apps/irssi/src/perl/ui/UI.xs b/apps/irssi/src/perl/ui/UI.xs new file mode 100644 index 00000000..83c2a366 --- /dev/null +++ b/apps/irssi/src/perl/ui/UI.xs @@ -0,0 +1,103 @@ +#include "module.h" + +static int initialized = FALSE; + +static void perl_process_fill_hash(HV *hv, PROCESS_REC *process) +{ + hv_store(hv, "id", 2, newSViv(process->id), 0); + hv_store(hv, "name", 4, new_pv(process->name), 0); + hv_store(hv, "args", 4, new_pv(process->args), 0); + + hv_store(hv, "pid", 3, newSViv(process->pid), 0); + hv_store(hv, "target", 6, new_pv(process->target), 0); + if (process->target_win != NULL) { + hv_store(hv, "target_win", 10, + plain_bless(process->target_win, "Irssi::UI::Window"), 0); + } + hv_store(hv, "shell", 5, newSViv(process->shell), 0); + hv_store(hv, "notice", 6, newSViv(process->notice), 0); + hv_store(hv, "silent", 6, newSViv(process->silent), 0); +} + +static void perl_window_fill_hash(HV *hv, WINDOW_REC *window) +{ + hv_store(hv, "refnum", 6, newSViv(window->refnum), 0); + hv_store(hv, "name", 4, new_pv(window->name), 0); + hv_store(hv, "history_name", 12, new_pv(window->history_name), 0); + + hv_store(hv, "width", 5, newSViv(window->width), 0); + hv_store(hv, "height", 6, newSViv(window->height), 0); + + if (window->active) + hv_store(hv, "active", 6, iobject_bless(window->active), 0); + if (window->active_server) + hv_store(hv, "active_server", 13, iobject_bless(window->active_server), 0); + + hv_store(hv, "servertag", 9, new_pv(window->servertag), 0); + hv_store(hv, "level", 5, newSViv(window->level), 0); + + hv_store(hv, "sticky_refnum", 13, newSViv(window->sticky_refnum), 0); + + hv_store(hv, "data_level", 10, newSViv(window->data_level), 0); + hv_store(hv, "hilight_color", 13, new_pv(window->hilight_color), 0); + + hv_store(hv, "last_timestamp", 14, newSViv(window->last_timestamp), 0); + hv_store(hv, "last_line", 9, newSViv(window->last_line), 0); + + hv_store(hv, "theme", 5, plain_bless(window->theme, "Irssi::UI::Theme"), 0); + hv_store(hv, "theme_name", 10, new_pv(window->theme_name), 0); +} + +static void perl_text_dest_fill_hash(HV *hv, TEXT_DEST_REC *dest) +{ + hv_store(hv, "window", 6, plain_bless(dest->window, "Irssi::UI::Window"), 0); + hv_store(hv, "server", 6, iobject_bless(dest->server), 0); + hv_store(hv, "target", 6, new_pv(dest->target), 0); + hv_store(hv, "level", 5, newSViv(dest->level), 0); + + hv_store(hv, "hilight_priority", 16, newSViv(dest->hilight_priority), 0); + hv_store(hv, "hilight_color", 13, new_pv(dest->hilight_color), 0); +} + +static PLAIN_OBJECT_INIT_REC fe_plains[] = { + { "Irssi::UI::Process", (PERL_OBJECT_FUNC) perl_process_fill_hash }, + { "Irssi::UI::Window", (PERL_OBJECT_FUNC) perl_window_fill_hash }, + { "Irssi::UI::TextDest", (PERL_OBJECT_FUNC) perl_text_dest_fill_hash }, + + { NULL, NULL } +}; + +MODULE = Irssi::UI PACKAGE = Irssi::UI + +PROTOTYPES: ENABLE + +void +processes() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = processes; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::UI::Process"))); + } + + +void +init() +CODE: + if (initialized) return; + perl_api_version_check("Irssi::UI"); + initialized = TRUE; + + irssi_add_plains(fe_plains); + perl_themes_init(); + +void +deinit() +CODE: + if (!initialized) return; + perl_themes_deinit(); + +BOOT: + irssi_boot(UI__Formats); + irssi_boot(UI__Themes); + irssi_boot(UI__Window); diff --git a/apps/irssi/src/perl/ui/Window.xs b/apps/irssi/src/perl/ui/Window.xs new file mode 100644 index 00000000..d6183789 --- /dev/null +++ b/apps/irssi/src/perl/ui/Window.xs @@ -0,0 +1,270 @@ +#include "module.h" + +MODULE = Irssi::UI::Window PACKAGE = Irssi +PROTOTYPES: ENABLE + +void +windows() +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::UI::Window"))); + } + + +Irssi::UI::Window +active_win() +CODE: + RETVAL = active_win; +OUTPUT: + RETVAL + +Irssi::Server +active_server() +CODE: + RETVAL = active_win->active_server; +OUTPUT: + RETVAL + +void +print(str, level=MSGLEVEL_CLIENTNOTICE) + char *str + int level; +CODE: + printtext_string(NULL, NULL, level, str); + +Irssi::UI::Window +window_find_name(name) + char *name + +Irssi::UI::Window +window_find_refnum(refnum) + int refnum + +int +window_refnum_prev(refnum, wrap) + int refnum + int wrap + +int +window_refnum_next(refnum, wrap) + int refnum + int wrap + +int +windows_refnum_last() + +Irssi::UI::Window +window_find_level(level) + int level +CODE: + RETVAL = window_find_level(NULL, level); +OUTPUT: + RETVAL + +Irssi::UI::Window +window_find_item(name) + char *name +CODE: + RETVAL = window_find_item(NULL, name); +OUTPUT: + RETVAL + +Irssi::UI::Window +window_find_closest(name, level) + char *name + int level +CODE: + RETVAL = window_find_closest(NULL, name, level); +OUTPUT: + RETVAL + +Irssi::Windowitem +window_item_find(name) + char *name +CODE: + RETVAL = window_item_find(NULL, name); +OUTPUT: + RETVAL + + +#******************************* +MODULE = Irssi::UI::Window PACKAGE = Irssi::Server +#******************************* + +void +print(server, channel, str, level=MSGLEVEL_CLIENTNOTICE) + Irssi::Server server + char *channel + char *str + int level +CODE: + printtext_string(server, channel, level, str); + +Irssi::Windowitem +window_item_find(server, name) + Irssi::Server server + char *name + +Irssi::UI::Window +window_find_item(server, name) + Irssi::Server server + char *name + +Irssi::UI::Window +window_find_level(server, level) + Irssi::Server server + int level + +Irssi::UI::Window +window_find_closest(server, name, level) + Irssi::Server server + char *name + int level + + +#******************************* +MODULE = Irssi::UI::Window PACKAGE = Irssi::UI::Window PREFIX=window_ +#******************************* + +void +items(window) + Irssi::UI::Window window +PREINIT: + GSList *tmp; +PPCODE: + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *rec = tmp->data; + + XPUSHs(sv_2mortal(iobject_bless(rec))); + } + +void +print(window, str, level=MSGLEVEL_CLIENTNOTICE) + Irssi::UI::Window window + char *str + int level; +CODE: + printtext_string_window(window, level, str); + +void +command(window, cmd) + Irssi::UI::Window window + char *cmd +PREINIT: + WINDOW_REC *old; +CODE: + old = active_win; + active_win = window; + perl_command(cmd, window->active_server, window->active); + active_win = old; + +void +window_item_add(window, item, automatic) + Irssi::UI::Window window + Irssi::Windowitem item + int automatic + +void +window_item_remove(item) + Irssi::Windowitem item + +void +window_item_destroy(item) + Irssi::Windowitem item + +void +window_item_prev(window) + Irssi::UI::Window window + +void +window_item_next(window) + Irssi::UI::Window window + +void +window_destroy(window) + Irssi::UI::Window window + +void +window_set_active(window) + Irssi::UI::Window window + +void +window_change_server(window, server) + Irssi::UI::Window window + Irssi::Server server + +void +window_set_refnum(window, refnum) + Irssi::UI::Window window + int refnum + +void +window_set_name(window, name) + Irssi::UI::Window window + char *name + +void +window_set_history(window, name) + Irssi::UI::Window window + char *name + +void +window_set_level(window, level) + Irssi::UI::Window window + int level + +char * +window_get_active_name(window) + Irssi::UI::Window window + +Irssi::Windowitem +window_item_find(window, server, name) + Irssi::UI::Window window + Irssi::Server server + char *name +CODE: + RETVAL = window_item_find_window(window, server, name); +OUTPUT: + RETVAL + +#******************************* +MODULE = Irssi::UI::Window PACKAGE = Irssi::Windowitem PREFIX = window_item_ +#******************************* + +void +print(item, str, level=MSGLEVEL_CLIENTNOTICE) + Irssi::Windowitem item + int level + char *str +CODE: + printtext_string(item->server, item->name, level, str); + +Irssi::UI::Window +window_create(item, automatic) + Irssi::Windowitem item + int automatic + +Irssi::UI::Window +window(item) + Irssi::Windowitem item +CODE: + RETVAL = window_item_window(item); +OUTPUT: + RETVAL + +void +window_item_change_server(item, server) + Irssi::Windowitem item + Irssi::Server server + +int +window_item_is_active(item) + Irssi::Windowitem item + +void +window_item_set_active(item) + Irssi::Windowitem item +CODE: + window_item_set_active(window_item_window(item), item); diff --git a/apps/irssi/src/perl/ui/module.h b/apps/irssi/src/perl/ui/module.h new file mode 100644 index 00000000..31775039 --- /dev/null +++ b/apps/irssi/src/perl/ui/module.h @@ -0,0 +1,14 @@ +#include "../common/module.h" + +#include "fe-windows.h" +#include "fe-exec.h" +#include "formats.h" +#include "printtext.h" +#include "window-items.h" +#include "themes.h" +#include "keyboard.h" + +typedef WINDOW_REC *Irssi__UI__Window; +typedef TEXT_DEST_REC *Irssi__UI__TextDest; +typedef THEME_REC *Irssi__UI__Theme; +typedef KEYINFO_REC *Irssi__UI__Keyinfo; diff --git a/apps/irssi/src/perl/ui/typemap b/apps/irssi/src/perl/ui/typemap new file mode 100644 index 00000000..2a16fe44 --- /dev/null +++ b/apps/irssi/src/perl/ui/typemap @@ -0,0 +1,16 @@ +TYPEMAP +Irssi::UI::Theme T_PlainObj +Irssi::UI::Window T_PlainObj +Irssi::UI::Keyinfo T_PlainObj +Irssi::UI::TextDest T_PlainObj + +INPUT + +T_PlainObj + $var = irssi_ref_object($arg) + +OUTPUT + +T_PlainObj + $arg = plain_bless($var, \"$type\"); + diff --git a/apps/irssi/src/silc/core/silc-core.c b/apps/irssi/src/silc/core/silc-core.c index 4878c716..2876a3a6 100644 --- a/apps/irssi/src/silc/core/silc-core.c +++ b/apps/irssi/src/silc/core/silc-core.c @@ -164,46 +164,15 @@ static bool silc_log_misc(SilcLogType type, char *message, void *context) return TRUE; } -/* Init SILC. Called from src/fe-text/silc.c */ - -void silc_core_init(void) -{ - static struct poptOption options[] = { - { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, - "Create new public key pair", NULL }, - { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, - "Set the PKCS of the public key pair", "PKCS" }, - { "bits", 0, POPT_ARG_INT, &opt_bits, 0, - "Set the length of the public key pair", "VALUE" }, - { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, - "Show the contents of the public key", "FILE" }, - { "list-ciphers", 'C', POPT_ARG_NONE, &opt_list_ciphers, 0, - "List supported ciphers", NULL }, - { "list-hash-funcs", 'H', POPT_ARG_NONE, &opt_list_hash, 0, - "List supported hash functions", NULL }, - { "list-hmacs", 'H', POPT_ARG_NONE, &opt_list_hmac, 0, - "List supported HMACs", NULL }, - { "list-pkcs", 'P', POPT_ARG_NONE, &opt_list_pkcs, 0, - "List supported PKCSs", NULL }, - { "debug", 'd', POPT_ARG_STRING, &opt_debug, 0, - "Enable debugging", "STRING" }, - { "version", 'V', POPT_ARG_NONE, &opt_version, 0, - "Show version", NULL }, - { NULL, '\0', 0, NULL } - }; - - args_register(options); -} - static void silc_nickname_format_parse(const char *nickname, char **ret_nickname) { silc_parse_userfqdn(nickname, ret_nickname, NULL); } -/* Finalize init. Called from src/fe-text/silc.c */ +/* Finalize init. Init finish signal calls this. */ -void silc_core_init_finish(void) +void silc_core_init_finish(SERVER_REC *server) { CHAT_PROTOCOL_REC *rec; SilcClientParams params; @@ -369,6 +338,39 @@ void silc_core_init_finish(void) idletag = g_timeout_add(5, (GSourceFunc) my_silc_scheduler, NULL); } +/* Init SILC. Called from src/fe-text/silc.c */ + +void silc_core_init(void) +{ + static struct poptOption options[] = { + { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, + "Create new public key pair", NULL }, + { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, + "Set the PKCS of the public key pair", "PKCS" }, + { "bits", 0, POPT_ARG_INT, &opt_bits, 0, + "Set the length of the public key pair", "VALUE" }, + { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, + "Show the contents of the public key", "FILE" }, + { "list-ciphers", 'C', POPT_ARG_NONE, &opt_list_ciphers, 0, + "List supported ciphers", NULL }, + { "list-hash-funcs", 'H', POPT_ARG_NONE, &opt_list_hash, 0, + "List supported hash functions", NULL }, + { "list-hmacs", 'H', POPT_ARG_NONE, &opt_list_hmac, 0, + "List supported HMACs", NULL }, + { "list-pkcs", 'P', POPT_ARG_NONE, &opt_list_pkcs, 0, + "List supported PKCSs", NULL }, + { "debug", 'd', POPT_ARG_STRING, &opt_debug, 0, + "Enable debugging", "STRING" }, + { "version", 'V', POPT_ARG_NONE, &opt_version, 0, + "Show version", NULL }, + { NULL, '\0', 0, NULL } + }; + + signal_add("irssi init finished", (SIGNAL_FUNC) silc_core_init_finish); + + args_register(options); +} + /* Deinit SILC. Called from src/fe-text/silc.c */ void silc_core_deinit(void) diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index cb41470e..86e3082b 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -164,7 +164,7 @@ static int isnickflag_func(char flag) return flag == '@' || flag == '+'; } -static int ischannel_func(const char *data) +static int ischannel_func(SERVER_REC *server, const char *data) { return *data == '#'; } @@ -246,8 +246,10 @@ SILC_SERVER_REC *silc_server_connect(SILC_SERVER_CONNECT_REC *conn) if (server->connrec->port <= 0) server->connrec->port = 706; + server_connect_ref(SERVER_CONNECT(conn)); + if (!server_start_connect((SERVER_REC *) server)) { - server_connect_free(SERVER_CONNECT(conn)); + server_connect_unref(SERVER_CONNECT(conn)); g_free(server); return NULL; } diff --git a/configure.in.pre b/configure.in.pre index 63e2a1ff..8472feb0 100644 --- a/configure.in.pre +++ b/configure.in.pre @@ -604,7 +604,7 @@ AC_SUBST(PIDFILE) AC_ARG_WITH(win32, [ --with-win32 Compile native WIN32 code (-mno-cygwin)], [ AC_DEFINE(SILC_WIN32) - win32-support = true + win32-support=true CFLAGS="-mno-cygwin $CFLAGS" LIBS="$LIBS -lwsock32" ]) @@ -613,8 +613,7 @@ AM_CONDITIONAL(SILC_WIN32, test x$win32-support = xtrue) # # Native EPOC support (disabled by default) # -epoc-support = false -AM_CONDITIONAL(SILC_EPOC, test x$epoc-support = xtrue) +AM_CONDITIONAL(SILC_EPOC, test xfalse = xtrue) # # IPv6 support diff --git a/prepare b/prepare index 191a915d..e61e74fe 100755 --- a/prepare +++ b/prepare @@ -154,6 +154,7 @@ file=irssi/irssi-version.h.in version_date=`date +%Y%m%d` echo "/* automatically created by autogen.sh */" > $file echo "#define IRSSI_VERSION \"$dist_version (Irssi base: @VERSION@ - SILC base: SILC Toolkit $version)\"" >>$file -echo "#define IRSSI_VERSION_DATE \"$version_date\"" >> $file +echo "#define IRSSI_VERSION_DATE $version_date" >> $file +echo "#define IRSSI_VERSION_TIME $version_date" >> $file echo "Done, now run ./configure and make."