--- /dev/null
+# 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-theme.h: $(srcdir)/default.theme
+ $(srcdir)/file2header.sh $(srcdir)/default.theme default_theme > default-theme.h
+
+SUBDIRS = src docs
+
+confdir = $(sysconfdir)/irssi
+conf_DATA = config default.theme
+
+noinst_HEADERS = irssi-version.h
+
+EXTRA_DIST = \
+ autogen.sh \
+ curses.m4 \
+ README \
+ file2header.sh \
+ irssi.spec \
+ irssi.spec.in \
+ $(conf_DATA) \
+ irssi-config.in \
+ irssi-icon.png
+
+## make rpms
+rpm: Makefile
+ $(MAKE) dist
+ rpm -ta --clean $(PACKAGE)-$(VERSION).tar.gz
--- /dev/null
+
+
+irssi, http://irssi.org
+
+
+ * FILES
+
+ - docs/ directory contains several documents:
+ - startup-HOWTO.txt - new users should read this
+ - manual.txt - manual I started writing but didn't get it finished :)
+ - perl.txt - Perl scripting help
+ - formats.txt - How to use colors, etc. with irssi
+ - faq.txt - Frequently Asked Questions
+ - special_vars.txt - some predefined $variables you can use with irssi
+
+
+ * ABOUT
+
+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
+UIs could be created pretty easily. Also, Irssi isn't really even IRC
+specific anymore, there's already a working SILC (http://www.silcnet.org)
+module available. Support for other protocols like ICQ could be created
+some day too.
+
+
+ * FEATURES
+
+So what's so great about Irssi? Here's a list of some features I can
+think of currently:
+
+ - Optional automation - There's lots of things Irssi does for you
+ automatically that some people like and others just hate. Things like:
+ nick completion, creating new window for newly joined channel, creating
+ queries when msgs/notices are received or when you send a msg, closing
+ queries when it's been idle for some time, etc.
+
+ - Multiserver friendy - I think Irssi has clearly the best support
+ for handling multiple server connections. You can have as many as you
+ want in as many ircnets as you want. Having several connections in one
+ server works too, for example when you hit the (ircnet's) 10
+ channels/connection limit you can just create another connection and
+ you hardly notice it. If connection to server is lost, Irssi tries to
+ connect back until it's successful. Also channels you were joined
+ before disconnection are restored, even if they're "temporarily
+ unavailable" because of netsplits, Irssi keeps rejoining back to them.
+ Also worth noticing - there's not that stupid "server is bound to this
+ window, if this window gets closed the connection closes" thing that
+ ircII based clients have.
+
+ - Channel automation - You can specify what channels to join to
+ immediately after connected to some server or IRC network. After joined
+ to channel, Irssi can automatically request ops for you (or do
+ anything, actually) from channel's bots.
+
+ - Window content saving - Say /LAYOUT SAVE when you've put all the
+ channels and queries to their correct place, and after restarting
+ Irssi, the channels will be joined back into windows where they were
+ saved.
+
+ - Tab completing anything - You can complete lots of things with tab:
+ nicks, commands, command -options, file names, settings, text format
+ names, channels and server names. There's also an excellent /msg
+ completion that works transparently with multiple IRC networks.
+ Completing channel nicks is also pretty intelligent, it first goes
+ through the people who have talked to you recently, then the people who
+ have talked to anyone recently and only then it fallbacks to rest of
+ the nicks. You can also complete a set of words you've specified, for
+ example homepage<tab> changes it to your actual home page URL.
+
+ - Excellent logging - You can log any way you want and as easily or
+ hard as you want. With autologging Irssi logs everything to specified
+ directory, one file per channel/nick. ircII style /WINDOW LOG ON is
+ also supported. There's also the "hard way" of logging - /LOG command
+ which lets you specify exactly what you wish to log and where. Log
+ rotating is supported with all the different logging methods, you can
+ specify how often you want it to rotate and what kind of time stamp to
+ use.
+
+ - Excellent ignoring - You can most probably ignore anything any way
+ you want. Nick masks, words, regular expressions. You can add
+ exceptions to ignores. You can ignore other people's replies in
+ channels to nicks you have ignored. You can also specify that the
+ specific ignores work only in specific channel(s).
+
+ - Lastlog and scrollback handling - /LASTLOG command has some new
+ features: -new option checks only lines that came since you last did
+ /LASTLOG command, -away option checks new lines since you last went
+ away. Regular expression matches work also, of course. Going to some
+ wanted place at scrollback has always been hard with non-GUI clients. A
+ search command that jumps around in scrollback in GUI-style is still
+ missing from Irssi, but there's something that's almost as good as it.
+ /LASTLOG always shows timestamps when the line was printed, even if you
+ didn't have timestamps on. Now doing /SB GOTO <timestamp> jumps
+ directly to the position in scrollback you wanted. Great feature when
+ you want to browse a bit of the discussion what happened when someone
+ said your name (as seen in awaylog) or topic was changed (/last
+ -topics)
+
+
+ * BUGS / SUGGESTIONS
+
+See TODO file if it is already listed in there - if not send me email..
+
+
+ * AUTHOR
+
+ - Timo Sirainen
+ - tss@iki.fi
+ - cras@ircnet/efnet/opn/silc
+ - #irssi@ircnet/opn
--- /dev/null
+/* paths */
+#undef SYSCONFDIR
+#undef HELPDIR
+#undef PLUGINSDIR
+
+/* misc.. */
+#undef MEM_DEBUG
+#undef HAVE_IPV6
+#undef HAVE_POPT_H
+#undef HAVE_SOCKS_H
+#undef HAVE_PL_PERL
+#undef HAVE_STATIC_PERL
+#undef HAVE_GMODULE
+#undef WANT_BIG5
+
+/* macros/curses checks */
+#undef HAS_CURSES
+#undef USE_SUNOS_CURSES
+#undef USE_BSD_CURSES
+#undef USE_SYSV_CURSES
+#undef USE_NCURSES
+#undef NO_COLOR_CURSES
+#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
+
+/* nls */
+#undef ENABLE_NLS
+#undef HAVE_CATGETS
+#undef HAVE_GETTEXT
+#undef HAVE_LC_MESSAGES
+#undef HAVE_STPCPY
--- /dev/null
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="Irssi SILC"
+
+if test ! -f $srcdir/configure.in; then
+ echo -n "**Error**: Directory \`$srcdir\' does not look like the"
+ echo " top-level $PKG_NAME directory"
+ exit 1
+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
+
+files=`echo docs/help/in/*.in|sed -e 's,docs/help/in/Makefile.in ,,' -e 's,docs/help/in/,!,g' -e 's/\.in /.in ?/g'`
+cat docs/help/in/Makefile.am.gen|sed "s/@HELPFILES@/$files/g"|sed 's/?/\\?/g'|tr '!?' '\t\n' > docs/help/in/Makefile.am
+
+files=`echo $files|sed 's/\.in//g'`
+cat docs/help/Makefile.am.gen|sed "s/@HELPFILES@/$files/g"|sed 's/?/\\?/g'|tr '!?' '\t\n' > docs/help/Makefile.am
+
+# .html -> .txt with lynx
+echo "Documentation: html -> txt..."
+lynx -dump -nolist docs/startup-HOWTO.html > docs/startup-HOWTO.txt
+
+echo "Checking auto* tools..."
+
+# *********** a bit modified GNOME's macros/autogen.sh **********
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`autoconf' installed to compile $PKG_NAME."
+ echo "Download the appropriate package for your distribution,"
+ echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+ DIE=1
+}
+
+(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && {
+ (libtool --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`libtool' installed to compile $PKG_NAME."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+ }
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`automake' installed to compile $PKG_NAME."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+ NO_AUTOMAKE=yes
+}
+
+
+# if no automake, don't bother testing for aclocal
+test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: Missing \`aclocal'. The version of \`automake'"
+ echo "installed doesn't appear recent enough."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+}
+
+if test "$DIE" -eq 1; then
+ exit 1
+fi
+
+case $CC in
+xlc )
+ am_opt=--include-deps;;
+esac
+
+rm -f aclocal.m4
+if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
+ echo "Running libtoolize..."
+ libtoolize --force --copy
+fi
+aclocalinclude="$ACLOCAL_FLAGS -I ."
+echo "Running aclocal $aclocalinclude ..."
+aclocal $aclocalinclude
+if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
+ echo "Running autoheader..."
+ autoheader
+fi
+echo "Running autoconf ..."
+autoconf
+echo "Running automake --gnu $am_opt ..."
+automake --add-missing --gnu $am_opt
+
+conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
--- /dev/null
+lservers = (
+ { address = "irc.stealth.net"; chatnet = IRCNet; port = 6668; },
+ { address = "irc.efnet.net"; chatnet = EFNet; port = 6667; },
+ { address = "irc.undernet.org"; chatnet = Undernet; port = 6667; },
+ { address = "irc.dal.net"; chatnet = DALnet; port = 6667; },
+ { address = "irc.openprojects.net"; chatnet = OPN; port = 6667; },
+ { address = "irc.ptlink.net"; chatnet = PTlink; port = 6667; }
+ { address = "silc.pspt.fi"; chatnet = SILC; port = 706; }
+);
+
+chatnets = {
+ IRCNet = { type = "IRC"; max_kicks = 4; max_modes = 3; max_msgs = 5; max_whois = 4; };
+ EFNet = { type = "IRC"; max_kicks = 4; max_modes = 4; max_msgs = 3; };
+ Undernet = { type = "IRC"; max_kicks = 4; max_modes = 3; max_msgs = 3; max_query_chans = "1"; };
+ DALNet = { type = "IRC"; max_kicks = 4; max_modes = 6; max_msgs = 3; };
+ OPN = { type = "IRC"; max_kicks = 1; max_modes = 6; max_msgs = 100; };
+ PTLink = { type = "IRC"; max_kicks = 1; max_modes = 6; max_msgs = 100; };
+ SILC = { type = "SILC"; };
+};
+
+channels = (
+ { name = "#irssi"; chatnet = ircnet; autojoin = No; },
+ { name = "#irssi"; chatnet = opn; autojoin = No; },
+ { name = "#silc"; chatnet = silc; autojoin = No; }
+);
+
+aliases = {
+ J = "join";
+ WJOIN = "join -window";
+ WQUERY = "query -window";
+ LEAVE = "part";
+ BYE = "quit";
+ EXIT = "quit";
+ SIGNOFF = "quit";
+ DESCRIBE = "action";
+ DATE = "time";
+ HOST = "userhost";
+ LAST = "lastlog";
+ SAY = "msg *";
+ 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";
+ UMODE = "mode $N";
+ WC = "window close";
+ WN = "window new hide";
+ SV = "say Irssi $J - http://irssi.org/";
+ GOTO = "sb goto";
+ CHAT = "dcc chat";
+};
--- /dev/null
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if you need to in order for stat and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* misc.. */
+#undef MEM_DEBUG
+#undef HAVE_IPV6
+#undef HAVE_POPT_H
+#undef HAVE_SOCKS_H
+#undef HAVE_PL_PERL
+#undef HAVE_STATIC_PERL
+#undef HAVE_GMODULE
+#undef WANT_BIG5
+
+/* macros/curses checks */
+#undef HAS_CURSES
+#undef USE_SUNOS_CURSES
+#undef USE_BSD_CURSES
+#undef USE_SYSV_CURSES
+#undef USE_NCURSES
+#undef NO_COLOR_CURSES
+#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
+
+/* Define if you have the fcntl function. */
+#undef HAVE_FCNTL
+
+/* Define if you have the mkfifo function. */
+#undef HAVE_MKFIFO
+
+/* Define if you have the <dirent.h> header file. */
+#undef HAVE_DIRENT_H
+
+/* Define if you have the <libintl.h> header file. */
+#undef HAVE_LIBINTL_H
+
+/* Define if you have the <regex.h> header file. */
+#undef HAVE_REGEX_H
+
+/* Define if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/utsname.h> header file. */
+#undef HAVE_SYS_UTSNAME_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Name of package */
+#undef PACKAGE
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 'int' if <sys/socket.h> doesn't define. */
+#undef socklen_t
+
--- /dev/null
+AC_INIT(src)
+
+AM_CONFIG_HEADER(config.h)
+AM_INIT_AUTOMAKE(Irssi SILC, 0.7.98.3)
+
+AM_MAINTAINER_MODE
+
+AC_ISC_POSIX
+AC_PROG_CC
+AC_PROG_CPP
+AC_STDC_HEADERS
+AC_ARG_PROGRAM
+AM_PROG_LIBTOOL
+
+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
+
+${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \
+$libtool_flags --disable-static --output=libtool-shared --no-verify $ac_aux_dir/ltmain.sh $lt_target \
+|| { echo "configure: error: libtool configure failed" 1>&2; exit 1; }
+${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \
+$libtool_flags --no-verify $ac_aux_dir/ltmain.sh $host \
+|| { echo "configure: error: libtool configure failed" 1>&2; exit 1; }
+
+AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h libintl.h)
+
+# check posix headers..
+AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h)
+
+AC_ARG_WITH(big5,
+[ --with-big5 Build with tr-Chinese Big5 support],
+ if test x$withval = xyes; then
+ want_big5=yes
+ else
+ if test "x$withval" = xno; then
+ want_big5=no
+ else
+ want_big5=yes
+ fi
+ fi,
+ want_big5=no)
+
+AC_ARG_WITH(socks,
+[ --with-socks Build with socks support],
+ if test x$withval = xyes; then
+ want_socks=yes
+ else
+ if test "x$withval" = xno; then
+ want_socks=no
+ else
+ want_socks=yes
+ fi
+ fi,
+ want_socks=no)
+
+AC_ARG_WITH(textui,
+[ --with-textui Build text frontend],
+ if test x$withval = xyes; then
+ want_textui=yes
+ else
+ if test "x$withval" = xno; then
+ want_textui=no
+ else
+ want_textui=yes
+ fi
+ fi,
+ want_textui=yes)
+
+AC_ARG_WITH(bot,
+[ --with-bot Build irssi-bot],
+ if test x$withval = xyes; then
+ want_irssibot=yes
+ else
+ if test "x$withval" = xno; then
+ want_irssibot=no
+ else
+ want_irssibot=yes
+ fi
+ fi,
+ want_irssibot=no)
+
+AC_ARG_WITH(proxy,
+[ --with-proxy Build irssi-proxy],
+ if test x$withval = xyes; then
+ want_irssiproxy=yes
+ else
+ if test "x$withval" = xno; then
+ want_irssiproxy=no
+ else
+ want_irssiproxy=yes
+ fi
+ fi,
+ want_irssiproxy=no)
+
+
+AC_ARG_WITH(modules,
+[ --with-modules Specify what modules to build in binary],
+ if test x$withval != xyes -a x$withval != xno; then
+ build_modules="$withval"
+ fi)
+
+if test "x$prefix" = "xNONE"; then
+ PERL_LIB_DIR=""
+else
+ PERL_LIB_DIR="$prefix"
+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
+ else
+ if test "x$enableval" = xno; then
+ want_perl=no
+ else
+ want_perl=yes
+ PERL_LIB_DIR="$enableval"
+ perl_lib_dir_given=yes
+ fi
+ fi,
+ want_perl=yes)
+
+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
+ want_perl=yes
+ elif test x$enableval = xstatic; then
+ want_perl=static
+ else
+ want_perl=no
+ fi,
+ want_perl=yes)
+
+AC_ARG_WITH(tests,
+[ --with-tests Run all the tests],
+ if test x$withval != xno; then
+ TEST_DIR=test
+ fi,
+ 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
+ want_ipv6=yes
+ else
+ if test "x$enableval" = xno; then
+ want_ipv6=no
+ else
+ want_ipv6=yes
+ fi
+ fi,
+ want_ipv6=no)
+
+dnl **
+dnl ** just some generic stuff...
+dnl **
+
+AC_CHECK_FUNCS(mkfifo fcntl)
+
+AC_CHECK_LIB(socket, socket, [
+ PROG_LIBS="$PROG_LIBS -lsocket"
+])
+
+AC_CHECK_LIB(nsl, inet_addr, [
+ PROG_LIBS="$PROG_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])
+AC_CACHE_VAL(irssi_cv_type_socklen_t,
+[AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>],
+[socklen_t t;],
+irssi_cv_type_socklen_t=yes,
+irssi_cv_type_socklen_t=no,
+)])
+if test $irssi_cv_type_socklen_t = no; then
+AC_DEFINE(socklen_t, int, Define to 'int' if <sys/socket.h> doesn't define.)
+fi
+AC_MSG_RESULT($irssi_cv_type_socklen_t)
+
+dnl **
+dnl ** check for socks
+dnl **
+
+if test "x$want_socks" = "xyes"; then
+ AC_CHECK_LIB(socks, connect, [
+ PROG_LIBS="$PROG_LIBS -lsocks"
+ AC_CHECK_HEADER(socks.h, [
+ AC_DEFINE(HAVE_SOCKS_H)
+ CFLAGS="$CFLAGS -DSOCKS"
+ AC_MSG_RESULT(["socks5 library found, building with it"])
+ ], [
+ AC_MSG_RESULT(["socks4 library found, building with it"])
+ CFLAGS="$CFLAGS -Dconnect=Rconnect -Dgetsockname=Rgetsockname -Dgetpeername=Rgetpeername -Dbind=Rbind -Daccept=Raccept -Dlisten=Rlisten -Dselect=Rselect"
+ ])
+ ])
+fi
+
+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
+ dnl * glib in irssi directory, use it
+ AC_MSG_RESULT([yes, using it])
+
+ dnl * we have to do this at this point so we know what libs gmodule needs
+ if test ! -f $GLIB_DIR/.libs/libglib.a; then
+ echo
+ echo "configuring GLib ..."
+ echo
+ cd $GLIB_DIR
+ if test ! -f glib-config; then
+ ./configure
+ fi
+ ${MAKE-make}
+ cd ..
+ echo
+ fi
+
+ GLIB_LDEXTRA=`$GLIB_DIR/glib-config --libs gmodule|$sedpath -e 's/-lglib//' -e 's/-lgmodule//' -e 's,-L/usr/local/lib ,,'|$sedpath 's/ \+/ /g'`
+ full_glib_dir="`pwd`/$GLIB_DIR"
+ GLIB_CFLAGS="-I$full_glib_dir -I$full_glib_dir/gmodule"
+ if test -f $full_glib_dir/.libs/libglib.a; then
+ GLIB_LIBS="$full_glib_dir/.libs/libglib.a $GLIB_LDEXTRA"
+ if test -f $full_glib_dir/gmodule/.libs/libgmodule.a; then
+ GLIB_LIBS="$GLIB_LIBS $full_glib_dir/gmodule/.libs/libgmodule.a"
+ AC_DEFINE(HAVE_GMODULE)
+ fi
+ else
+ GLIB_LIBS="$full_glib_dir/libglib.a $GLIB_LDEXTRA"
+ if test -f $full_glib_dir/gmodule/libgmodule.a; then
+ GLIB_LIBS="$GLIB_LIBS $full_glib_dir/gmodule/libgmodule.a"
+ AC_DEFINE(HAVE_GMODULE)
+ fi
+ fi
+ AC_SUBST(GLIB_CFLAGS)
+ AC_SUBST(GLIB_LIBS)
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+AC_CHECK_GLIBDIR
+
+if test "x$GLIB_DIR" = "x"; then
+ AM_PATH_GLIB(1.2.0,,, gmodule)
+ if test "x$GLIB_LIBS" = "x"; then
+ echo "*** trying without -lgmodule"
+ glib_config_args=
+ AM_PATH_GLIB(1.2.0)
+ else
+ AC_DEFINE(HAVE_GMODULE)
+ fi
+
+ if test "x$GLIB_LIBS" = "x"; then
+ echo
+ echo "*** If you don't have GLIB, you can get it from ftp://ftp.gtk.org"
+ echo "*** If you can't install GLIB anywhere or if you don't want to,"
+ echo "*** you can just unpack it to Irssi's source directory and"
+ echo "*** Irssi will automatically compile and use it."
+ echo
+
+ dnl * I think it's pretty safe to assume GLib 1.2.9 since the next
+ dnl * will be 2.0 (or 1.4?) and it's not sure if irssi compiles
+ dnl * with it (yea, just a few weeks after I put this text for 1.2.8
+ dnl * the 1.2.9 came :) .. and then .10
+ glib_file=glib-1.2.10.tar.gz
+
+ dlcmd=
+ if test "x`ncftpget 2>/dev/null|grep -i ncftp`" != "x"; 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
+ dlcmd="wget http://irssi.org/files/$glib_file"
+ fi
+ if test "x$dlcmd" != "x"; then
+ echo "*** I can download GLib for you now. If you don't want to, press CTRL-C now."
+ read answer
+ eval $dlcmd
+ if `gunzip $glib_file`; then
+ glib_file=`echo $glib_file|$sedpath s/\.gz$//`
+ if `tar xf $glib_file`; then
+ rm -f $glib_file
+ AC_CHECK_GLIBDIR
+ fi
+ fi
+ fi
+
+ if test "x$GLIB_LIBS" = "x"; then
+ AC_ERROR([GLIB is required to build irssi.])
+ fi
+ fi
+fi
+
+PROG_LIBS="$PROG_LIBS $GLIB_LIBS"
+
+dnl **
+dnl ** check if we can link dynamic libraries to modules
+dnl ** also checks if libraries are built to .libs dir
+dnl **
+
+AC_MSG_CHECKING([if we can link dynamic libraries with modules])
+DYNLIB_MODULES=no
+
+dnl ** compile object file
+cat > conftest.c <<EOF
+#include <math.h>
+int modfunc(void){return (int)floor(1.2);}
+EOF
+
+./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
+
+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 ** check if dynamic linking worked
+libfile=`grep '^library_names' libconftest.la|$sedpath "s/library_names='\(.*\)'.*/\1/"|$sedpath 's/.* \([[^ ]]*\)$/\1/'`
+if test ! -s .libs/$libfile; then
+ AC_MSG_RESULT([no, error linking test module])
+else
+ cat > conftest.c <<EOF
+#include <gmodule.h>
+main() {
+GModule *m; int (*modfunc)(void);
+m = g_module_open(".libs/$libfile", 0);
+if (!m) g_print("error loading: %s", g_module_error());
+else if (!g_module_symbol(m, "modfunc", (gpointer *) &modfunc))
+ g_print("modfunc() symbol not found from module");
+else if (modfunc() == 1) g_print("ok"); else g_print("wrong result?! 1 vs %d", modfunc());
+return 0; }
+EOF
+ $CC $CFLAGS 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 **
+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)
+ else
+ want_textui=no
+ curses_error=yes
+ fi
+else
+ has_curses=false
+fi
+
+dnl **
+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
+ 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"
+ AC_MSG_RESULT([not found, building without Perl])
+ want_perl=no
+ else
+ PERL_LDFLAGS=`$perlpath -MExtUtils::Embed -e ldopts 2>/dev/null`
+
+ dnl * Perl 5.004 and older use perl_xxx variables while
+ dnl * later use PL_perl_xxx variables ..
+ have_pl_perl=`$perlpath -e 'print $] < 5.005 ? "no" : "yes";'`
+ if test "x$have_pl_perl" = "xyes"; then
+ AC_DEFINE(HAVE_PL_PERL)
+ fi
+
+ if test "x$DYNLIB_MODULES" = "xno" -a "$want_perl" != "static"; then
+ dnl * wanted perl as module, won't get it.
+ want_perl=static
+ perl_mod_error="Dynamic library dependencies don't work with modules"
+ fi
+
+ if test "$want_perl" != "static"; then
+ dnl * dynaloader.a -> libperl_dynaloader.la
+ DYNALOADER_A=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*([[^ ]]*DynaLoader\.a).*/\2/'`
+ fi
+
+ dnl * don't check libperl.a if dynaloader.a wasn't found..
+ if test "x$DYNALOADER_A" != "x"; then
+ dnl * find either libperl.a or libperl.so
+ LIBPERL_A=`echo "$PERL_LDFLAGS -L/usr/lib"|$perlpath -e 'foreach (split(/ /, <STDIN>)) { if (/^-L(.*)/) { my $dir=$1; if (\`ls $dir/libperl.so* 2>/dev/null\`) { print "-lperl"; last; }; if (-e "$dir/libperl.a") { print "$dir/libperl.a"; last } } };'`
+ if test "x$LIBPERL_A" = "x"; then
+ perl_mod_error="Didn't find location of -lperl"
+ DYNALOADER_A=
+ elif test "$LIBPERL_A" = "-lperl"; then
+ LIBPERL_A=
+ fi
+ fi
+
+ dnl * remove all database stuffs
+ dnl * nsl is already in ldflags
+ dnl * libc is of course linked without needing -lc
+ dnl * -rdynamic must not be in LIBADD line
+ for word in -ldb -ldbm -lndbm -lgdbm -lc -rdynamic; do
+ PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e "s/$word //" -e "s/$word$//"`
+ done
+
+ case "$host_os" in
+ linux*)
+ PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lposix //' -e 's/-lposix$//'`
+ ;;
+ hpux*)
+ if test "x$ac_cv_prog_gcc" = "xyes"; then
+ PERL_CFLAGS=`echo $PERL_CFLAGS | $sedpath -e 's/-Ae //' -e 's/-Ae$//'`
+ PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-Ae //' -e 's/-Ae$//'`
+ fi
+ ;;
+ *)
+ ;;
+ esac
+
+ 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
+ if test -s conftest; then
+ irssi_cv_lib_perl_works=yes
+ else
+ irssi_cv_lib_perl_works=no
+ fi
+ ])
+
+ if test "x$irssi_cv_lib_perl_works" = "xno"; then
+ perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS"
+ AC_MSG_RESULT([error linking with perl libraries, building without Perl])
+ want_perl=no
+ fi
+ 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
+ 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
+ PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lperl /libperl_orig.la /' -e 's/-lperl$/libperl_orig.la$/'`
+ fi
+ AC_SUBST(LIBPERL_A)
+ AC_SUBST(DYNALOADER_A)
+ fi
+
+ 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_FE_LINK_LIBS="../perl/libfe_perl_static.la"
+ PERL_LDFLAGS=
+ AC_DEFINE(HAVE_STATIC_PERL)
+
+ dnl * build only static library of perl module
+ perl_module_lib=
+ perl_module_fe_lib=
+ perl_static_lib=libperl_static.la
+ perl_static_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
+ 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'
+ 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_LIBS)
+ AC_SUBST(PERL_FE_LINK_LIBS)
+
+ AC_SUBST(PERL_LDFLAGS)
+ AC_SUBST(PERL_CFLAGS)
+ AC_SUBST(PERL_LIB_DIR)
+ fi
+fi
+
+dnl ** check what we want to build
+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(HAVE_PERL, test "$want_perl" != "no")
+AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static")
+
+AC_SUBST(PROG_LIBS)
+
+dnl **
+dnl ** Keep all the libraries here so each frontend doesn't need to
+dnl ** keep track of them all
+dnl **
+dnl ** (these could be made configurable)
+
+CHAT_MODULES="silc"
+silc_MODULES=""
+if test "x$build_modules" != "x"; then
+ silc_MODULES="$silc_MODULES $build_modules"
+fi
+#PROG_LIBS="$PROG_LIBS -lsilc"
+
+dnl ****************************************
+
+AC_SUBST(CHAT_MODULES)
+AC_SUBST(silc_MODULES)
+
+CORE_LIBS="../core/libcore.a ../lib-config/libirssi_config.a ../lib-popt/libpopt.a"
+FE_COMMON_LIBS=""
+
+CHAT_LIBS=""
+for c in $CHAT_MODULES; do
+ module_inits=""
+ module_deinits=""
+ fe_module_inits=""
+ fe_module_deinits=""
+ CHAT_LIBS="$CHAT_LIBS ../$c/lib$c.a ../$c/core/lib${c}_core.a"
+ if test -f $srcdir/src/fe-common/$c/module.h; then
+ 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"
+ 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_module_inits="$fe_module_inits fe_${c}_${s}_init();"
+ fe_module_deinits="fe_${c}_${s}_deinit(); $fe_module_deinits"
+ fi
+ done
+
+ 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_deinit(void);" >> $file
+ if test "x$module_inits" != "x"; 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}_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
+ echo "$fe_module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
+ echo "$fe_module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
+ fi
+ echo "void fe_${c}_modules_init(void) { $fe_module_inits }" >> $file
+ echo "void fe_${c}_modules_deinit(void) { $fe_module_deinits }" >> $file
+ fi
+done
+
+FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/core/libfe_common_core.a"
+
+dnl ** common libraries needed by frontends
+COMMON_NOUI_LIBS="$CHAT_LIBS $CORE_LIBS $INTLLIBS"
+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 **
+
+if test "x$want_big5" = "xyes"; then
+ AC_DEFINE(WANT_BIG5)
+fi
+
+dnl **
+dnl ** IPv6 support
+dnl **
+
+if test "x$want_ipv6" = "xyes"; then
+ AC_DEFINE(HAVE_IPV6)
+fi
+
+AC_OUTPUT(
+Makefile
+src/Makefile
+src/core/Makefile
+src/fe-common/Makefile
+src/fe-common/core/Makefile
+src/fe-common/silc/Makefile
+src/fe-text/Makefile
+src/lib-config/Makefile
+src/lib-popt/Makefile
+src/silc/Makefile
+src/silc/core/Makefile
+docs/Makefile
+docs/help/Makefile
+docs/help/in/Makefile
+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
+
+ 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
+ link=`echo $file|$sedpath "s?$whole_dir/??"`
+ rm -f $link
+ $LN_S $file $link
+ done
+ fi
+fi
+
+echo
+
+if test "x$curses_error" != "xyes"; then
+ echo "Building text frontend ..... : $want_textui"
+else
+ echo "Building text frontend ..... : NO!!"
+ echo " - Because curses was not found, specify the path to it with"
+ echo " --with-curses=/dir and make sure you have the curses headers"
+ echo " installed (usually in ncurses-devel package)"
+fi
+echo "Building irssi bot ......... : $want_irssibot"
+echo "Building irssi proxy ....... : $want_irssiproxy"
+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
+ echo "Building with Perl support . : module"
+else
+ if test "x$perl_check_error" = "x"; then
+ echo "Building with Perl support . : no"
+ else
+ echo "Building with Perl support . : NO!"
+ echo " - $perl_check_error"
+ fi
+fi
+
+if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then
+ echo " - NOTE: Perl support will be compiled statically to irssi, not as"
+ echo " a module as requested. Reason:"
+ 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
+ fi
+fi
+echo "Install prefix ............. : $prefix"
+
--- /dev/null
+dnl Curses detection: Munged from Midnight Commander's configure.in
+dnl
+dnl What it does:
+dnl =============
+dnl
+dnl - Determine which version of curses is installed on your system
+dnl and set the -I/-L/-l compiler entries and add a few preprocessor
+dnl symbols
+dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that
+dnl @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in
+dnl Makefile.in's
+dnl - Modify the following configure variables (these are the only
+dnl curses.m4 variables you can access from within configure.in)
+dnl CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if
+dnl an ncurses.h that's been renamed to curses.h
+dnl is found.
+dnl CURSES_LIBS - sets -L and -l's appropriately
+dnl CFLAGS - if --with-sco, add -D_SVID3
+dnl has_curses - exports result of tests to rest of configure
+dnl
+dnl Usage:
+dnl ======
+dnl 1) Add lines indicated below to acconfig.h
+dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in
+dnl 3) Instead of #include <curses.h> you should use the following to
+dnl properly locate ncurses or curses header file
+dnl
+dnl #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+dnl #include <ncurses.h>
+dnl #else
+dnl #include <curses.h>
+dnl #endif
+dnl
+dnl 4) Make sure to add @CURSES_INCLUDEDIR@ to your preprocessor flags
+dnl 5) Make sure to add @CURSES_LIBS@ to your linker flags or LIBS
+dnl
+dnl Notes with automake:
+dnl - call AM_CONDITIONAL(HAS_CURSES, test "$has_curses" = true) from
+dnl configure.in
+dnl - your Makefile.am can look something like this
+dnl -----------------------------------------------
+dnl INCLUDES= blah blah blah $(CURSES_INCLUDEDIR)
+dnl if HAS_CURSES
+dnl CURSES_TARGETS=name_of_curses_prog
+dnl endif
+dnl bin_PROGRAMS = other_programs $(CURSES_TARGETS)
+dnl other_programs_SOURCES = blah blah blah
+dnl name_of_curses_prog_SOURCES = blah blah blah
+dnl other_programs_LDADD = blah
+dnl name_of_curses_prog_LDADD = blah $(CURSES_LIBS)
+dnl -----------------------------------------------
+dnl
+dnl
+dnl The following lines should be added to acconfig.h:
+dnl ==================================================
+dnl
+dnl /*=== Curses version detection defines ===*/
+dnl /* Found some version of curses that we're going to use */
+dnl #undef HAS_CURSES
+dnl
+dnl /* Use SunOS SysV curses? */
+dnl #undef USE_SUNOS_CURSES
+dnl
+dnl /* Use old BSD curses - not used right now */
+dnl #undef USE_BSD_CURSES
+dnl
+dnl /* Use SystemV curses? */
+dnl #undef USE_SYSV_CURSES
+dnl
+dnl /* Use Ncurses? */
+dnl #undef USE_NCURSES
+dnl
+dnl /* If you Curses does not have color define this one */
+dnl #undef NO_COLOR_CURSES
+dnl
+dnl /* Define if you want to turn on SCO-specific code */
+dnl #undef SCO_FLAVOR
+dnl
+dnl /* Set to reflect version of ncurses *
+dnl * 0 = version 1.*
+dnl * 1 = version 1.9.9g
+dnl * 2 = version 4.0/4.1 */
+dnl #undef NCURSES_970530
+dnl
+dnl /*=== End new stuff for acconfig.h ===*/
+dnl
+
+
+AC_DEFUN(AC_CHECK_CURSES,[
+ search_ncurses=true
+ screen_manager=""
+ has_curses=false
+
+ CFLAGS=${CFLAGS--O}
+
+ AC_SUBST(CURSES_LIBS)
+ AC_SUBST(CURSES_INCLUDEDIR)
+
+ AC_ARG_WITH(sco,
+ [ --with-sco Use this to turn on SCO-specific code],[
+ if test x$withval = xyes; then
+ AC_DEFINE(SCO_FLAVOR)
+ CFLAGS="$CFLAGS -D_SVID3"
+ fi
+ ])
+
+ AC_ARG_WITH(sunos-curses,
+ [ --with-sunos-curses Used to force SunOS 4.x curses],[
+ if test x$withval = xyes; then
+ AC_USE_SUNOS_CURSES
+ fi
+ ])
+
+ AC_ARG_WITH(osf1-curses,
+ [ --with-osf1-curses Used to force OSF/1 curses],[
+ if test x$withval = xyes; then
+ AC_USE_OSF1_CURSES
+ fi
+ ])
+
+ AC_ARG_WITH(vcurses,
+ [ --with-vcurses[=incdir] Used to force SysV curses],
+ if test x$withval != xyes; then
+ CURSES_INCLUDEDIR="-I$withval"
+ fi
+ AC_USE_SYSV_CURSES
+ )
+
+ AC_ARG_WITH(ncurses,
+ [ --with-ncurses[=dir] Compile with ncurses/locate base dir],
+ if test x$withval = xno ; then
+ search_ncurses=false
+ elif test x$withval != xyes ; then
+ AC_NCURSES($withval/include, ncurses.h, -L$withval/lib -lncurses, -I$withval/include, "ncurses on $withval/include")
+ fi
+ )
+
+ if $search_ncurses
+ then
+ AC_SEARCH_NCURSES()
+ fi
+])
+
+
+AC_DEFUN(AC_USE_SUNOS_CURSES, [
+ search_ncurses=false
+ screen_manager="SunOS 4.x /usr/5include curses"
+ AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses)
+ AC_DEFINE(USE_SUNOS_CURSES)
+ AC_DEFINE(HAS_CURSES)
+ has_curses=true
+ AC_DEFINE(NO_COLOR_CURSES)
+ AC_DEFINE(USE_SYSV_CURSES)
+ CURSES_INCLUDEDIR="-I/usr/5include"
+ CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a"
+ AC_MSG_RESULT(Please note that some screen refreshs may fail)
+])
+
+AC_DEFUN(AC_USE_OSF1_CURSES, [
+ AC_MSG_RESULT(Using OSF1 curses)
+ search_ncurses=false
+ screen_manager="OSF1 curses"
+ AC_DEFINE(HAS_CURSES)
+ has_curses=true
+ AC_DEFINE(NO_COLOR_CURSES)
+ AC_DEFINE(USE_SYSV_CURSES)
+ CURSES_LIBS="-lcurses"
+])
+
+AC_DEFUN(AC_USE_SYSV_CURSES, [
+ AC_MSG_RESULT(Using SysV curses)
+ AC_DEFINE(HAS_CURSES)
+ has_curses=true
+ AC_DEFINE(USE_SYSV_CURSES)
+ search_ncurses=false
+ screen_manager="SysV/curses"
+ CURSES_LIBS="-lcurses"
+])
+
+dnl AC_ARG_WITH(bsd-curses,
+dnl [--with-bsd-curses Used to compile with bsd curses, not very fancy],
+dnl search_ncurses=false
+dnl screen_manager="Ultrix/cursesX"
+dnl if test $system = ULTRIX
+dnl then
+dnl THIS_CURSES=cursesX
+dnl else
+dnl THIS_CURSES=curses
+dnl fi
+dnl
+dnl CURSES_LIBS="-l$THIS_CURSES -ltermcap"
+dnl AC_DEFINE(HAS_CURSES)
+dnl has_curses=true
+dnl AC_DEFINE(USE_BSD_CURSES)
+dnl AC_MSG_RESULT(Please note that some screen refreshs may fail)
+dnl AC_WARN(Use of the bsdcurses extension has some)
+dnl AC_WARN(display/input problems.)
+dnl AC_WARN(Reconsider using xcurses)
+dnl)
+
+
+dnl
+dnl Parameters: directory filename cureses_LIBS curses_INCLUDEDIR nicename
+dnl
+AC_DEFUN(AC_NCURSES, [
+ if $search_ncurses
+ then
+ if test -f $1/$2
+ then
+ AC_MSG_RESULT(Found ncurses on $1/$2)
+
+ CURSES_LIBS="$3"
+ AC_CHECK_LIB(ncurses, initscr, [
+ ], [
+ CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'`
+ AC_CHECK_LIB(curses, initscr, [
+ CURSES_LIBS="$CHECKLIBS"
+ ],, $CHECKLIBS)
+ ], $CURSES_LIBS)
+ CURSES_INCLUDEDIR="$4"
+ search_ncurses=false
+ screen_manager=$5
+ AC_DEFINE(HAS_CURSES)
+ has_curses=true
+ has_ncurses=true
+ AC_DEFINE(USE_NCURSES)
+ fi
+ fi
+])
+
+AC_DEFUN(AC_SEARCH_NCURSES, [
+ AC_CHECKING("location of ncurses.h file")
+
+ AC_NCURSES(/usr/include, ncurses.h, -lncurses,, "ncurses on /usr/include")
+ AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses, "ncurses on /usr/include/ncurses")
+ AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include, "ncurses on /usr/local")
+ AC_NCURSES(/usr/pkg/include, ncurses.h, -L/usr/pkg/lib -lncurses, -I/usr/pkg/include, "ncurses on /usr/pkg")
+ AC_NCURSES(/usr/contrib/include, ncurses.h, -L/usr/contrib/lib -lncurses, -I/usr/contrib/include, "ncurses on /usr/contrib")
+ AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -L/usr/local/lib/ncurses -lncurses, -I/usr/local/include/ncurses, "ncurses on /usr/local/include/ncurses")
+
+ AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/local/.../ncurses")
+
+ AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/include/ncurses")
+
+ dnl
+ dnl We couldn't find ncurses, try SysV curses
+ dnl
+ if $search_ncurses
+ then
+ AC_EGREP_HEADER(init_color, /usr/include/curses.h,
+ AC_USE_SYSV_CURSES)
+ AC_EGREP_CPP(USE_NCURSES,[
+#include <curses.h>
+#ifdef __NCURSES_H
+#undef USE_NCURSES
+USE_NCURSES
+#endif
+],[
+ CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES"
+ AC_DEFINE(HAS_CURSES)
+ has_curses=true
+ has_ncurses=true
+ AC_DEFINE(USE_NCURSES)
+ search_ncurses=false
+ screen_manager="ncurses installed as curses"
+])
+ fi
+
+ dnl
+ dnl Try SunOS 4.x /usr/5{lib,include} ncurses
+ dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES
+ dnl should be replaced by a more fine grained selection routine
+ dnl
+ if $search_ncurses
+ then
+ if test -f /usr/5include/curses.h
+ then
+ AC_USE_SUNOS_CURSES
+ fi
+ fi
+
+ dnl use whatever curses there happens to be
+ if $search_ncurses
+ then
+ if test -f /usr/include/curses.h
+ then
+ CURSES_LIBS="-lcurses"
+ AC_DEFINE(HAS_CURSES)
+ has_curses=true
+ search_ncurses=false
+ screen_manager="curses"
+ fi
+ fi
+])
+
--- /dev/null
+const char *default_config =
+"lservers = (\n"
+" { address = \"irc.stealth.net\"; chatnet = IRCNet; port = 6668; },\n"
+" { address = \"irc.efnet.net\"; chatnet = EFNet; port = 6667; },\n"
+" { address = \"irc.undernet.org\"; chatnet = Undernet; port = 6667; },\n"
+" { address = \"irc.dal.net\"; chatnet = DALnet; port = 6667; },\n"
+" { address = \"irc.openprojects.net\"; chatnet = OPN; port = 6667; },\n"
+" { address = \"irc.ptlink.net\"; chatnet = PTlink; port = 6667; }\n"
+" { address = \"silc.pspt.fi\"; chatnet = SILC; port = 706; }\n"
+");\n"
+"\n"
+"chatnets = {\n"
+" IRCNet = { type = \"IRC\"; max_kicks = 4; max_modes = 3; max_msgs = 5; max_whois = 4; };\n"
+" EFNet = { type = \"IRC\"; max_kicks = 4; max_modes = 4; max_msgs = 3; };\n"
+" Undernet = { type = \"IRC\"; max_kicks = 4; max_modes = 3; max_msgs = 3; max_query_chans = \"1\"; };\n"
+" DALNet = { type = \"IRC\"; max_kicks = 4; max_modes = 6; max_msgs = 3; };\n"
+" OPN = { type = \"IRC\"; max_kicks = 1; max_modes = 6; max_msgs = 100; };\n"
+" PTLink = { type = \"IRC\"; max_kicks = 1; max_modes = 6; max_msgs = 100; };\n"
+" SILC = { type = \"SILC\"; };\n"
+"};\n"
+"\n"
+"channels = (\n"
+" { name = \"#irssi\"; chatnet = ircnet; autojoin = No; },\n"
+" { name = \"#irssi\"; chatnet = opn; autojoin = No; },\n"
+" { name = \"#silc\"; chatnet = silc; autojoin = No; }\n"
+");\n"
+"\n"
+"aliases = {\n"
+" J = \"join\";\n"
+" WJOIN = \"join -window\";\n"
+" WQUERY = \"query -window\";\n"
+" LEAVE = \"part\";\n"
+" BYE = \"quit\";\n"
+" EXIT = \"quit\";\n"
+" SIGNOFF = \"quit\";\n"
+" DESCRIBE = \"action\";\n"
+" DATE = \"time\";\n"
+" HOST = \"userhost\";\n"
+" LAST = \"lastlog\";\n"
+" SAY = \"msg *\";\n"
+" WI = \"whois\";\n"
+" WII = \"whois $0 $0\";\n"
+" WW = \"whowas\";\n"
+" W = \"who\";\n"
+" N = \"names\";\n"
+" M = \"msg\";\n"
+" T = \"topic\";\n"
+" C = \"clear\";\n"
+" CL = \"clear\";\n"
+" K = \"kick\";\n"
+" KB = \"kickban\";\n"
+" KN = \"knockout\";\n"
+" BANS = \"ban\";\n"
+" B = \"ban\";\n"
+" MUB = \"unban *\";\n"
+" UB = \"unban\";\n"
+" IG = \"ignore\";\n"
+" UNIG = \"unignore\";\n"
+" SB = \"scrollback\";\n"
+" UMODE = \"mode $N\";\n"
+" WC = \"window close\";\n"
+" WN = \"window new hide\";\n"
+" SV = \"say Irssi $J - http://irssi.org/\";\n"
+" GOTO = \"sb goto\";\n"
+" CHAT = \"dcc chat\";\n"
+"};\n"
+;
--- /dev/null
+# When testing changes, the easiest way to reload the theme is with /RELOAD.
+# This reloads the configuration file too, so if you did any changes remember
+# to /SAVE it first. Remember also that /SAVE overwrites the theme file with
+# old data so keep backups :)
+
+# TEMPLATES:
+
+# The real text formats that irssi uses are the ones you can find with
+# /FORMAT command. Back in the old days all the colors and texts were mixed
+# up in those formats, and it was really hard to change the colors since you
+# might have had to change them in tens of different places. So, then came
+# this templating system.
+
+# Now the /FORMATs don't have any colors in them, and they also have very
+# little other styling. Most of the stuff you need to change is in this
+# theme file. If you can't change something here, you can always go back
+# to change the /FORMATs directly, they're also saved in these .theme files.
+
+# So .. the templates. They're those {blahblah} parts you see all over the
+# /FORMATs and here. Their usage is simply {name parameter1 parameter2}.
+# When irssi sees this kind of text, it goes to find "name" from abstracts
+# block below and sets "parameter1" into $0 and "parameter2" into $1 (you
+# can have more parameters of course). Templates can have subtemplates.
+# Here's a small example:
+# /FORMAT format hello {colorify {underline world}}
+# abstracts = { colorify = "%G$0-%n"; underline = "%U$0-%U"; }
+# When irssi expands the templates in "format", the final string would be:
+# hello %G%Uworld%U%n
+# ie. underlined bright green "world" text.
+# and why "$0-", why not "$0"? $0 would only mean the first parameter,
+# $0- means all the parameters. With {underline hello world} you'd really
+# want to underline both of the words, not just the hello (and world would
+# actually be removed entirely).
+
+# COLORS:
+
+# You can find definitions for the color format codes in docs/formats.txt.
+
+# There's one difference here though. %n format. Normally it means the
+# default color of the terminal (white mostly), but here it means the
+# "reset color back to the one it was in higher template". For example
+# if there was /FORMAT test %g{foo}bar, and foo = "%Y$0%n", irssi would
+# print yellow "foo" (as set with %Y) but "bar" would be green, which was
+# set at the beginning before the {foo} template. If there wasn't the %g
+# at start, the normal behaviour of %n would occur. If you _really_ want
+# to use the terminal's default color, use %N.
+
+#############################################################################
+
+# default foreground color (%N) - 0 is the "default terminal color"
+default_color = 0;
+# default foreground color when "0" can't be used,
+# such as with bolds and reverses. white is default.
+default_real_color = 7;
+
+# these characters are automatically replaced with specified color
+# (dark grey by default)
+replaces = { "[]<>=" = "%K$0-%n"; };
+
+abstracts = {
+ ##
+ ## generic
+ ##
+
+ # text to insert at the beginning of each non-message line
+ line_start = "%B-%W!%B-%n ";
+
+ # timestamp styling, nothing by default
+ timestamp = "$0-";
+
+ # any kind of text that needs hilighting, default is to bold
+ hilight = "%_$0-%_";
+
+ # any kind of error message, default is bright red
+ error = "%R$0-%n";
+
+ # channel name is printed
+ channel = "%_$0-%_";
+
+ # nick is printed
+ nick = "%_$0-%_";
+
+ # nick host is printed
+ nickhost = "[$0-]";
+
+ # server name is printed
+ server = "%_$0-%_";
+
+ # some kind of comment is printed
+ comment = "[$0-]";
+
+ # reason for something is printed (part, quit, kick, ..)
+ reason = "{comment $0-}";
+
+ # mode change is printed ([+o nick])
+ mode = "{comment $0-}";
+
+ ##
+ ## channel specific messages
+ ##
+
+ # highlighted nick/host is printed (joins)
+ channick_hilight = "%C$0-%n";
+ chanhost_hilight = "{nickhost %c$0-%n}";
+
+ # nick/host is printed (parts, quits, etc.)
+ channick = "%c$0-%n";
+ chanhost = "{nickhost $0-}";
+
+ # highlighted channel name is printed
+ channelhilight = "%c$0-%n";
+
+ # ban/ban exception/invite list mask is printed
+ ban = "%c$0-%n";
+
+ ##
+ ## messages
+ ##
+
+ # the basic styling of how to print message, $0 = nick mode, $1 = nick
+ msgnick = "<$0$1-> %|";
+
+ # message from you is printed. "msgownnick" specifies the styling of the
+ # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the
+ # whole line.
+
+ # Example1: You want the message text to be green:
+ # ownmsgnick = "{msgnick $0 $1-}%g";
+ # Example2.1: You want < and > chars to be yellow:
+ # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n";
+ # (you'll also have to remove <> from replaces list above)
+ # Example2.2: But you still want to keep <> grey for other messages:
+ # pubmsgnick = "%K{msgnick $0 $1-%K}%n";
+ # pubmsgmenick = "%K{msgnick $0 $1-%K}%n";
+ # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n";
+ # ownprivmsgnick = "%K{msgnick $0-%K}%n";
+ # privmsgnick = "%K{msgnick %R$0-%K}%n";
+
+ # $0 = nick mode, $1 = nick
+ ownmsgnick = "{msgnick $0 $1-}";
+ ownnick = "%W$0-%n";
+
+ # public message in channel, $0 = nick mode, $1 = nick
+ pubmsgnick = "{msgnick $0 $1-}";
+ pubnick = "%N$0-%n";
+
+ # public message in channel meant for me, $0 = nick mode, $1 = nick
+ pubmsgmenick = "{msgnick $0 $1-}";
+ menick = "%Y$0-%n";
+
+ # public highlighted message in channel
+ # $0 = highlight color, $1 = nick mode, $2 = nick
+ pubmsghinick = "{msgnick $1 $0$2-%n}";
+
+ # channel name is printed with message
+ msgchannel = "%K:%c$0-%n";
+
+ # private message, $0 = nick, $1 = host
+ privmsg = "[%R$0%K(%r$1-%K)%n] ";
+
+ # private message from you, $0 = "msg", $1 = target nick
+ ownprivmsg = "[%r$0%K(%R$1-%K)%n] ";
+
+ # own private message in query
+ ownprivmsgnick = "{msgnick $0-}";
+ ownprivnick = "%W$0-%n";
+
+ # private message in query
+ privmsgnick = "{msgnick %R$0-%n}";
+
+ ##
+ ## Actions (/ME stuff)
+ ##
+
+ # used internally by this theme
+ action_core = "%W * $0-%n";
+
+ # generic one that's used by most actions
+ action = "{action_core $0-} ";
+
+ # own action, both private/public
+ ownaction = "{action $0-}";
+
+ # own action with target, both private/public
+ ownaction_target = "{action_core $0}%K:%c$1%n ";
+
+ # private action sent by others
+ pvtaction = "%W (*) $0-%n ";
+ pvtaction_query = "{action $0-}";
+
+ # public action sent by others
+ pubaction = "{action $0-}";
+
+
+ ##
+ ## other IRC events
+ ##
+
+ # notices
+ ownnotice = "[%r$0%K(%R$1-%K)]%n ";
+ notice = "%K-%M$0-%K-%n ";
+ pubnotice_channel = "%K:%m$0-";
+ pvtnotice_host = "%K(%m$0-%K)";
+ servernotice = "%g!$0-%n ";
+
+ # CTCPs
+ ownctcp = "[%r$0%K(%R$1-%K)] ";
+ ctcp = "%g$0-%n";
+
+ # wallops
+ wallop = "%W$0-%n: ";
+ wallop_nick = "%n$0-";
+ wallop_action = "%W * $0-%n ";
+
+ # netsplits
+ netsplit = "%R$0-%n";
+ netjoin = "%C$0-%n";
+
+ # /names list
+ names_nick = "[%_$0%_$1-] ";
+ names_users = "[%g$0-%n]";
+ names_channel = "%G$0-%n";
+
+ # DCC
+ dcc = "%g$0-%n";
+ dccfile = "%_$0-%_";
+
+ # DCC chat, own msg/action
+ dccownmsg = "[%r$0%K($1-%K)%n] ";
+ dccownnick = "%R$0-%n";
+ dccownaction = "{action $0-}";
+ dccownaction_target = "{action_core $0}%K:%c$1%n ";
+
+ # DCC chat, others
+ dccmsg = "[%G$1-%K(%g$0%K)%n] ";
+ dccquerynick = "%G$0-%n";
+ dccaction = "%W (*dcc*) $0-%n %|";
+
+ ##
+ ## statusbar
+ ##
+
+ # background of statusbar
+ sb_background = "%4";
+
+ # default statusbar item style
+ sb = "%c[%n$0-%c]%n";
+
+ sbmode = "(%c+%n$0-)";
+ 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-";
+};
--- /dev/null
+docdir = $(prefix)/doc/irssi
+
+doc_DATA = \
+ formats.txt \
+ manual.txt \
+ faq.txt \
+ startup-HOWTO.html \
+ startup-HOWTO.txt
+
+EXTRA_DIST = $(doc_DATA)
+
+SUBDIRS = help
--- /dev/null
+Q: Why doesn't irssi display colors even when ircii etc. displays them?
+A: Irssi uses curses, ircii and others don't (no, not even if ldd says they
+ use it). Curses decides if terminal supports colors based on the TERM
+ variable, so changing it should help. Some values to try when running
+ in xterm are: xterm, xterm-color and color_xterm.
+
+Q: Why does irssi crash when pressing Ctrl-4?
+A: Most unices are usually configured to send SIGQUIT to active process when
+ Ctrl-\ is pressed. Some terminals also treat Ctrl-4 and Ctrl-\ equally, so
+ Irssi will die to SIGQUIT. Two ways to fix this: change it to something
+ else with stty (stty quit undef) or in irssi /SET ignore_signals quit
+
+Q: Where's the GUI version?
+A: Read http://irssi.org/?page=about
+
+Q: How do I easily write text to channel that starts with '/' character?
+A: / /text
+
+Q: I connected to some server which isn't responding but now irssi tries
+ to connect back to it all the time! How can I stop it?
+A: Two ways. The "good way" to do it is with /DISCONNECT. Check the server
+ tags first with /SERVER without giving it any parameters, reconnections
+ are those that have tag starting with "recon" text. So most probably you're
+ going to do /DISCONNECT recon-1. The other way is to remove all the
+ reconnections with /RMRECONNS, easier but may remove some connections
+ you actually wanted to reconnect (if you used multiple servers..).
+
+Q: Why does irssi say "Irssi: Channel not fully synchronized yet, try again
+ after a while" when I try to use /BAN etc?
+A: IRC server you use is coded badly, do something like this:
+ /IRCNET ADD -querychans 1 quakenet
+ /SERVER ADD -ircnet quakenet irc.quakenet.org
+ /SAVE
+ After that /CONNECT quakenet should work properly (NOTE: when you do this
+ the first time you'll have to /DISCONNECT and /CONNECT again, /SERVER
+ doesn't work correctly).
--- /dev/null
+
+ Irssi's colors that you can use in text formats, hilights, etc. :
+
+ text text background
+ ---------------------------------------------------------------------
+ %k %K %0 black bold black black
+ %r %R %1 red bold red red
+ %g %G %2 green bold green green
+ %y %Y %3 yellow bold yellow yellow
+ %b %B %4 blue bold blue blue
+ %m %M %5 magenta bold magenta magenta
+ %p %P magenta (think: purple)
+ %c %C %6 cyan bold cyan cyan
+ %w %W %7 white bold white white
+ %n %N Turn off all colors and other formatting
+ %F Blinking on/off (think: flash)
+ %U Underline on/off
+ %8 Reverse on/off
+ %9 %_ Bold on/off
+ %: Insert newline
+ %| Marks the indentation position
+ %% A single %
+
+ In .theme files %n works a bit differently. See default.theme
+ for more information.
+
+
+ MIRC colors that you can use when writing text to channel:
+
+ foreground (fg) background (bg)
+ -------------------------------------------------------
+ 0 white black with 0 as fg
+ light gray with fg > 0
+ 1 ligh gray black
+ 2 blue blue
+ 3 green green
+ 4 light red red + blinking fg
+ 5 orange orange
+ 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)
+
+ These colors may differ depending on your terminal.
+
+ How to use these colors ('#' means a number as MIRC color code):
+
+ <Ctrl>-b set bold
+ <Ctrl>-c#[,#] set foreground and optionally background color
+ <Ctrl>-o reset all formats to plain text
+ <Ctrl>-v set inverted color mode
+ <Ctrl>-_ set underline
+ <Ctrl>-7 same as <Ctrl>-_
+
+ To reset a mode set it again, f.e.
+ <Ctrl-C>3<Ctrl-V>FOO<Ctrl-V>BAR
+ creates black on green FOO followed by a green on black BAR
--- /dev/null
+# Makefile.am is autogenerated by autogen.sh from Makefile.am.gen
+
+helpdir = $(datadir)/irssi/help
+
+help_DATA = \
+@HELPFILES@
+
+EXTRA_DIST = \
+ Makefile.am.gen \
+ $(help_DATA)
+
+SUBDIRS = in
--- /dev/null
+Makefile.am
+Makefile.in
+Makefile
--- /dev/null
+# Makefile.am is autogenerated by autogen.sh from Makefile.am.gen
+
+EXTRA_DIST = \
+ Makefile.am.gen \
+@HELPFILES@
--- /dev/null
+
+@SYNTAX:action@
+
+Same as ME, but gets channel or nick as an additional parameter.
+Example: /ACTION #irssi yawns
+(This outputs the following to #irssi: * Nick yawns)
+
+See also: ME
+
--- /dev/null
+
+@SYNTAX:admin@
+
+Displays the administrative details about the given server. If
+no server is specified, the server you are connected to is
+used. If a nickname is supplied then it gives the administrative
+information for that person's current server.
+
--- /dev/null
+
+@SYNTAX:alias@
+
+Creates a new alias or shows matching defined aliases. Without
+parameters shows all defined aliases. Multiple commands can be run if
+separated with ';' character. Parameters given to alias are in $0..$9
+variables, if you don't use them in your alias, all parameters are
+automatically appended after it.
+
+Examples:
+
+/ALIAS w
+ - shows all defined aliases starting with letter w.
+
+/ALIAS send echo Sending file $1 to $0;dcc send $0-
+ - creates alias 'send'.
+
+/ALIAS -send
+ - removes alias 'send'.
+
+See also: UNALIAS
+
--- /dev/null
+
+@SYNTAX:away@
+
+ -one
+ -all
+
+This command marks you as being "away". It is used to tell people that
+you currently aren't paying attention to your screen. You might use it
+if you are taking a nap, in the shower, getting some food, or otherwise
+just aren't there at the moment. When you're "away" you will see "(zZzZ)"
+in your statusbar.
+
+Anyone who does a WHOIS on your nickname will see that you are away,
+as well as your away message. Anyone doing a WHO that returns information
+about you will also see that you're gone.
+
+By default, if someone sends you a MSG while you are away, your client
+will beep. You can turn this off by setting BEEP_WHEN_AWAY to OFF.
+
+If you send a MSG to someone who is away, you will automatically be
+notified of this. By default, you will only receive this notification
+once. If you wish to see it every time (to tell when a person is no
+longer marked away, for instance), change SHOW_AWAY_ONCE to OFF.
+
+You can remove your away status by using AWAY with no arguments.
+
+See also: SET AWAY
+
--- /dev/null
+
+@SYNTAX:ban@
+
+Bans the specified nick or userhost mask.
+
+If nick is given as parameter, the ban type is used to generate the ban
+mask. /SET ban_type specified the default ban type. Ban type is one of
+the following:
+
+ Normal - *!user@*.domain.net
+ Host - *!*@host.domain.net
+ Domain - *!*@*.domain.net
+ Custom [nick] [user] [host] [domain]
+
+Examples:
+ /BAN looser - This bans the nick 'looser'
+ /BAN *!*@*.org - This bans all the users coming from any
+ .org domain.
+
+ /SET ban_type custom nick domain - nick!*@*.domain.net
+ /SET ban_type custom user host - *!user@host.domain.net
+
+See also: KNOCKOUT, KICKBAN
+
--- /dev/null
+
+@SYNTAX:beep@
+
+Outputs the bell-character, usually causing
+your terminal beep.
+
--- /dev/null
+
+@SYNTAX:bind@
+
+Bind some action to specified keystroke. Remember that all characters
+in keystrokes are case-sensitive! Uppercase letter usually means that
+you need to keep SHIFT pressed to get the key to work.
+
+Most most commonly used keystrokes are:
+
+ ^X - Ctrl-X
+ meta-x - Meta-x (Meta is quite often Alt-key in PCs, ESC-x works too)
+
+Irssi has by default also defined several other keys which you can use:
+
+ return - The return/enter key
+ up, down, left, right - Arrow keys
+ home, end, prior, next - prior = Page Up, next = Page Down
+ insert, delete
+
+The keystroke can contain as many key presses as you want, and you can
+define names for different key sequences to use them more easily (the
+keys above are done like that). For example, you may want to manage
+windows with ^W key, so that ^W^C creates new window, ^W^K kills the
+active window, etc. you may do it like:
+
+ /BIND ^W^C /WINDOW NEW HIDE
+ /BIND ^W^K /WINDOW KILL
+
+But maybe you wish to give these binds to other people who want to use
+some other key than ^W, then it would be better done as:
+
+ /BIND ^W key window
+ /BIND window-^C /WINDOW NEW HIDE
+ /BIND window-^K /WINDOW KILL
+
+
+Command can be one of:
+
+ command - Run any /COMMAND (you could use /COMMAND directly without
+ specifying this)
+
+(Cursor movement)
+ backward_character
+ forward_character
+ backward_word
+ forward_word
+ beginning_of_line
+ end_of_line
+
+(Scrollback movement)
+ scroll_backward - Previous page
+ scroll_forward - Next page
+ scroll_start - Beginning of the window
+ scroll_end - End of the window
+
+(Switching windows)
+ change_window
+ previous_window
+ next_window
+ upper_window
+ lower_window
+ active_window - Go to next window with the highest activity
+ next_window_item - Next channel/query. In empty windows change
+ to next server
+ previous_window_item - Previous channel/query. In empty windows change
+ to previous server
+
+(History)
+ backward_history
+ forward_history
+
+(Deleting text)
+ backspace
+ delete_character
+ delete_character
+ delete_next_word
+ delete_previous_word
+ delete_to_previous_space
+ erase_line
+ erase_to_beg_of_line
+ erase_to_end_of_line
+
+(Word completion)
+ word_completion
+ check_replaces - Check word replaces
+
+(Misc)
+ refresh_screen
+ yank_from_cutbuffer - "Undelete" line
+ transpose_characters - Swap current and previous character
+ insert_text - Insert data to entry line, data may contain $variables.
+
+Examples:
+
+Clear screen:
+ /BIND meta-c /CLEAR
+
+People with qwertz layout probably want to swap meta-y and meta-z:
+ /BIND meta-z change_window 16
+ /BIND -delete meta-y
--- /dev/null
+
+@SYNTAX:cat@
+
+Outputs the contents of the specified file. Equivalent to
+UNIX 'cat' command.
+
+See also: CD
+
--- /dev/null
+
+@SYNTAX:cd@
+
+Changes the current working directory. Equivalent to UNIX
+'cd' command.
+
+See also: DCC GET
+
--- /dev/null
+
+@SYNTAX:channel@
+
+Irssi can automatically join to specified channels in specified
+IRC networks. It can also automatically send the password when
+manually joining to channel without specifying the password.
+
+/CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
+ <channel> <ircnet> [<password>]
+
+With -bots and -botcmd arguments you can automatically send
+commands to someone in channel. This is useful for automatically
+getting ops for channels, for example
+
+/CHANNEL ADD -auto -bots "*!bot@bothost.org bot*!*@host2.org"
+ -botcmd "msg $0 op mypass" #channel ircnet
+
+You can also use the -botcmd without -bots argument. The command is
+then sent whenever you join the channel.
+
+If you want to remove some settings from existing channel record,
+for example bots, just give the -bots "" parameters to it. Password
+can be removed by setting it to - (or actually, "" works too).
+
+You can remove the channels with
+/CHANNEL REMOVE <channel> <ircnet>
+
+/CHANNEL LIST displays list of channels with settings.
+
+/CHANNEL without any arguments displays list of channels you have
+joined. You can also use /CHANNEL to join to channels just as with
+/JOIN, like /CHANNEL #a.
+
+See also: TS, JOIN
+
--- /dev/null
+
+@SYNTAX:clear@
+
+This command clears the current window of all text. It is useful
+for wiping a screen that has rendered improperly (such as due
+to a bad termcap entry) or that contains sensitive information
+(such as one's OPER password).
+
+
--- /dev/null
+
+@SYNTAX:connect@
+
+ -4, -6: specify explicitly whether to use IPv4 or IPv6 address
+ -ircnet: the IRCNet
+ -host: the host
+
+This command makes irssi to connect to specified server.
+Current connections are kept and a new one is created.
+
+See also: SERVER
+
--- /dev/null
+
+@SYNTAX:ctcp@
+
+Sends a CTCP-message. For example CTCP ACTION, or CTCP VERSION.
+
+See also: ME, ACTION
+
--- /dev/null
+
+@SYNTAX:cycle@
+
+Cycles (leaves and joins) the current channel or the specified
+channel.
+
+See also: JOIN, LEAVE, PART
+
--- /dev/null
+
+@SYNTAX:time@
+
+This displays the time of day, local to the server queried (thus,
+the time returned may not be the same as the client's local time).
+
+If the server name is omitted, the client's current server is used.
+If a nickname is given, that client's server is queried.
+
+Same as /TIME.
+
--- /dev/null
+
+@SYNTAX:dcc@
+
+This is a command to handle different DCC-connections. DCC is mainly
+used for more reliable and faster chatting and for sending and receiving
+files.
+
+/DCC LIST
+ - Shows all the open DCC connections.
+/DCC RESUME [<nick> [<file>]]
+ - Resumes a DCC SEND/GET connection.
+/DCC CHAT [<nick>]
+ - Sends a chat connection request to remote client or accepts
+ a chat connection if the remote end has already sent a request.
+/DCC GET [<nick> [<file>]]
+ - Gets the file offered by remote client. The file is downloaded and
+ saved into the current working directory.
+/DCC SEND <nick> <file>
+ - Sends a DCC SEND request to remote client. Remote end has to accept
+ the request before the transmission can be started.
+/DCC CLOSE <type> <nick> [<file>]
+ - Closes a DCC-connection. Type can be either SEND, GET or CHAT.
+
+See also: CD
+
--- /dev/null
+
+@SYNTAX:dehilight@
+
+Makes irssi not to highlight items containing the text.
+If parameter is a number, deletes the specified hilight
+entry from the list.
+
+See also: HILIGHT
+
--- /dev/null
+
+@SYNTAX:deop@
+
+Takes off the channel operator privileges from the
+specified nick(s).
+
+Wildcards in the nick are allowed.
+
+See also: OP
+
--- /dev/null
+
+@SYNTAX:devoice@
+
+Takes off the voice from the specified nick(s). This makes them
+not to be able to send messages to the moderated (+m) channel.
+
+Wildcards in the nick are allowed.
+
+See also: VOICE, MODE
+
--- /dev/null
+
+@SYNTAX:die@
+
+IRC-operator command. Makes IRC-server to die.
+
+See also: OPER
+
--- /dev/null
+
+@SYNTAX:disconnect@
+
+Disconnects from the specified IRC-server.
+The server tags can be seen with:
+/SERVER LIST
+
+See also: CONNECT, SERVER
+
--- /dev/null
+
+@SYNTAX:echo@
+
+Prints text into the current window. Useful for scripts.
+
--- /dev/null
+
+@SYNTAX:eval@
+
+Evaluates the given commands and executes them. Internal variables
+are expanded. See the special_vars.txt file in the docs-directory.
+
--- /dev/null
+
+@SYNTAX:exec@
+
+ -: Don't print "process terminated ..." message
+ -nosh: Don't start command through /bin/sh
+ -out: Send output to active channel/query
+ -msg: Send output to specified nick/channel
+ -notice: Send output to specified nick/channel as notices
+ -name: Name the process so it could be accessed easier
+
+ -window: Move the output of specified process to active window
+ -close: Forcibly close (or "forget") a process that doesn't die.
+ This only removes all information from irssi concerning the
+ process, it doesn't send SIGKILL or anything the process.
+ -<signal>: Send a signal to process. <signal> can be either numeric
+ or one of the few most common ones (hup, term, kill, ...)
+
+ -in: Send text to standard input of the specified process
+ -interactive: Creates a query-like window item. Text written to it is
+ sent to executed process, like /EXEC -in.
+
+Execute specified command in background. Output of process is printed
+to active window by default, but can be also sent as messages or
+notices to specified nick or channel.
+
+Processes can be accessed either by their ID or name if you named it.
+Process identifier must always begin with '%%' character, like %%0 or
+%%name.
+
+Once the process is started, it's output can still be redirected
+elsewhere with the -window, -msg, etc. options. You can send text to
+standard input of the process with -in option.
+
+-close option shouldn't probably be used if there's a better way to
+kill the process. It is meant to remove the processes that don't die
+even with SIGKILL. This option just closes the pipes used to
+communicate with the process and frees all memory it used.
--- /dev/null
+
+@SYNTAX:format@
+
+ -reset
+ -delete
+
+Allows you to view/change irssi's messages.
+Use this command with care.
+
--- /dev/null
+
+@SYNTAX:hash@
+
+Not available.
+
--- /dev/null
+
+@SYNTAX:help@
+
+Shows help on commands. Try:
+ /HELP command
+
+Also try, for example:
+ /SET beep
+or
+ /SET auto
+
+See also:
+
--- /dev/null
+
+@SYNTAX:hilight@
+
+ -mask: Match only for nick, <text> is a nick mask
+ -regexp: <text> is a regular expression
+ -full: <text> must match to full words
+ -nick: Hilight only the nick, not the whole line (default)
+ -word: Hilight only the word (default with non-public messages)
+ -line: Hilight the whole line with the hilight color.
+ -color: Print the message with <color>. color is in %code format
+ (see docs/formats.txt)
+ -level: Match only for <level> messages, default is
+ publics,msgs,notices,actions
+ -channels: Match only in <channels>
+
+Examples:
+
+Hilight lines that have "mynick" word:
+ /HILIGHT mynick
+
+Hilight lines that were written by nicks from *.fi with bold green
+ /HILIGHT -color %G -mask *!*@*.fi
+
+For regular expressions, see `man 7 regex`.
+
+See also: DEHILIGHT, SET HILIGHT
+
--- /dev/null
+
+@SYNTAX:ignore@
+
+ -regexp: <pattern> is a regular expression
+ -word: <pattern> must match to full words
+ -pattern: <pattern> must match to the message's text
+ -replies: Ignore replies to nick in channels. For example
+ "/IGNORE -replies *!*@*.fi PUBLIC" ignores everyone
+ from Finland, but also anyone sending message
+ "tofinnishnick: blahblah".
+ -except: *DON'T* ignore
+ -channels: Ignore only in channels
+ <mask>: Either a nick mask or list of channels
+ <levels>: List of levels to ignore
+ <^levels>: List of levels to NOT ignore
+ (/ignore -except nick notices = /ignore nick ^notices)
+
+
+/IGNORE without any arguments displays list of ignores.
+
+The best match always wins, so you can have:
+ /IGNORE * CTCPS
+ /IGNORE -except *!*@host.org CTCPS
+
+See also: UNIGNORE
+
--- /dev/null
+
+@SYNTAX:info@
+
+Shows information about the IRC creators, debuggers, slaves and
+a lot of other people who no longer have much to do with irc.
+
--- /dev/null
+
+@SYNTAX:invite@
+
+Invites the specified nick to the current or specified channel.
+
+Example:
+ /INVITE buddy #mychannel
+
+See also: MODE
+
--- /dev/null
+
+@SYNTAX:invitelist@
+
+Shows the +I modes of the current channel. +I mode
+allows free joins of clients with certain userhost mask
+even if the channel is invite only.
+
+See also: INVITE, MODE
+
--- /dev/null
+
+@SYNTAX:ircnet@
+
+ -kicks: Maximum number of nicks in one /KICK command
+ -msgs: Maximum number of nicks in one /MSG command
+ -modes: Maximum number of mode changes in one /MODE command
+ -whois: Maximum number of nicks in one /WHOIS command
+ -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1
+ -cmdmax: Same as /SET cmd_max_at_once, see section 3.1
+ -nick, -user, -realname: Specify what nick/user/name to use
+ -host: Specify what host name to use, if you have multiple
+ -autosendcmd: Command to send after connecting to a server
+
+With -autosendcmd argument you can automatically run any commands
+after connecting to ircnet. This is useful for automatically
+identifying yourself to NickServ, for example
+
+Shows and changes the settings of defined IRC networks.
+
+See also: CONNECT
+
--- /dev/null
+
+@SYNTAX:ison@
+
+Tells whether specified nicks are online.
+
+See also: WHOIS, WHOWAS, NOTIFY
+
--- /dev/null
+
+@SYNTAX:join@
+
+Joins a specified channel. Channel names usually begin with #-sign,
+which may be omitted here.
+
+JOIN is aliased to J by default. Example: /j irssi
+(This joins to the channel #irssi)
+
+Description
+
+See also: LEAVE, WINDOW CLOSE
+
--- /dev/null
+
+@SYNTAX:kick@
+
+This command "kicks" the specified user off of the specified
+channel. It is typically used to remove troublemakers, flooders,
+or people otherwise making a nuisanse of themselves on the channel.
+The reason for the kick is recommended, but not required by the IRC
+servers
+
+If the <channel> is omitted, removes the nick from the current
+channel.
+
+The default alias for /KICK is /K.
+
+See also: KNOCKOUT
+
--- /dev/null
+
+@SYNTAX:kickban@
+
+Kicks off and bans a nick from the current channel.
+A reason for the kick can be supplied.
+
+Default alias for /KICKBAN is /KB.
+
+See also: KNOCKOUT, BANTYPE
+
--- /dev/null
+
+@SYNTAX:kill@
+
+IRC operator command.
+
+KILL is used to forcibly remote a client from the irc network.
+It works similarly to KICK, except that a reason must be
+given (even if it is meaningless or flat-out wrong).
+
+In general, KILL is useful only as a warning tool for abusive
+users. Modern irc clients (this one included) have automated
+means for reconnecting to a server after a disconnection (whether
+due to a KILL or something else), so KILL is by no means a
+permanent solution. It is not intended as a means for personal
+vendettas; this practice is generally frowned upon.
+
+See also: OPER
+
--- /dev/null
+
+@SYNTAX:knockout@
+
+Kicks user off the channel and bans him/her. Ban
+lasts the given number of seconds or 5 minutes
+by default.
+
+Default alias for /KNOCKOUT is /KN.
+
+See also: BAN, KICK
+
--- /dev/null
+
+@SYNTAX:lastlog@
+
+ -clear: remove all lastlog lines from window
+
+ -: don't print the "Lastlog:" and "End of Lastlog" messages.
+ -file: write lastlog to file instead of screen
+ -window: which window's lastlog to check (output is always to active)
+ -case: Case-sensitive matching
+ -force: Force displaying lastlog even if it's longer than 1000 lines
+ -new: show only lines since last /LASTLOG
+ -regexp: `text' is a regular expression
+ -word: `text' must match to full words
+ -level: what levels to check, like -public -msgs (default is all)
+ <pattern>: text to search for, or all if empty
+ <count>: maximum number of lines to show
+ <start>: skip the last `start' lines
+
+Shows the given number of lines of log from the current window.
+
+See also:
+
--- /dev/null
+
+@SYNTAX:layout@
+
+Saves the current window layout to configuration (yes, you'll still
+need to use /SAVE to save the configuration to file). Next time you run
+irssi, all the channels and queries are exactly in the same windows
+where they were when you called /LAYOUT SAVE.
+
+Channels aren't actually joined in those windows immediately, they're
+just marked "next time you join to '#channel' in server that has tag
+'ircnet' place it to this window".
+
+/LAYOUT RESET removes the saved layout.
--- /dev/null
+Message levels (or in short, levels) are used almost everywhere.
+They describe what kind of messages we're dealing with. Here's a
+list of them all:
+
+ CRAP - Can be almost anything
+ MSGS - Private messages
+ PUBLIC - Public messages in channel
+ NOTICES - Notices
+ SNOTES - Server notices
+ CTCPS - CTCP messages
+ ACTIONS - Actions (/me) - usually ORed with PUBLIC or MSGS
+ JOINS - Someone joins a channel
+ PARTS - Someone parts a channel
+ QUITS - Someone quits IRC
+ KICKS - Someone gets kicked from channel
+ MODES - Channel mode is changed
+ TOPICS - Channel topic is changed
+ WALLOPS - Wallop is received
+ INVITES - Invite is received
+ NICKS - Someone changes nick
+ DCC - DCC related messages
+ DCCMSGS - DCC chat messages
+ CLIENTNOTICES - Irssi's notices
+ CLIENTERRORS - Irssi's error messages
+ CLIENTCRAP - Some other messages from Irssi
+
+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
+
--- /dev/null
+
+@SYNTAX:links@
+
+Shows the links between the IRC servers of the
+current IRC network. If a wildcard parameter is
+specified, shows only the matching entries.
+
+See also:
+
--- /dev/null
+
+@SYNTAX:list@
+
+Lists the channel names. Trying to list all the channel
+names usually causes you to be disconnected from the
+server with the reason "Excessive flood".
+
+See also:
+
--- /dev/null
+
+@SYNTAX:load@
+
+Load a plugin. If full path isn't given, irssi searches the plugin from
+directories:
+
+ ~/.irssi/modules/
+ <install dir, /usr/local or /usr maybe>/lib/irssi/modules/
+
+See plugins page of http://irssi.org/ to see if there's any plugins
+you'd want to use.
+
+See also: UNLOAD
+
--- /dev/null
+
+@SYNTAX:log@
+
+ -noopen: Create the entry to log list, but don't start logging
+ -autoopen: Automatically open this log file at startup
+ -<server tag>: Targets are logged only in this server
+ -targets: Log only in specified channels/nicks
+ -window: Log the active window
+ <filename>: File name where to log, it is parsed with
+ strftime(), so %%d=day, etc. see "man strftime" for
+ more info.
+ <levels>: Defaults to ALL
+ <id>: ID number of log.
+
+/SET log_create_mode <mode> - Specifies what file mode to use with
+ the created log files. Default is 0644.
+
+All of these are parsed with strftime():
+/SET log_timestamp <text> - Specifies the time stamp format.
+ Default is "%%H:%%M ".
+/SET log_open_string <text> - Text written to log when it's opened
+/SET log_close_string <text> - Text written to log when it's closed
+/SET log_day_changed <text> - Text written to log when day changes
+
+NOTE: Log files are locked after opened, so two Irssis can't
+accidentally try to write to the same log file.
+
+Examples:
+
+/LOG OPEN -targets cras ~/irclogs/cras.log MSGS
+ - Logs all messages from/to nick `cras'
+
+/LOG OPEN -targets #linux ~/irclogs/linux/linux-%%Y-%%m-%%d
+ - Logs all messages in channel #linux. Log is rotated daily, so
+ logs in 1. May 2000 goes to file "linux-2000-05-01", when the
+ day is changed, Irssi closes the log and starts logging to
+ "linux-2000-05-02" etc.
+
+See also: SET LOG, WINDOW LOG
+
--- /dev/null
+
+@SYNTAX:lusers@
+
+Shows user statistics of the current IRC network.
+
--- /dev/null
+
+@SYNTAX:map@
+
+Not available in IRC.
+
--- /dev/null
+
+@SYNTAX:me@
+
+Sends a CTCP ACTION to the current channel or query.
+For example: /me sits back.
+
+See also: ACTION, CTCP
+
--- /dev/null
+
+@SYNTAX:mircdcc@
+
+Selects whether to send mIRC style CTCPs in DCC chat
+session.
+
+If a mIRC user sends first a CTCP, mIRC style CTCPs is
+automatically selected for that DCC Chat session.
+
+See also: SET MIRC
+
--- /dev/null
+
+@SYNTAX:mode@
+
+Irssi knows these channel modes:
+
+ i - Invite only - People can't join to channel without being
+ /INVITEd, or being in invite list (+I, see below).
+ m - Moderated - People who don't have voices (+v) can't send
+ messages to channel
+ p - Private - People who aren't joined to channel can't see it
+ for example with /WHOISing people who are in channel.
+ s - Secret - Like private, but the channel isn't displayed in
+ /LIST's output.
+ n - No external msgs - Without this mode, anyone can send messages
+ to channel without even being joined.
+ t - Topic can be changed only by channel operators.
+
+ k <key> - Channel password (aka. key) - The channel can't be joined
+ without specifying the channel key (see section 6.2).
+
+ l <count> - User limit - No more than <count> people can join to
+ channel. This can be overridden with /INVITE with some
+ servers.
+
+ This is usually used for protecting channel from join
+ flooding, like some bot allows max. 5 users to join in
+ one minute or so.
+
+ a - Anonymous - No-one's nick name, host or anything else can be
+ seen. All messages, joins, parts, modes, etc. are seen as coming
+ from nick "anonymous", this could be pretty confusing but nice
+ feature if you want total anonymity. This mode can only be set,
+ never unset. This mode isn't supported by all servers.
+
+ NOTE: there is/was one bug :) Channel operators can guess if some
+ nick might be in the channel and try to kick it. If nick was in
+ channel, everyone will see the nick that was kicked.
+
+ r - Re-op - If channel becomes opless for longer than 45 (?) minutes,
+ op everyone in the channel. This works only in !channels. This
+ mode can only be set, not unset by channel creator.
+
+ b - Set/remove ban. For example MODE #channel +b *!*@*.org bans
+ everyone from .org domain.
+
+ If someone from .org domain was already in channel before the
+ ban was set, he/she couldn't be able to write any messages to
+ channel (doesn't work with all servers).
+
+ Ban can also be overridden with /INVITE, although many stupid
+ IRC clients automatically kick the user out because they see
+ the ban and think that because of it the user shouldn't be in
+ the channel (doesn't work with all servers).
+
+ e - Ban exceptions. You could for example ban everyone from
+ *!*@*.org but set ban exception to *!*@*.host.org - works only
+ in IRCnet/EFnet servers.
+
+ I - Invite list. If channel is invite only (+i), people in this
+ list can join it without being /INVITEd - works only in
+ IRCnet/EFnet servers.
+
+ This is excellent for in-country channels that don't want
+ foreigners (spammers!) to join the channel, for example setting
+ channel's mode to +i and +I *!*@*.fi allows only finnish people
+ to join the channel. In addition to this, there's usually a bot
+ in the channels and sending /MSG bot invite command to it
+ /INVITEs you to the channel.
+
+ The ':' feature in channel modes is quite similiar, see section
+ 6.2.
+
+ O - Channel owner, the nick who creates a !channel receives this
+ mode. It isn't displayed anywhere, you can't pass it to anyone
+ else and you can't regain it again. This is needed for setting
+ +r mode in channel when it's first created.
+
+ o <nick> - Grant or revoke channel operator status from nick
+ v <nick> - Grant or revoke voice status from nick, only people with
+ +v (or +o) can talk to channel when it's moderated (+m).
+
+You can send multiple mode changes with one mode command:
+
+/MODE #channel +nto-o+v nick1 nick2 nick3
+
+This would set channel's mode to +nt, give ops to nick1, take ops
+from nick2 and give voices to nick3.
+
+You can set only limited number of modes that requires argument in
+one command. In IRCnet it's 3, in EFnet it's 4 and in many others
+it's 6. If it's not known, Irssi defaults to 3. Irssi will also
+automatically split them, so you can use /MODE +oooooo n1,n2,..
+command to op 6 people and Irssi will split it to two commands in
+IRCnet/EFnet.
+
+See also: OP, DEOP, VOICE, DEVOICE, BAN, UNBAN
+
--- /dev/null
+
+@SYNTAX:motd@
+
+Shows the motd of the current server. This contains
+usually some useful info on the server, administrator and
+the rules.
+
--- /dev/null
+
+@SYNTAX:msg@
+
+Sends a message to a nick or a channel. Usually this
+is used for sending private messages to other persons.
+
+Examples:
+
+/MSG friend Hi, what's up?
+
+/MSG #irssi Hello, is the new gtk-version out already?
+(This format is rarely needed.)
+
+See also: CTCP
+
--- /dev/null
+
+@SYNTAX:names@
+
+ -ops: show channel operators in list
+ -halfops: show half operators in list
+ -voices: show voiced people in list
+ -normal: show rest of the people in list
+
+Shows the names (nicks) in the specified channels. /NAMES ** shows all
+nicks in all channels, you probably don't want to do this.
+
+Examples:
+
+/NAMES
+ - shows nicks in the current channel.
+
+/NAMES -ops #c1,#c2
+ - shows operators in channels #c1 and #c2
+
+See also: WHO, CHANNEL
+
--- /dev/null
+
+@SYNTAX:nctcp@
+
+Sends a CTCP reply notice to the nick/channel.
+
+See also: CTCP, ACTION, MSG, NOTICE
+
--- /dev/null
+
+@SYNTAX:netsplit@
+
+Irssi keeps track of people who were lost in net splits. With this
+command you can get a list of them.
+
--- /dev/null
+
+@SYNTAX:nick@
+
+Changes your nick. This should be hardly rarely
+used or needed.
+
--- /dev/null
+
+@SYNTAX:note@
+
+NOTE is a sort of turbo-charged messaging system for irc. In short,
+it achieves at the server level what the client attempts to do with MSG
+and NOTIFY. The messaging system resembles modern voicemail systems
+(except in text); messages can be sent, stored, or set for deferred
+delivery. The client notification system works like NOTIFY, except with
+greater accuracy and flexibility.
+
+The most common uses of NOTE are its SPY and SEND functions. SPY is similar
+to NOTIFY, except it can accept a full address to spy on, not just a nickname.
+SEND, as its name implies, sends a note to a user; if that user is not currently
+online, it will be delivered if the user logs onto irc within a set time period.
+
+When referring to a particular user, NOTE can deal with the standard
+nick!user@host notation. Wildcards are allowed, and any portion may be omitted,
+so long as the identifier remains unambiguous.
+
+Examples:
+To send a note to Joebob (whose account is jbriggs@drivein.com):
+/NOTE SEND joebob!jbriggs@drivein.com Hey there! Great movie!
+
+To spy on anyone from blah.com for the next 30 days:
+/NOTE SPY +30 *!*@*.blah.com A blah.com user is active
+
+This command is Not available in the IRCNet.
+
--- /dev/null
+
+@SYNTAX:notice@
+
+Sends a notice to the nick or the channel. Usually notices are
+used in bots and scripts for different kinds of replies. The
+IRC protocol states that notices may not generate replies to
+avoid msg loops.
+
+See also: NCTCP, MSG
+
--- /dev/null
+
+@SYNTAX:notify@
+
+ -away: Notifies about away-status changes
+ -idle: Notifies if idle time is first larger than <minutes>
+ (default is hour) and then it drops down.
+ -list: Lists the notify list entries with all their settings
+ <mask>: Either a simple "nick" or "nick!*@*blah.org".
+ The nick can't contain wildcards, but the user/host can.
+
+/NOTIFY without any arguments displays if the people in notify
+list are online or offline.
+
+See also: UNNOTIFY, SET NOTIFY
+
--- /dev/null
+
+@SYNTAX:op@
+
+Gives the channel operator privileges for the specified
+nick(s). Wildcards in the nick are allowed.
+
+See also: DEOP, MODE, VOICE, DEVOICE, KICK
+
--- /dev/null
+
+@SYNTAX:oper@
+
+Gives you operator priviledges if the correct nickname and
+password are given. If password is not given, you will be
+prompted for one. If no nickname is given, your current
+nickname will be used.
+
+See also: KILL, DIE
+
--- /dev/null
+
+@SYNTAX:part@
+
+Parts from the current or specified channel. Depending
+on your settings, closes the corresponding window, too.
+
+See also: LEAVE, JOIN
+
--- /dev/null
+
+@SYNTAX:perlflush@
+
+Stops and removes all Perl scripts which have been run.
+Also undefines all the commands defined by Perl scripts.
+
+See also: RUN
+
--- /dev/null
+
+@SYNTAX:ping@
+
+Sends CTCP PING to another IRC client. This is used
+to find out the speed of IRC network. When the PONG
+reply comes in, irssi shows the interval time between
+sending the request and receiving the reply.
+
+See also: CTCP
+
--- /dev/null
+
+@SYNTAX:query@
+
+Starts a private conversation with the nick. All text you
+type that would normally be sent to your channel now goes to
+the specified nick in the form of MSGs.
+
+Usually this command opens up a new window, too.
+
+See also: WINDOW, MSG, SET QUERY
+
--- /dev/null
+
+@SYNTAX:quit@
+
+This ends your irc session. If a quit message is supplied, it
+will be displayed to anyone else on any channel you were on
+before quitting. If one isn't specified, the text "Leaving" is
+used.
+
+/EXIT does the same.
+
--- /dev/null
+
+@SYNTAX:quote@
+
+Sends server raw data without parsing.
+
+Example:
+ /QUOTE PRIVMSG cras :Hey, this works!
+
--- /dev/null
+
+@SYNTAX:rawlog@
+
+All data that is received or sent to server is kept in a raw log
+buffer for a while. Also event redirections are kept there. This is
+very useful for debugging purposes.
+
+/RAWLOG SAVE <filename> - Save the current raw log buffer to file
+/RAWLOG OPEN <filename> - Like /RAWLOG SAVE, but keep the log file
+ open and write all new log to it.
+/RAWLOG CLOSE - Close the open raw log
+
+/SET rawlog_lines <count> - Specify the number of raw log lines to
+ keep in memory.
+
--- /dev/null
+
+@SYNTAX:reconnect@
+
+You can reconnect to server with /RECONNECT <n>.
+
+/RECONNECT without any arguments will disconnect from the
+active server and reconnect back immediately.
+
+See also: SERVER, DISCONNECT, RMRECONNS
+
--- /dev/null
+
+@SYNTAX:rehash@
+
+IRC Operator command.
+
+This command is used to force the current server to reload it's
+ircd.conf configuration file. This is useful for effecting
+configuration changes without starting a new server.
+
+See also: OPER, RESTART
+
--- /dev/null
+
+@SYNTAX:reload@
+
+Reloads the irssi's configuration file.
+
+See also: SAVE
+
--- /dev/null
+
+@SYNTAX:restart@
+
+IRC Operator command.
+
+This command is used to completely restart the server. A side effect of
+this is that the configuration file will be read again. However, it is
+generally more useful for clearing out internal buffers and other
+wasted memory.
+
+See also: OPER, DIE
+
--- /dev/null
+
+@SYNTAX:rmreconns@
+
+Removes the pending reconnections from the reconnect list.
+
+See also: CONNECT, RECONNECT, SERVER
+
--- /dev/null
+
+@SYNTAX:rmrejoins@
+
+Removes the pending rejoins from the channel rejoin list in active
+server. Channels are added to rejoin list when join failed because of
+netsplits in server ("Channel is temporarily unavailable").
+
+See also: JOIN
+
--- /dev/null
+
+@SYNTAX:rping@
+
+IRC Operator command.
+
+This command works like the PING command (CTCP PING), except
+it is used on a server instead of a client. As with PING, it
+is used to test the relative distance another server is from
+you across the irc network.
+
+See also: OPER
+
--- /dev/null
+
+@SYNTAX:run@
+
+Runs a perl-script. For more information, see the
+perl.txt in the docs-directory of irssi.
+
+See also: PERLFLUSH
--- /dev/null
+
+@SYNTAX:save@
+
+Saves the current Irssi configuration into the configuration
+file.
+
+See also: RELOAD
+
--- /dev/null
+
+@SYNTAX:sconnect@
+
+IRC Operator command. Makes an IRC server to connect
+to another server.
+
+See also: OPER, SQUIT, RESTART
+
--- /dev/null
+
+@SYNTAX:scrollback@
+
+/SCROLLBACK, or the default alias /SB:
+
+/SB CLEAR - Clear screen, free all memory used by texts in window.
+/SB HOME - Jump to start of the buffer
+/SB END - Jump to end of the buffer
+/SB GOTO [[-|+]line#|time] - Jump to specified line or timestamp.
+
+See also: SET SCROLL
+
--- /dev/null
+
+@SYNTAX:server@
+
+ -4, -6: specify explicitly whether to use IPv4 or IPv6 address
+ -auto: Automatically connect to server at startup (default)
+ -noauto: Don't connect to server at startup
+ -ircnet: Specify what IRC network this server belongs to
+ -host: Specify what host name to use, if you have multiple
+ -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1
+ -cmdmax: Same as /SET cmd_max_at_once, see section 3.1
+ -port: This is pretty much like the port argument later,
+ except this can be used to modify existing server's
+ port.
+
+/SERVER disconnects the server in active window and connects
+to the new one. It will take the same arguments as /CONNECT.
+If you prefix the address with the + character, Irssi won't
+disconnect the active server, and it will create a new window
+where the server is connected (ie. /window new hide;
+/connect address)
+
+/SERVER without any arguments displays the list of connected
+ servers.
+
+/SERVER REMOVE <address> [<port>]
+
+/SERVER LIST
+
+See also: RMRECONNS, DISCONNECT, RECONNECT
+
--- /dev/null
+
+@SYNTAX:servlist@
+
+SERVLIST gives the list of services currently present on the
+IRC network. It can take two arguments.
+ <mask> limits the output to the services which names matches
+ the mask.
+ <type> limits the output to the services of the specified type.
+
+The fields returned are:
+ Service name.
+ Server who introduced the service.
+ Distribution mask.
+ Service type.
+ Hop count to the service.
+ A comment.
+
+See also: SQUERY
+
--- /dev/null
+
+@SYNTAX:set@
+
+You can view or change the settings with /SET command.
+
+/SET without any arguments displays all the settings.
+/SET <key> displays settings whose key (partly) matches <key>
+/SET <key> <value> sets <key> to <value>
+
+Boolean settings accept only values ON, OFF and TOGGLE. You can
+also use /TOGGLE command to change them, so /TOGGLE <key> behaves
+like /SET <key> TOGGLE. /TOGGLE also accepts arguments ON and OFF
+when /TOGGLE behaves exactly like /SET.
+
+Remember that changes are not saved until you use /SAVE!
+
+Examples:
+ /SET autolog OFF - Sets value for setting 'autolog'
+ /SET close - Shows all settings whose variable name
+ contains 'close'. Very practical.
+
+See also: TOGGLE
+
--- /dev/null
+
+@SYNTAX:silence@
+
+Works only in the Undernet and Open Projects (ircu).
+
+SILENCE is similar in many respects to IGNORE, except that it is
+server-based. What this means is the server will never even send
+you messages from anyone you have SILENCEd, whereas it will with
+IGNORE, where your client is responsible for filtering the messages
+out. This has the advantage of not bogging your client down with
+excessive data as it tries to filter out messages.
+
+The default behavior is to SILENCE a nick!user@host pattern, and
+if such a pattern is not passed as the argument, it must be prepended
+with a plus ('+') to be added to your silence list. If a pattern is
+prepended with a minus ('-'), it will be removed from your silence list.
+If you only specify a nickname, you can list the patterns in the
+silence list owned by that nickname. If no arguments are given, your
+own silence list is displayed.
+
+See also: IGNORE
+
--- /dev/null
+
+@SYNTAX:squery@
+
+ <service> - Service name
+ <commands> - Commands to pass to the service.
+
+/SQUERY sends a query to the specified service.
+
+See also: SERVLIST
+
--- /dev/null
+
+@SYNTAX:squit@
+
+IRC Operator command. Makes server to quit IRC network.
+
+See also: OPER, DIE, RESTART
+
--- /dev/null
+
+@SYNTAX:stats@
+
+Shows some irc server usage statistics.
+ c - Shows C and N lines for a given server. These are
+ the names of the servers that are allowed to connect.
+ h - Shows H and L lines for a given server (Hubs and Leaves).
+ k - Show K lines for a server. This shows who is not
+ allowed to connect and possibly at what time they are
+ not allowed to connect.
+ i - Shows I lines. This is who CAN connect to a server.
+ l - Shows information about amount of information passed
+ to servers and users.
+ m - Shows a count for the number of times the various
+ commands have been used since the server was booted.
+ o - Shows the list of authorized operators on the server.
+ u - Shows the uptime for a server
+ y - Shows Y lines, which lists the various connection
+ classes for a given server.
+
--- /dev/null
+
+@SYNTAX:time@
+
+This displays the time of day, local to the server queried (thus,
+the time returned may not be the same as the client's local time).
+
+If the server name is omitted, the client's current server is used.
+If a nickname is given, that client's server is queried.
+
+Same as /DATE.
+
--- /dev/null
+
+@SYNTAX:toggle@
+
+/TOGGLE <key> behaves like /SET <key> TOGGLE. /TOGGLE also
+accepts arguments ON and OFF when /TOGGLE behaves exactly
+like /SET.
+
+Remember that changes are not saved until you use /SAVE!
+
+See also: SET
+
--- /dev/null
+
+@SYNTAX:topic@
+
+ -delete - Deletes the topic.
+
+Shows or/and changes the topic of the current or specified
+channel.
+
--- /dev/null
+
+@SYNTAX:trace@
+
+Without a specified server it shows the current connections on
+the local server. If you specify a remote server it will show
+all servers between your current server and that remote server
+as well as the connections on that remote server.
+
--- /dev/null
+
+@SYNTAX:ts@
+
+Shows topics of all channels you're on.
+
+See also: CHANNEL, TOPIC
+
--- /dev/null
+
+@SYNTAX:unalias@
+
+Removes an alias.
+
+See also: ALIAS
+
--- /dev/null
+
+@SYNTAX:unban@
+
+Removes the specified ban(s) from the channel.
+
+Examples:
+ /UNBAN *!*@*.fi
+ /UNBAN larry!*@* *!me@*.mydomain.net
+
+See also: BAN, KNOCKOUT
+
--- /dev/null
+
+@SYNTAX:unignore@
+
+Unignores the specified userhost mask.
+
+See also: IGNORE
+
--- /dev/null
+
+@SYNTAX:unload@
+
+Unload a running plugin. List of running plugins can be shown with
+/LOAD.
+
+See also: LOAD
+
--- /dev/null
+
+@SYNTAX:unnotify@
+
+Removes an entry from the notify list.
+
+See also: NOTIFY
+
--- /dev/null
+
+@SYNTAX:unquery@
+
+Removes a query window of specified nick.
+
+See also: QUERY, SET QUERY
+
--- /dev/null
+
+@SYNTAX:unsilence@
+
+Works only in the Undernet and Open Projects (ircu).
+
+Removes a pattern from your silence list.
+
+See also: SILENCE
+
--- /dev/null
+
+@SYNTAX:uping@
+
+IRC Operator command. Works only in the Undernet and Open Projects (ircu).
+
+This command works like the PING command (CTCP PING), except
+it is used on a server instead of a client. As with PING, it
+is used to test the relative distance another server is from
+you across the irc network.
+
+See also: RPING, OPER
+
--- /dev/null
+
+@SYNTAX:userhost@
+
+Shows the userhost info of the specified nick.
+
+See also: WHOIS
+
--- /dev/null
+
+@SYNTAX:ver@
+
+Sends a CTCP VERSION request to the nick. This is used
+to find out which client and/or script the nick
+is using.
+
+See also: CTCP
+
--- /dev/null
+
+@SYNTAX:version@
+
+Shows the version info of the current or specified
+IRC server.
+
+See also: ADMIN, STATS
+
--- /dev/null
+
+@SYNTAX:voice@
+
+Gives the voice (+v mode) to the nick(s) on the current channel.
+Wildcards in the nick are allowed.
+
+See also: DEVOICE, OP, DEOP
+
--- /dev/null
+
+@SYNTAX:wait@
+
+Wait for <milliseconds> before sending the next command to server.
+
+This could be useful for example when identifying to NickServ; after
+sending the identify message you'd wait 3 seconds before joining to
+channels so NickServ has time to identify you to ChanServ which then
+auto-ops you when joining.
+
--- /dev/null
+
+@SYNTAX:wall@
+
+This command sends a message to all operators in a channel. This is internal
+irssi command which sends a message separately to each opearator, so this
+may not be very good idea to use in channel with lots of operators.
+
+Some IRC servers support also /MSG @#channel or /WALLCHOPS which you should
+use if possible.
+
+See also: WALLCHOPS
--- /dev/null
+
+@SYNTAX:wallchops@
+
+Works only in the Undernet and Open Projects (ircu).
+
+Sends an message to all other channel operators of the current channel.
+
+See also: WALL
+
--- /dev/null
+
+@SYNTAX:wallops@
+
+This command sends the given message to everyone on
+the network who has user mode +w turned on. If you
+are not an operator, you will probably receive
+an error message when using this command
+
+See also: OPER, WALLOPS
--- /dev/null
+
+@SYNTAX:who@
+
+Without parameters, shows all users and their user infos
+on the current channel. If you specify a channel, shows
+the all users of the given channel.
+
+If you specify an string with wildcards, you will be
+shown all users whose nick, userhost or realname matches
+the wildcard expression.
+
+If a channel is secret or private and you're not on it,
+you will be shown only those channel members, who do not
+have the invisible (+i) mode set.
+
+Examples:
+ /WHO - Shows users on current channel
+ /WHO #irssi - Shows users on channel #irssi
+ /WHO timo* - Shows users whose nick, userhost,
+ or realname begins with string 'timo'
+
+See also: WHOIS, CHANNEL
+
--- /dev/null
+
+@SYNTAX:whois@
+
+Shows whois information of the specified nick.
+By default, this is aliased to /WI.
+
+/WHOIS nick1 nick1 also queries the idle time of
+the user. This is aliased to /WII by default.
+
+If given nick is not in the IRC, irssi automatically
+sends a WHOWAS query. Read carefully the reply to see
+if it is a WHOIS or WHOWAS reply. :)
+
+See also: WHO, CHANNEL
+
--- /dev/null
+
+@SYNTAX:whowas@
+
+This command is similar to WHOIS, except it returns information
+about nicknames that were recently in use. Like WHOIS, it shows
+the nickname, address, real name, and server. It may also return
+multiple entries if the nickname has been used recently by several
+people. These multiples may be limited by specifying a count to show.
+
+WHOWAS will work regardless of whether the queried nick is in use.
+If no arguments are given, the client's current nickname is used.
+
+Example: To show the last 5 users of the nickname JoeBob:
+
+/whowas joebob 5
+
+
+See also: WHOIS
+
--- /dev/null
+
+@SYNTAX:window@
+
+This command includes various subcommands for handling
+irssi windows.
+
+/WINDOW CLOSE
+ - Closes the current window.
+/WINDOW MOVE #NUMBER|PREV|NEXT
+ - Moves current window to another position in the
+ window list.
+/WINDOW GOTO #NUMBER
+ - Moves into the specified window.
+/WINDOW GOTO ACTIVE
+ - finds the first window with the higest
+ activity (msgs to you -> msgs -> rest). Alt-A is
+ the default shortcut key for this
+/WINDOW GOTO #CHANNEL|NICK|=NICK
+ - moves you to the window with the channel, query or
+ dcc chat.
+/WINDOW LEVEL [+/-]PUB/MSGS/...
+ - /window level msgs - creates messages window
+ - /window level all -msgs - creates status window
+/WINDOW NEW [HIDDEN|SPLIT]
+ - creates new hidden/splitted window,
+
+Window logging
+
+/WINDOW LOG ON|OFF|TOGGLE [<filename>]
+ - Start/stop logging the active window. This works exactly like
+ /LOG OPEN -window.
+/WINDOW LOGFILE <filename>
+ - Sets the default log file to use in the window, it can be
+ overridden by specifying the file name in /WINDOW LOG.
+ If no file name isn't given, Irssi defaults to
+ ~/irc.log.<windowname> or ~/irc.log.Window<ref#> if window
+ doesn't have a name.
+
+See also: SET CREATE, JOIN, QUERY, LOG
+
--- /dev/null
+
+@SYNTAX:wjoin@
+
+With this you can join multiple channels in same
+window. This command does the same as /JOIN but
+it doesn't create a new window for the channel joined.
+
+See also: JOIN, WINDOW
+
--- /dev/null
+
+@SYNTAX:wquery@
+
+Starts a query in the current window without
+opening a new window.
+
+See also: QUERY, WINDOW, SET AUTOCREATE
+
--- /dev/null
+
+ Irssi 0.8 documentation - http://irssi.org/
+
+ Copyright(c) 2000 Timo Sirainen <tss@iki.fi>
+
+
+ Index
+
+ 0. Generic babbling
+ 1. Installation
+ 2. Message levels
+ 3. Flood protection
+ 4. Configuration
+ 5. Servers
+ 6. Channels
+ 7. IRC commands and features
+ 8. Notify list
+ 9. Text highlighting
+ 10. Ignoring
+ 11. Logging
+
+ ( not written yet: )
+ 12. Aliases
+ 13. Themes
+ 14. Last log (currently text version only)
+ 15. Nick and word completion
+ 16. Translation tables
+ 17. Windowing system (text version)
+ 18. Keyboard (text version)
+ 19. Perl scripting
+
+
+
+ 0. Generic babbling
+
+ 0.1 History
+
+ Hello. I'm Timo Sirainen aka. cras, and I'm an IRC addict. :)
+
+ I'm actually quite new in IRC, I got my first internet connection
+ sometimes around fall 1997 and I started actively IRCing around
+ christmas. I used EPIC and BitchX mostly at the start, but soon
+ found some nice KDE IRC client which name I can't remember anymore.
+ It's author however stopped developing it after I had been using it
+ a few months. And since it had bugs and all, I wanted another nice
+ GUI IRC client. I didn't find any.
+
+ Since I've always been a coder and do-it-yourself guy (my own
+ offline reader and BBS software in the BBS ages), I started my own
+ IRC client at spring 1998. I called it yagIRC standing for "Yet
+ another GTK IRC client". GTK was in around version 1.0 back then,
+ and it had a lot of features/bugs which I found all the time as I
+ tried to do some "different" things than other people. These
+ sometimes prevented me of doing something some feature I wanted.
+
+ So, in summer 1998 I went to army and I passed development of yagIRC
+ to two guys, they did a few new features and released a version or
+ two, but finally (in summer 1999?) they put a message to web page
+ which told that they finally had stopped developing it entirely,
+ also saying that my code was a total mess :) (yes, it was a mess)
+
+ I got out of the army 1.1.1999. I promised to myself that I wouldn't
+ do another IRC client, but after trying to use BitchX a while, I
+ started dreaming about an IRC client which would have an excellent
+ look and feel. After trying various things, I only came up with the
+ GNOME panel applet which people still tell me that it's a great
+ feature. I was more like thinking some pretty little icons in
+ some corner telling me about new messages and other stuff..
+
+ I thought that I would keep Irssi a small project, just doing a few
+ little features that *I* wanted, nothing for others. But after few
+ versions and few interested people, I started coding it more and
+ more generic..
+
+ Finally, after releasing version 0.6.0 (february, 1999) I realized
+ that things were getting into a big mess again. I started a rewrite,
+ I organized the code into irc-base, irc-extra, user interface and
+ GUI directories, created the signalling system for letting them
+ communicate themselves easily and released 0.7.0. This was the base
+ for the rest of the 0.7.x releases, and it did work pretty well.
+ The signalling system was excellent, for example creating text mode
+ version was really easy and you didn't need tens of (empty) gui_xxx()
+ functions like in the yagIRC days. Maintaining the text and GTK
+ versions separately was really easy too.
+
+ About a year later after releasing Irssi 0.7.0, I started having
+ dreams about an IRC client that would be extremely modular, like you
+ could upgrade the client to newer version ON THE FLY without needing
+ to even disconnect from the servers. I started a project codenamed
+ i2k, I took the code from Irssi, split it into more directories and
+ changed quite a lot of the code to work a bit differently.
+
+ I developed i2k quite a long, until I finally gave up with it since
+ it could do only some basic things, and Irssi 0.7 really needed
+ maintaining. After a while I got an idea, maybe I could merge the
+ code from the i2k to Irssi more easily than rewriting the whole
+ client. This was more easier than I thought. It's now been two
+ months since I started it, and Irssi 0.8 is looking absolutely
+ excellent.
+
+ 0.2 Irssi 0.8
+
+ Irssi 0.8 is my fourth try to create the perfect IRC client.
+ This time I'm concentrating to the code. I try to avoid kludges, I
+ try to make as simple code as I can, and I try to provide enough
+ easy to use functions so that extending Irssi is as simple as
+ possible. I also try to keep the "bloat" features in scripts or
+ modules instead of build-in.
+
+ I think I'm succeeded with these goals pretty well, there's some
+ small problems but everything in the big picture looks great.
+
+ 0.3 Future
+
+ What about Irssi 1.0, what will it look like?
+
+ I was thinking about the Linux kernel versioning and keeping
+ Irssi 0.8 a stable version all the time while developing new
+ features only to Irssi 0.9. After 0.9 is finished, it will be
+ called 0.10 or 1.0 depending if I think it's ready to be called 1.0.
+
+ 1.0's goal is that it has all the possible features anyone will
+ ever need. If not build-in, then in scripts or loadable modules.
+ Not very small goal :)
+
+ 0.4 This documentation
+
+ Strange, I just created the index list and started writing this.
+ I've never been too good at documentation and I usually don't like
+ writing it, but after coding so much recently and seeing that the
+ NEWS file was getting *SO* large, I thought that I had to put all
+ these features down somewhere so people (and me!) would find them.
+
+
+ 1. Installation
+
+ 1.1 Configuration
+
+ configure script accepts these parameters:
+
+ --enable-ipv6 Enable IPv6 support
+ --disable-curses-windows Don't use curses windows. Use this if
+ screen drawing seems too slow for some reason.
+ This option may not work properly with all
+ curseses (solaris8).
+ --enable-memdebug Enable memory debugging, great for finding
+ memory leaks
+
+ --enable-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-socks Build with socks library
+ --with-bot Build irssi-bot
+ --with-proxy Build irssi-proxy module
+ --without-textui Build without text frontend
+ --with-servertest Build test irc server which you can use to try
+ crash irc clients
+
+ In short:
+
+ ./configure
+ make
+ make install
+
+
+ 1.2 Command line parameters
+
+ --connect -c <server> Connect to server at startup
+ --port -p <port> - specify port
+ --noconnect -! Don't autoconnect to any servers at startup
+ --nick -n Specify what nick to use
+ --hostname -h Specify what host name to use
+
+
+ 2. Message levels
+
+
+ Message levels (or in short, levels) are used almost everywhere.
+ They describe what kind of messages we're dealing with. Here's a
+ list of them all:
+
+ CRAP - Can be almost anything
+ MSGS - Private messages
+ PUBLIC - Public messages in channel
+ NOTICES - Notices
+ SNOTES - Server notices
+ CTCPS - CTCP messages
+ ACTIONS - Actions (/me) - usually ORed with PUBLIC or MSGS
+ JOINS - Someone joins a channel
+ PARTS - Someone parts a channel
+ QUITS - Someone quits IRC
+ KICKS - Someone gets kicked from channel
+ MODES - Channel mode is changed
+ TOPICS - Channel topic is changed
+ WALLOPS - Wallop is received
+ INVITES - Invite is received
+ NICKS - Someone changes nick
+ DCC - DCC related messages
+ DCCMSGS - DCC chat messages
+ CLIENTNOTICES - Irssi's notices
+ CLIENTERRORS - Irssi's error messages
+ CLIENTCRAP - Some other messages from Irssi
+
+ 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
+
+
+ 3. Flood protection
+
+ 3.1 Command flood protection
+
+ Most (all?) IRC servers' flood protection works like this
+ (from RFC 1459):
+
+ --------
+ * check to see if client's `message timer' is less than
+ current time (set to be equal if it is);
+
+ * read any data present from the client;
+
+ * while the timer is less than ten seconds ahead of the current
+ time, parse any present messages and penalize the client by
+ 2 seconds for each message;
+
+ which in essence means that the client may send 1 message every 2
+ seconds without being adversely affected.
+ --------
+
+ Irssi's flood protection works the same way, except it penalizes
+ 2.2 seconds by default for each message (helps with some servers).
+ You can change it with /SET cmd_queue_speed <milliseconds>. You can
+ also change the number of commands before flood protection activates
+ (ie. the burst count) with /SET cmd_max_at_once <count>.
+
+ IRC servers also have an input buffer where the client's commands
+ are saved before processed. It's size is server specific (can be as
+ low as 1k!) If it gets full, the server kicks you out (the
+ "Excess flood" quit message). Irssi's flood protecion protects this
+ pretty well with small commands, but if you send many big commands
+ (like >400 char long messages) fast, you could get easily kicked out.
+ Normally this isn't problem, but if you have scripts sending long
+ messages, you should remember this. I'm not sure how much you should
+ wait between the long messages, but 2 seconds isn't enough.
+
+ This protection is used with all commands sent to server, so you
+ don't need to worry about it with your scripts.
+
+ 3.2 CTCP flood protection
+
+ Other people can pretty easily flood you with CTCP requests, and
+ even if you wouldn't get kicked out from the server, they could
+ easily grow your command queue. So, Irssi's CTCP flood protection
+ works like this:
+
+ First it checks how big the CTCP reply queue is, if it's longer
+ than `max_ctcp_queue', the CTCP is ignored. You can change it with
+ /SET max_ctcp_queue <count> (default is 5).
+
+ After this the CTCP reply is placed to server's "idle queue", so
+ the reply is sent "when there's extra time", this means that if
+ you are busy sending other commands, it might take a while before
+ the reply is sent.
+
+ 3.3 Detecting floods
+
+ Irssi is all the time automatically checking different flooding,
+ when flood is noticed, it sends "flood" signal. This can be easily
+ used for example to create a script for kicking channel flooders.
+ Autoignore uses this also, see section 10.2.
+
+ Flood is detected when more than `flood_max_msgs' same kind of
+ messages arrives in `flood_timecheck' seconds to same target
+ (channel or private msg) so it isn't flooding if same user sends a
+ message to 10 different channels you are on, but it is flooding if
+ 10 messages are sent to same channel by the same user.
+
+ Currently only messages, notices and ctcps are checked for
+ flooding.
+
+ /SET flood_max_msgs = <count>, default is 4
+ /SET flood_timecheck = <seconds>, default is 5 seconds
+ If either of these is 0, the flood checking is disabled.
+
+
+ 4. Configuration
+
+ 4.1 Configuration files
+
+ The configuration is saved to ~/.irssi/config file. You can edit
+ it with text editor if you want, you can also add comments to it
+ and they stay there even if /SAVE is used. Comments are the lines
+ starting with # character. Any errors in config file are displayed
+ at startup.
+
+ Irssi uses it's own config library for handling the config file.
+ The format is pretty much the same as in libPropList and should be
+ easily understandable.
+
+ You can reload the config file on the fly with /RELOAD command, you
+ can also read a different config file with /RELOAD <filename>.
+
+ If you change any settings, they aren't saved to file until you use
+ /SAVE. You can save the config file to different place with
+ /SAVE <filename>.
+
+ 4.2 Settings
+
+ You can view or change the settings with /SET command.
+
+ /SET without any arguments displays all the settings.
+ /SET <key> displays settings which key (partly) matches <key>
+ /SET <key> <value> sets <key> to <value>
+
+ Boolean settings accepts only values ON, OFF and TOGGLE. You can
+ also use /TOGGLE command to change them, so /TOGGLE <key> behaves
+ like /SET <key> TOGGLE. /TOGGLE also accepts arguments ON and OFF
+ when /TOGGLE behaves exactly like /SET.
+
+ Remember that changes are not saved until you use /SAVE!
+
+
+ 5. Servers
+
+ 5.1 Generic
+
+ Irssi is multi-server friendly. You can be connected to multiple
+ different servers, or the same server multiple times. Most of the
+ settings that let you specify the channel, let you also specify IRC
+ network.
+
+ Servers are referenced by a "server tag". If the server is known
+ to belong to some IRC network, the tag is the IRC network's name,
+ like "ircnet". If the IRC network is unknown, the tag is created
+ from the server's name, like irc.funet.fi -> funet. If the tag
+ already exists, a number is added to the end of it and raised until
+ unused tag is found.
+
+ Quit messages have a small problem if there's already a few
+ commands in server's input command queue. If the server's socket is
+ disconnected immediately after QUIT message is sent, it is possible
+ that the server didn't yet process the quit command and your quit
+ message will be "broken pipe" or something similiar. The right thing
+ to do is to let the server disconnect you, but what if the
+ connection to server is broken and the server never disconnects you?
+ I solved the problem by waiting a few seconds to see if the server
+ disconnects us. If it didn't, force the disconnect. This explains
+ the (a bit annoying) "waiting for servers to close connections"
+ message when quiting Irssi. Most IRC clients just ignore this whole
+ problem, but I hate it if my quit message isn't displayed right.
+
+ 5.2 IRC networks
+
+ Different IRC networks behave a bit differently, and to be as
+ efficient as possible, Irssi needs to know a few things about them
+ or the safe defaults will be used. The default configuration file
+ contains the settings for the biggest IRC networks.
+
+ /IRCNET ADD [-kicks <count>] [-msgs <count>] [-modes <count>]
+ [-whois <count>] [-cmdspeed <ms>] [-cmdmax <count>]
+ [-nick <nick>] [-user <user>] [-realname <name>]
+ [-host <host>] [-autosendcmd <cmd>] <name>
+
+ -kicks: Maximum number of nicks in one /KICK command
+ -msgs: Maximum number of nicks in one /MSG command
+ -modes: Maximum number of mode changes in one /MODE command
+ -whois: Maximum number of nicks in one /WHOIS command
+ -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1
+ -cmdmax: Same as /SET cmd_max_at_once, see section 3.1
+ -nick, -user, -realname: Specify what nick/user/name to use
+ -host: Specify what host name to use, if you have multiple
+ -autosendcmd: Command to send after connecting to a server
+
+ With -autosendcmd argument you can automatically run any commands
+ after connecting to ircnet. This is useful for automatically
+ identifying yourself to NickServ, for example
+
+ /IRCNET ADD -autosendcmd "/msg NickServ identify secret" OPN
+
+ /IRCNET REMOVE <name>
+
+ 5.3 Manually connecting and disconnecting
+
+ To connect to a new server, use:
+ /CONNECT [-ircnet <ircnet>] [-host <hostname>] <address>|<ircnet>
+ [<port> [<password> [<nick>]]]
+
+ If there's no password, set it to -. You can directly connect to
+ IRC server in specified address, or you can connect to some IRC
+ network and Irssi will pick the server for you.
+
+ You don't need to specify the IRC network, password, nick, etc. if
+ you setup the server using /SERVER ADD (see next section). If the
+ settings can't be found there either, Irssi will use the defaults:
+
+ /SET default_nick = <nick>, defaults to user_name
+ /SET alternate_nick = <nick>, defaults to <default_nick>_
+ /SET user_name = <user>, defaults to your login name
+ /SET real_name = <name>, taken from /etc/passwd by default
+ /SET hostname = <host>, what host name to use when connecting
+ /SET skip_motd ON|OFF|TOGGLE - Don't show server's MOTD
+
+ NOTE: /CONNECT is also a command for IRC operators to connect IRC
+ servers to other IRC servers. If you want to use it, use /SCONNECT
+ instead.
+
+ You can disconnect from the server with:
+ /DISCONNECT *|<tag> [message]
+
+ If message isn't given, Irssi will use the default quit message. You
+ can set it with /SET quit_message <message>, default is "leaving".
+
+ /SERVER disconnects the server in active window and connects to new
+ one. It will take the same arguments as /CONNECT. If you prefix the
+ address with + character, Irssi won't disconnect the active server,
+ and it will create a new window where the server is connected
+ (ie. /window new hide;/connect address)
+
+ /SERVER without any arguments displays list of connected servers.
+
+ 5.4 Server settings
+
+ /SERVER ADD [-auto | -noauto] [-ircnet <ircnet>] [-host <hostname>]
+ [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>]
+ <address> [<port> [<password>]]
+
+ -auto: Automatically connect to server at startup
+ -noauto: Don't connect to server at startup (default)
+ -ircnet: Specify what IRC network this server belongs to
+ -host: Specify what host name to use, if you have multiple
+ -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1
+ -cmdmax: Same as /SET cmd_max_at_once, see section 3.1
+ -port: This is pretty much like the port argument later, except
+ this can be used to modify existing server's port.
+
+ /SERVER REMOVE <address> [<port>]
+
+ /SERVER LIST
+
+ Servers are identified by their name and port. You can have multiple
+ entries for the same server name but in different ports. This is
+ useful for IRC proxies, in one port you could have IRCNet proxy,
+ another port would have EFNet, etc.
+
+ If you wish to change existing server's port to something else, use
+ -port command. For example if you had irc.server.org in port 6667
+ and you wanted to change it to port 6668, use command:
+
+ /SERVER ADD -port 6668 irc.server.org 6667
+
+ If you want to remove some settings from existing server, for
+ example hostname, just give -host "" parameters to it.
+
+ After connected to server, Irssi can automatically change your user
+ mode. You can set it with /SET usermode <mode>, default is +i.
+
+ 5.5 Automatic reconnecting
+
+ If you get disconnected from server, Irssi will try to reconnect
+ back to some of the servers in the same IRC network. To prevent
+ flooding the server that doesn't let you in (and avoiding K-lines),
+ Irssi won't try to reconnect to the same server more often than
+ once in `server_reconnect_time' seconds. You can change it with
+ /SET server_reconnect_time <seconds>, default is 5 minutes.
+
+ After reconnected to server, Irssi will re-set your user mode, away
+ message and will join you back to the same channels where you were
+ before the connection was lost.
+
+ You can see list of the reconnections with /SERVER. The servers
+ that have tag as RECON-n are the reconnections. You can remove them
+ with /DISCONNECT <tag>, and you can reconnect to them immediately
+ with /RECONNECT <n>. /RECONNECT without any arguments will
+ disconnect from the active server and reconnect back immediately.
+
+ 5.6 Command redirections
+
+ FIXME
+
+ 5.7 Server idle command queue
+
+ There's some situations when you want to ask something from the
+ server which isn't really important. For example when connected
+ to server and you didn't get your nick, Irssi asks with /WHOIS
+ who has your nick and displays it. But if you already have a lot of
+ commands in buffer, like you just autojoined to many channels,
+ you'd rather first let the JOIN commands to be sent to server
+
+ This is where server idle queue gets into picture. Commands in
+ idle queue are sent to server when there's nothing else in the
+ normal command queue.
+
+ Idle queue works with server redirections, so you can ask something
+ from server when it has time and your function is called when the
+ reply is received.
+
+ 5.8 Net splits
+
+ Irssi keeps track of people who were lost in net splits. You can
+ get a list of them with /NETSPLIT command.
+
+ Another use for this is with bots. Channel master can op anyone in
+ the channel and the bot happily accepts it. But if the opped user
+ is lost behind a net split and in netjoin the server gives ops for
+ the user, the bot should decide if the user (who isn't in bot's user
+ database) is a malicious attacker who should be deopped, or if
+ he/she/it is just some user that already had ops before the net
+ split.
+
+ /SET hide_netsplit_quits - If ON, hide all netsplit quit messages
+ and display only "Netsplit host1 host2: nicks".
+
+ /SET netsplit_max_nicks - If non-zero, limit the number of nicks
+ printed in netsplit message and add "(+<n> more, use /NETSPLIT
+ to show all of them)" text.
+
+ 5.9 Lag checking
+
+ Irssi will constantly check how big the lag to the server is. It's
+ done by sending IRSSILAG CTCP replies to ourself. Using PING command
+ for this would seem more reasonable, but there was too many problems
+ with it - some servers didn't even know the whole PING command!
+
+ If the lag is too big, Irssi will reconnect to different IRC server.
+ This is sometimes useful if the connection has been stuck for 30
+ minutes but it still hasn't been closed.
+
+ /SET lag_check_time <seconds> - Specifies how often to check the
+ lag. If it is set to 0, the lag detection is disabled. Default
+ is 30 seconds.
+ /SET lag_max_before_disconnect <seconds> - Specifies how big the lag
+ can be before reconnecting to another server. Default is 5
+ minutes.
+ /SET lag_min_show <100th seconds> - Specifies the minimum lag to
+ display in status bar. Default is 1 second.
+
+ 5.10 Raw log
+
+ All data that is received or sent to server is kept in a raw log
+ buffer for a while. Also event redirections are kept there. This is
+ very useful for debugging purposes.
+
+ /RAWLOG SAVE <filename> - Save the current raw log buffer to file
+ /RAWLOG OPEN <filename> - Like /RAWLOG SAVE, but keep the log file
+ open and write all new log to it.
+ /RAWLOG CLOSE - Close the open raw log
+
+ /SET rawlog_lines <count> - Specify the number of raw log lines to
+ keep in memory.
+
+
+ 6. Channels
+
+ 6.1 Generic
+
+ There's several types of channels you can join, here's a list of
+ the ones that Irssi supports:
+
+ #channel - Normal channels, most commonly used
+ +channel - Modeless channels, channel has no modes, no channel
+ operators and no topic. This way no-one is above others
+ and there's no operator-wars etc. But on the other hand,
+ you can't kick any troublemakers..
+ &channel - Local channels, these channels aren't distributed outside
+ the IRC server. IRCNet has replaced server notices with
+ several different &channels (&ERRORS, &NOTICES, etc.)
+ !channel - New channels, currently supported only by IRCNet. These
+ channels are designed so that they can't be taken over
+ with net splits. /JOIN !channel joins to existing
+ !channel, /JOIN !!channel creates a new channel.
+
+ Most of the commands that take channel name as parameter, can also
+ accept * as the channel name, which means the active channel.
+
+
+ 6.2 Joining, parting
+
+ Channels can be joined with /JOIN command. You can join to multiple
+ channels with one /JOIN by giving it a comma-separated list of
+ channels, like /JOIN #channel1,#channel2. If you don't give the
+ channel mode character (#+&!) before the channel name, Irssi
+ automatically uses # channels.
+
+ Channel name may contain any characters except SPACE, BELL, NUL,
+ CR, LF or comma (','). You can also restrict the channel to only
+ certain users by adding the hostmask to the end of the channel
+ name separated with a ':' character, like #channel:*!*@*.fi lets
+ only people from .fi domain join the channel. This doesn't work with
+ all IRC servers and it's pretty difficult to use, since everyone
+ will have to always join the #channel:*!*@*.fi channel, #channel or
+ #channel:*!*@*.se channels are different channels. Ban exceptions
+ (+e) and especially invite lists (+I) replace this functionality
+ pretty well, see section 6.5.
+
+ If channel has a password (aka. key), you can join it with
+ /JOIN #channel pass, or multiple channels with passwords with
+
+ /JOIN #secret1,#public,#secret2 pass1,x,pass2
+
+ #public didn't have any password, so we used "x" as it's password.
+ It doesn't really matter what password you send with channels that
+ don't have passwords.
+
+ If you want to join to channel in different server than active one
+ in window, you can do it with /JOIN -<server tag> #channel, like
+ /JOIN -efnet #irssi.
+
+ You can leave channels with /PART [<channels>] [<part message>].
+ For example "/PART byebye all" leaves the active channel with
+ "byebye all" message, or /PART #chan1,#chan2 leaves those channels.
+
+ NOTE: Sending JOIN 0 directly to server (/quote join 0) leaves all
+ the channels you are joined. There's been some jokes about joining
+ for example to #2000,0 where the server actually leaves you from all
+ channels. With Irssi this isn't really a problem, since irssi would
+ happily join to channels #2000 and #0.
+
+ 6.3 Automatic joining
+
+ Irssi can automatically join to specified channels in specified
+ IRC networks. It can also automatically send the password when
+ manually joining to channel without specifying the password.
+
+ /CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
+ <channel> <ircnet> [<password>]
+
+ With -bots and -botcmd arguments you can automatically send
+ commands to someone in channel. This is useful for automatically
+ getting ops for channels, for example
+
+ /CHANNEL ADD -auto -bots "*!bot@bothost.org bot*!*@host2.org"
+ -botcmd "msg $0 op mypass" #channel ircnet
+
+ You can also use the -botcmd without -bots argument. The command is
+ then sent whenever you join the channel.
+
+ If you want to remove some settings from existing channel record,
+ for example bots, just give the -bots "" parameters to it. Password
+ can be removed by setting it to - (or actually, "" works too).
+
+ You can remove the channels with
+ /CHANNEL REMOVE <channel> <ircnet>
+
+ /CHANNEL LIST displays list of channels with settings.
+ /CHANNEL without any arguments displays list of channels you have
+ joined. You can also use /CHANNEL to join to channels just as with
+ /JOIN, like /CHANNEL #a.
+
+ 6.4 After-join automation
+
+ When joined to channel, Irssi asks some information about it.
+ After it has got all of it, it prints the "Channel synchronized"
+ text. The following information is asked:
+
+ - Channel mode
+ - WHO list to get nicks' hosts - useful for /BAN for example
+ - Ban list - useful for allowing /UNBAN to use wildcards
+ - Exception list, Invite list - these are asked only from servers
+ that support +I and +e modes, mostly just IRCNet and some EFNet
+ servers. These aren't really needed for anything currenty, except
+ /INVITELIST and /BANS uses them to display the lists.
+
+ If you have joined many channels at once, Irssi tries to optimize
+ the commands it sends to server. Instead of sending two commands
+ to ask two channels' mode, it just sends MODE #a,#b. Same thing with
+ WHO list and ban/except/invite lists. Some servers do not support
+ this and they reply with different kinds of error messages, Irssi
+ tries to deal with them all right and resend the commands again
+ separately. However, some strange servers sometimes use some weird
+ error replies that Irssi doesn't know about, and the channel never
+ gets synchronized. If this happens with some server you know, please
+ let the Irssi's author know about it.
+
+ 6.5 Channel modes
+
+ Irssi knows these channel modes:
+
+ i - Invite only - People can't join to channel without being
+ /INVITEd, or being in invite list (+I, see below).
+ m - Moderated - People who don't have voices (+v) can't send
+ messages to channel
+ p - Private - People who aren't joined to channel can't see it
+ for example with /WHOISing people who are in channel.
+ s - Secret - Like private, but the channel isn't displayed in
+ /LIST's output.
+ n - No external msgs - Without this mode, anyone can send messages
+ to channel without even being joined.
+ t - Topic can be changed only by channel operators.
+
+ k <key> - Channel password (aka. key) - The channel can't be joined
+ without specifying the channel key (see section 6.2).
+
+ l <count> - User limit - No more than <count> people can join to
+ channel. This can be overridden with /INVITE with some
+ servers.
+
+ This is usually used for protecting channel from join
+ flooding, like some bot allows max. 5 users to join in
+ one minute or so.
+
+ a - Anonymous - No-one's nick name, host or anything else can be
+ seen. All messages, joins, parts, modes, etc. are seen as coming
+ from nick "anonymous", this could be pretty confusing but nice
+ feature if you want total anonymity. This mode can only be set,
+ never unset. This mode isn't supported by all servers.
+
+ NOTE: there is/was one bug :) Channel operators can guess if some
+ nick might be in the channel and try to kick it. If nick was in
+ channel, everyone will see the nick that was kicked.
+
+ r - Re-op - If channel becomes opless for longer than 45 (?) minutes,
+ op everyone in the channel. This works only in !channels. This
+ mode can only be set, not unset by channel creator.
+
+ b - Set/remove ban. For example MODE #channel +b *!*@*.org bans
+ everyone from .org domain.
+
+ If someone from .org domain was already in channel before the
+ ban was set, he/she couldn't be able to write any messages to
+ channel (doesn't work with all servers).
+
+ Ban can also be overridden with /INVITE, although many stupid
+ IRC clients automatically kick the user out because they see
+ the ban and think that because of it the user shouldn't be in
+ the channel (doesn't work with all servers).
+
+ e - Ban exceptions. You could for example ban everyone from
+ *!*@*.org but set ban exception to *!*@*.host.org - works only
+ in IRCnet/EFnet servers.
+
+ I - Invite list. If channel is invite only (+i), people in this
+ list can join it without being /INVITEd - works only in
+ IRCnet/EFnet servers.
+
+ This is excellent for in-country channels that don't want
+ foreigners (spammers!) to join the channel, for example setting
+ channel's mode to +i and +I *!*@*.fi allows only finnish people
+ to join the channel. In addition to this, there's usually a bot
+ in the channels and sending /MSG bot invite command to it
+ /INVITEs you to the channel.
+
+ The ':' feature in channel modes is quite similiar, see section
+ 6.2.
+
+ O - Channel owner, the nick who creates a !channel receives this
+ mode. It isn't displayed anywhere, you can't pass it to anyone
+ else and you can't regain it again. This is needed for setting
+ +r mode in channel when it's first created.
+
+ o <nick> - Grant or revoke channel operator status from nick
+ v <nick> - Grant or revoke voice status from nick, only people with
+ +v (or +o) can talk to channel when it's moderated (+m).
+
+ You can send multiple mode changes with one mode command:
+
+ /MODE #channel +nto-o+v nick1,nick2,nick3
+
+ This would set channel's mode to +nt, give ops to nick1, take ops
+ from nick2 and give voices to nick3.
+
+ You can set only limited number of modes that requires argument in
+ one command. In IRCnet it's 3, in EFnet it's 4 and in many others
+ it's 6. If it's not known, Irssi defaults to 3. Irssi will also
+ automatically split them, so you can use /MODE +oooooo n1,n2,..
+ command to op 6 people and Irssi will split it to two commands in
+ IRCnet/EFnet.
+
+ Instead of manually setting o, v and b modes you probably want to
+ use /OP, /DEOP, /VOICE, /DEVOICE, /BAN and /UNBAN commands.
+
+ /OP, /DEOP, /VOICE and /DEVOICE commands allows wildcards as their
+ argument. So /OP ni* will op all non-opped people whose nick start
+ with "ni". /DEOP * will deop everyone else except you. /VOICE and
+ /DEVOICE work the same way.
+
+ 6.6 Bans
+
+ You can give /BAN a list of nicks or whole ban masks. /UNBAN
+ accepts wildcards, so if you have ban nick!user@reallylonghost.org,
+ you can simply unban it with /UNBAN *really*
+
+ Using /BAN <nicks>, Irssi will automatically create the mask. You
+ can change the way it's created with /BANTYPE command:
+
+ /BANTYPE normal|host|domain|custom
+
+ Normal - *!user@*.domain.net
+ Host - *!*@host.domain.net
+ Domain - *!*@*.domain.net
+ Custom [nick] [user] [host] [domain]
+ eg. /bantype custom nick domain - nick!*@*.domain.net
+ eg. /bantype custom user host - *!user@host.domain.net
+
+ Irssi has also a couple of commands to help banning people:
+
+ /KICKBAN [<channel>] <nick> <reason> - ban and kick the nick
+ /KNOCKOUT [<seconds>] <nick> <reason> - kickban the nick, unban
+ after waiting <seconds>, default is 5 minutes.
+
+ 6.7 Massjoins
+
+ Automatic opping the nick right after joined to channel is a pretty
+ commonly used. What mostly irritates me with this is that the nick
+ may be opped multiple times by different people, or after netsplits
+ when the people join back, the server will op them, but still the
+ bots op the people again, even if it was just done by the server.
+
+ Irssi has this feature that it sends a "massjoin" signal a while
+ after the real join. If someone has already opped the nick, you can
+ easily check it in the massjoin signal handler.
+
+ The default is to report maximum of 5 joins in one massjoin signal.
+ If the 5 joins don't come in 5 seconds, the signal is sent anyway.
+ You can change these with /SET massjoin_max_wait <milliseconds> and
+ /SET massjoin_max_joins <count>.
+
+
+ 7. IRC commands and features (FIXME)
+
+ 7.x Basic commands
+
+ 7.x IRC operator commands
+
+ 7.x Away features
+
+ 8. Notify list
+
+ Notify list is generally used for knowing when someone you know
+ comes to IRC or leaves from IRC. Traditionally notify list can
+ handle only a list of nicks, no nick masks etc. I lost interest to
+ traditional notify lists long time ago, since the people I know
+ are in IRC all the time. So I made a bit more featureful notify
+ list:
+
+ /NOTIFY [-list] [-away] [-idle [minutes]] <mask> [ircnet [ircnet...]]
+
+ -away: Notifies about away-status changes
+ -idle: Notifies if idle time is first larger than <minutes>
+ (default is hour) and then it drops down.
+ -list: Lists the notify list entries with all their settings
+ <mask>: Either a simple "nick" or "nick!*@*blah.org". The nick
+ can't contain wildcards, but the user/host can.
+
+ /UNNOTIFY <mask>
+
+ /NOTIFY without any arguments displays if the people in notify
+ list are online or offline.
+
+
+ 9. Text highlighting
+
+ Irssi supports highlighting lines that match the specified pattern.
+ You can also change the color of the nicks that match specified nick
+ mask, so you could for example show your friends' nicks with
+ different color.
+
+ /HILIGHT [-mask | -regexp | -word] [-nick] [-color <color>]
+ [-level <level>] [-channels <channels>] <text>
+
+ -mask: Match only for nick, <text> is a nick mask
+ -regexp: <text> is a regular expression
+ -word: <text> must match to full words
+ -nick: Hilight only the nick, not the whole line
+ -color: Print the reply with <color> - see below
+ -level: Match only for <level> messages, default is
+ publics,msgs,notices,actions
+ -channels: Match only in <channels>
+
+ /DEHILIGHT <ref#> | <text>
+
+ /HILIGHT without any arguments displays list of the hilights.
+
+ By default the highlighted line will be printed with white color.
+ You can change this with the -color argument. If <color> is a
+ number, Irssi will treat it as a MIRC color code. You can also use
+ bolds (^B), underlines (^_) etc. as <color> if you like.
+
+
+ 10. Ignoring
+
+ 10.1 Manual ignoring
+
+ Irssi's ignoring options should be enough for everyone :)
+
+ /IGNORE [-regexp | -word] [-pattern <pattern>] [-replies] [-except]
+ [-channels <channel>] <mask> <levels> <^levels>
+
+ -regexp: <pattern> is a regular expression
+ -word: <pattern> must match to full words
+ -pattern: <pattern> must match to the message's text
+ -replies: Ignore replies to nick in channels. For example
+ "/IGNORE -replies *!*@*.fi PUBLIC" ignores everyone
+ from Finland, but also anyone sending message
+ "tofinnishnick: blahblah".
+ -except: *DON'T* ignore
+ -channels: Ignore only in channels
+ <mask>: Either a nick mask or list of channels
+ <levels>: List of levels to ignore
+ <^levels>: List of levels to NOT ignore
+ (/ignore -except nick notices = /ignore nick ^notices)
+
+ /UNIGNORE <ref#> | <mask>
+
+ /IGNORE without any arguments displays list of ignores.
+
+ The best match always wins, so you can have:
+
+ /IGNORE * CTCPS
+ /IGNORE -except *!*@host.org CTCPS
+
+ 10.2 Automatic ignoring
+
+ Irssi can automatically set ignores for people who flood you.
+ Currently you can autoignore MSGS, NOTICES, CTCPS and PUBLIC.
+ Actions are placed to either MSGS or PUBLIC. See section 3.3 for
+ definition of the flood.
+
+ /SET autoignore_time <seconds> specifies how long to ignore the
+ user.
+
+ /SET autoignore_levels <levels> specifies what levels to ignore
+ automatically, default is to ignore only CTCPS.
+
+
+ 11. Logging
+
+ 11.1 Basic logging
+
+ /LOG OPEN [-noopen] [-autoopen] [-targets <targets>]
+ [-window] <filename> [<levels>]
+
+ -noopen: Create the entry to log list, but don't start logging
+ -autoopen: Automatically open this log file at startup
+ -targets: Log only in specified channels/nicks
+ -window: Log the active window
+ <filename>: File name where to log, it is parsed with
+ strftime(), so %d=day, etc. see "man strftime" for
+ more info. Irssi will automatically check every hour
+ if log should be rotated.
+ <levels>: Defaults to ALL
+
+ /LOG CLOSE <ref#> | <fname> - Close log and remove from log list
+ /LOG START <ref#> | <fname> - Start logging to file
+ /LOG STOP <ref#> | <fname> - Stop logging to file
+ /LOG without any arguments displays the log list
+
+ /SET log_create_mode <mode> - Specifies what file mode to use with
+ the created log files. Default is 0644.
+
+ All of these are parsed with strftime():
+ /SET log_timestamp <text> - Specifies the time stamp format.
+ Default is "%H:%M ".
+ /SET log_open_string <text> - Text written to log when it's opened
+ /SET log_close_string <text> - Text written to log when it's closed
+ /SET log_day_changed <text> - Text written to log when day changes
+
+ NOTE: Log files are locked after opened, so two Irssis can't
+ accidentally try to write to the same log file.
+
+ Examples:
+
+ /LOG OPEN -targets cras ~/irclogs/cras.log MSGS
+ - Logs all messages from/to nick `cras'
+
+ /LOG OPEN -targets #linux ~/irclogs/linux/linux-%Y-%m-%d
+ - Logs all messages in channel #linux. Log is rotated daily, so
+ logs in 1. May 2000 goes to file "linux-2000-05-01", when the
+ day is changed, Irssi closes the log and starts logging to
+ "linux-2000-05-02" etc.
+
+ 11.2 Window logging
+
+ /WINDOW LOG ON|OFF|TOGGLE [<filename>]
+
+ Start/stop logging the active window. This works exactly like
+ /LOG OPEN -window.
+
+ /WINDOW LOGFILE <filename>
+
+ Sets the default log file to use in the window, it can be
+ overridden with specifying the file name in /WINDOW LOG. If no file
+ name isn't given, Irssi defaults to ~/irc.log.<windowname> or
+ ~/irc.log.Window<ref#> if window doesn't have name.
+
+ Creates the entry to log list, same as /LOG OPEN -window -noopen.
+ Also, if /WINDOW LOG ON is used it starts logging to this file.
+
+ 11.3 Automatic logging
+
+ This is the logging method that I had been asked to implement for
+ ages, and it is really simple to use too. It logs only messages
+ that have "targets", ie. private messages and channel specific
+ messages (msgs, modes, topics, etc). WHOIS replies and such aren't
+ logged. If you with to log them too, use the /LOG command.
+
+ So, when for example a private messages comes to you from "guy"
+ nick, Irssi creates a log file ~/irclogs/guy.log for it. After few
+ minutes of inactivity, the log file is closed.
+
+ /SET AUTOLOG ON|OFF|TOGGLE - Enable/disable autolog.
+
+ /SET AUTOLOG_LEVEL <level> - Specifies what levels to log, default
+ is ALL.
+
+ /SET AUTOLOG_PATH <path> - expandos (see special_vars.txt) can be
+ used, $0 is the target. If you are using multiple servers, it makes
+ sense to use the server tag as part of the file name, for example
+ ~/irclogs/$tag/$0.log (this is the default). The directories are
+ created automatically.
+
+ 11.4 Awaylog
+
+ Irssi logs specified messages when you're away. After you set
+ yourself unaway, Irssi will display all the messages in the awaylog.
+
+ /SET awaylog_level <level> - Default is MSGS HILIGHT
+ /SET awaylog_file <filename> - Default is ~/.irssi/away.log
+
+ You can disable this feature by setting awaylog_level to NONE.
+
+
+.. no, the docs end here, I got bored of writing these after a few days and
+haven't touched these since then.
--- /dev/null
+ <h2>Startup HOWTO</h2>
+
+ <h3>To new Irssi users (not to new IRC users ..)</h3>
+
+ <p>Copyright (c) 2000-2001 by Timo Sirainen</p>
+
+
+<p>Index with some FAQ questions that are answered in the chapter:</p>
+
+<ol>
+<li><a href="#c1">For all the lazy people</a></li>
+<li><a href="#c2">Basic user interface usage</a></li>
+<li><a href="#c3">Server and channel automation</a>
+ <ul>
+ <li>how do I automatically connect to servers at startup?</li>
+ <li>how do I automatically join to channels at startup?</li>
+ </ul></li>
+<li><a href="#c4">Setting up windows and automatically restoring them
+ at startup</a></li>
+<li><a href="#c5">Status and msgs windows & message levels</a>
+ <ul>
+ <li>I want /WHOIS to print reply to current window</li>
+ <li>I want all messages to go to one window, not create new windows</li>
+ </ul></li>
+<li><a href="#c6">How support for multiple servers works in irssi</a>
+ <ul>
+ <li>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??</li>
+ <li>I want to have own status and/or msgs window for each servers</li>
+ </ul></li>
+<li><a href="#c7">/LASTLOG and jumping around in scrollback</a>
+ <ul>
+ <li>How can I save all texts in a window to file?</li>
+ </ul></li>
+<li><a href="#c8">Logging</a></li>
+<li><a href="#c9">Irssi's settings</a></li>
+</ol>
+
+<h3><a id="c1">1. For all the lazy people</a></h3>
+
+<p>These settings should give you pretty good defaults (the ones I use):</p>
+
+<p>I don't like automatic query windows, I don't like status window, I do
+like msgs window where all messages go:</p>
+
+<pre>
+ /SET autocreate_own_query OFF
+ /SET autocreate_query_level DCCMSGS
+ /SET use_status_window OFF
+ /SET use_msgs_window ON
+</pre>
+
+<p>Disable automatic window closing when /PARTing channel or /UNQUERYing
+query:</p>
+
+<pre>
+ /SET autoclose_windows OFF
+ /SET reuse_unused_windows ON
+</pre>
+
+<p>And example how to add servers:</p>
+
+<p>(openprojects network, identify with nickserv and wait for 2 seconds before
+joining channels)</p>
+
+<pre>
+ /IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait -opn 2000" opn
+</pre>
+
+<p>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:</p>
+
+<pre>
+ /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
+</pre>
+
+<p>Automatically join to channels after connected to server, send op request
+to bot after joined to efnet/#irssi:</p>
+
+<pre>
+ /CHANNEL ADD -auto #irssi ircnet
+ /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+ #irssi efnet
+</pre>
+
+<h3><a id="c2">2. Basic user interface usage</a></h3>
+
+<p>By default, irssi uses "hidden windows" for everything. Hidden
+window is created every time you /JOIN a channel or /QUERY someone.
+There's several ways you can change between these windows:</p>
+
+<pre>
+ Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
+ Meta-q .. Meta-p - Jump directly between windows 11-20
+ /WINDOW <number> - Jump to any window with specified number
+ Ctrl-P, Ctrl-N - Jump to previous / next window
+</pre>
+
+<p>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):</p>
+
+<pre>
+ XTerm*eightBitInput: false
+ XTerm*metaSendsEscape: true
+</pre>
+
+<p>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:</p>
+
+<pre>
+ rxvt*modifier: alt
+</pre>
+
+<p>And how exactly do you set these X resources? For Debian, there's
+/etc/X11/Xresources/xterm file where you can put them and it's read
+automatically when X starts. ~/.Xresources and ~/.Xdefaults files might also
+work. If you can't get anything else to work, just copy&paste those lines to
+~/.Xresources and directly call "xrdb -merge ~/.Xresources" in some xterm.
+The resources affect only the new xterms you start, not existing ones.</p>
+
+<p>Many windows SSH clients also don't allow usage of ALT. One excellent
+client that does allow is putty, you can download it from
+<a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">
+http://www.chiark.greenend.org.uk/~sgtatham/putty/</a>.</p>
+
+<p>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:</p>
+
+<pre>
+ /WINDOW NEW - Create new split window
+ /WINDOW NEW HIDE - Create new hidden window
+ /WINDOW CLOSE - Close split or hidden window
+
+ /WINDOW HIDE [<number>|<name>] - Make the split window hidden window
+ /WINDOW SHOW <number>|<name> - Make the hidden window a split window
+
+ /WINDOW SHRINK [<lines>] - Shrink the split window
+ /WINDOW GROW [<lines>] - Grow the split window
+ /WINDOW BALANCE - Balance the sizes of all split windows
+</pre>
+
+<p>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:</p>
+
+<pre>
+ 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
+</pre>
+
+<p>When you are in win#1 and press ALT-6, irssi jumps to split window
+#3 and moves the efnet/#channel2 the active window.</p>
+
+<p>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</p>
+
+<pre>
+ /SET autostick_split_windows OFF
+</pre>
+
+<p>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</p>
+
+<pre>
+ /SET autocreate_windows OFF
+</pre>
+
+<p>If you want to group only some channels or queries in one window,
+use</p>
+
+<pre>
+ /JOIN -window #channel
+ /QUERY -window nick
+</pre>
+
+<h3><a id="c3">3. Server and channel automation</a></h3>
+
+<p>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.</p>
+
+<p>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.</p>
+
+<p>After that you need to add your servers. For example:</p>
+
+<pre>
+ /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+ /SERVER ADD -auto -ircnet worknet irc.mycompany.com 6667 password
+</pre>
+
+<p>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.</p>
+
+<p>And finally channels:</p>
+
+<pre>
+ /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+ #irssi efnet
+ /CHANNEL ADD -auto #secret ircnet password
+</pre>
+
+<p>-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.</p>
+
+
+<h3><a id="c4">4. Setting up windows and automatically restoring them at startup</a></h3>
+
+<p>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:</p>
+
+<pre>
+ /WINDOW MOVE LEFT/RIGHT/number - move window elsewhere
+ /WINDOW ITEM MOVE <number>|<name> - move channel/query to another window
+</pre>
+
+<p>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.</p>
+
+<p>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.</p>
+
+
+<h3><a id="c5">5. Status and msgs windows & message levels</a></h3>
+
+<p>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</p>
+
+<pre>
+ /SET use_status_window OFF
+</pre>
+
+<p>This doesn't have any effect until you restart irssi. If you want to
+remove it immediately, just /WINDOW CLOSE it.</p>
+
+<p>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:</p>
+
+<pre>
+ /SET use_msgs_window ON
+ /SET autocreate_query_level DCCMSGS (or if you don't want queries to
+ dcc chats either, say NONE)
+</pre>
+
+<p>use_msgs_window either doesn't have any effect until restarting
+irssi. To create it immediately say:</p>
+
+<pre>
+ /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
+</pre>
+
+<p>Note that neither use_msgs_window nor use_status_window have any
+effect at all if /LAYOUT SAVE has been used.</p>
+
+<p>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</p>
+
+<pre>
+ /HELP levels
+</pre>
+
+<p>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.</p>
+
+
+<h3><a id="c6">6. How support for multiple servers works in irssi</a></h3>
+
+<p>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:</p>
+
+<pre>
+ /CONNECT irc.server.org
+</pre>
+
+<p>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:</p>
+
+<pre>
+ -!- IRCNet: irc.telia.fi:6667 (IRCNet)
+ -!- OPN: tolkien.openprojects.net:6667 (OPN)
+ -!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
+</pre>
+
+<p>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.</p>
+
+<p>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.</p>
+
+<p>To disconnect one of the servers, or to stop irssi from
+reconnecting, use</p>
+
+<pre>
+ /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
+</pre>
+
+<p>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</p>
+
+<pre>
+ /WINDOW SERVER tag - set server "tag" active
+ Ctrl-X - set the next server in list active
+</pre>
+
+<p>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.</p>
+
+<p>Several commands also accept -servertag option to specify which server
+it should use:</p>
+
+<pre>
+ /MSG -tag nick message
+ /JOIN -tag #channel
+ /QUERY -tag nick
+</pre>
+
+<p>/MSG tab completion also automatically adds the -tag option when
+nick isn't in active server.</p>
+
+<p>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</p>
+
+<pre>
+ /WINDOW SERVER -sticky tag
+</pre>
+
+<p>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)</p>
+
+<pre>
+ /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
+</pre>
+
+<h3><a id="c7">7. /LASTLOG and jumping around in scrollback</a></h3>
+
+<p>/LASTLOG command can be used for searching texts in scrollback
+buffer. Simplest usages are</p>
+
+<pre>
+ /LASTLOG word - print all lines with "word" in them
+ /LASTLOG word 10 - print last 10 occurances of "word"
+ /LASTLOG -topics - print all topic changes
+</pre>
+
+<p>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</p>
+
+<pre>
+ /LASTLOG -file ~/irc.log
+</pre>
+
+<p>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.</p>
+
+<p>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.</p>
+
+
+<h3><a id="c8">8. Logging</a></h3>
+
+<p>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:</p>
+
+<pre>
+ /SET awaylog_level MSGS HILIGHT - Specifies what messages to log
+ /SET awaylog_file ~/.irssi/away.log - Specifies the file to use
+</pre>
+
+<p>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</p>
+
+<pre>
+ /SET autolog ON
+</pre>
+
+<p>By default it logs pretty much everything execept CTCPS or CRAP
+(/WHOIS requests, etc). You can specify the logging level yourself with</p>
+
+<pre>
+ /SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
+</pre>
+
+<p>By default irssi logs to ~/irclogs/<servertag>/<target>.log.
+You can change this with</p>
+
+<pre>
+ /SET autolog_path ~/irclogs/$tag/$0.log (this is the default)
+</pre>
+
+<p>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</p>
+
+<pre>
+ /SET autolog_path = ~/irclogs/%Y/$tag/$0.%m-%d.log
+</pre>
+
+<p>For logging only some specific channels or nicks, see /HELP log</p>
+
+
+<h3><a id="c9">9. Irssi's settings</a></h3>
+
+<p>You probably don't like Irssi's default settings. I don't like them.
+But I'm still convinced that they're pretty good defaults. Here's some
+of them you might want to change (the default value is shown):</p>
+
+<p><strong>Queries</strong></p>
+
+<dl>
+<dt>/SET autocreate_own_query ON</dt>
+ <dd>Should new query window be created when you send message to someone
+ (with /msg).</dd>
+
+<dt>/SET autocreate_query_level MSGS</dt>
+ <dd>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.</dd>
+
+<dt>/SET autoclose_query 0</dt>
+ <dd>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.</dd>
+</dl>
+
+<p><strong>Windows</strong></p>
+
+<dl>
+<dt>/SET use_msgs_window OFF</dt>
+ <dd>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).</dd>
+
+<dt>/SET use_status_window ON</dt>
+ <dd>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).</dd>
+
+<dt>/SET autocreate_windows ON</dt>
+ <dd>Should we create new windows for new window items or just place
+ everything in one window</dd>
+
+<dt>/SET autoclose_windows ON</dt>
+ <dd>Should window be automatically closed when the last item in them is
+ removed (ie. /PART, /UNQUERY).</dd>
+
+<dt>/SET reuse_unused_windows OFF</dt>
+ <dd>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.</dd>
+
+<dt>/SET window_auto_change OFF</dt>
+ <dd>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.</dd>
+
+<dt>/SET print_active_channel OFF</dt>
+ <dd>When you keep more than one channel in same window, Irssi prints
+ the messages coming to active channel as "<nick> text"
+ and other channels as "<nick:channel> text". If this
+ setting is set ON, the messages to active channels are also printed in
+ the latter way.</dd>
+
+<dt>/SET window_history OFF</dt>
+ <dd>Should command history be kept separate for each window.</dd>
+</dl>
+
+
+<p><strong>User information</strong></p>
+
+<dl>
+<dt>/SET nick</dt>
+ <dd>Your nick name</dd>
+
+<dt>/SET alternate_nick</dt>
+ <dd>Your alternate nick.</dd>
+
+<dt>/SET user_name</dt>
+ <dd>Your username, if you have ident enabled this doesn't affect
+ anything</dd>
+
+<dt>/SET real_name</dt>
+ <dd>Your real name.</dd>
+</dl>
+
+
+<p><strong>Server information</strong></p>
+
+<dl>
+<dt>/SET skip_motd OFF</dt>
+ <dd>Should we hide server's MOTD (Message Of The Day).</dd>
+
+<dt>/SET server_reconnect_time 300</dt>
+ <dd>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.</dd>
+
+<dt>/SET lag_max_before_disconnect 300</dt>
+ <dd>Maximum server lag in seconds before disconnecting and trying to
+ reconnect. This happens mostly only when network breaks between you and
+ IRC server.</dd>
+</dl>
+
+
+<p><strong>Appearance</strong></p>
+
+<dl>
+<dt>/SET timestamps ON</dt>
+ <dd>Show timestamps before each message.</dd>
+
+<dt>/SET hide_text_style OFF</dt>
+ <dd>Hide all bolds, underlines, MIRC colors, etc.</dd>
+
+<dt>/SET show_nickmode ON</dt>
+ <dd>Show the nick's mode before nick in channels, ie. ops have
+ <@nick>, voices <+nick> and others < nick></dd>
+
+<dt>/SET show_quit_once OFF</dt>
+ <dd>Show quit message only once in some of the channel windows the
+ nick was in instead of in all windows.</dd>
+
+<dt>/SET topicbar ON</dt>
+ <dd>Show the channel's topic in top of screen.</dd>
+
+<dt>/SET lag_min_show 100</dt>
+ <dd>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).</dd>
+
+<dt>/SET indent 10</dt>
+ <dd>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.</dd>
+
+<dt>/SET activity_hide_targets</dt>
+ <dd>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.</dd>
+
+<dt>/SET mail_counter ON</dt>
+ <dd>Show the number of mails in your mbox in status
+ bar. The mbox file is taken from $MAIL environment setting. Only mbox
+ format works for now.</dd>
+
+
+<p><strong>Nick completion</strong></p>
+
+<dl>
+<dt>/SET completion_auto OFF</dt>
+ <dd>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 ;)</dd>
+
+<dt>/SET completion_char :</dt>
+ <dd>Completion character to use.</dd>
+</dl>
--- /dev/null
+#!/bin/sh
+
+echo "const char *$2 ="
+cat $1|sed 's/\\/\\\\/g'|sed 's/"/\\"/g'|sed 's/^/\"/'|sed 's/$/\\n\"/'
+echo ";"
--- /dev/null
+#!/usr/bin/perl -w
+
+while(<>) {
+ if(m!/\*.SYNTAX\:! || $tt) {
+ s/^\s+/ /;
+ if (/^ [A-Z]+/) {
+ print "\n";
+ s/^ //;
+ }
+ if (m!\*/!) {
+ $tt=0;
+ } else {
+ $tt=1;
+ chomp;
+ }
+ print;
+ }
+}
--- /dev/null
+PROG_LIBS="@PROG_LIBS@"
+PERL_LDFLAGS="@PERL_LDFLAGS@"
+COMMON_LIBS="@COMMON_LIBS@"
+
+CHAT_MODULES="@CHAT_MODULES@"
+silc_MODULES="@silc_MODULES@"
--- /dev/null
+/* automatically created by autogen.sh */
+#define IRSSI_VERSION "@VERSION@"
+#define IRSSI_VERSION_DATE "20010524"
--- /dev/null
+# $Revision$, $Date$
+Name: irssi
+Version: @VERSION@
+Release: 1
+Vendor: Timo Sirainen <tss@iki.fi>
+Summary: Irssi is a IRC client
+Summary(pl): Irssi - klient IRC
+Copyright: GPL
+Group: Applications/Communications
+Group(pl): Aplikacje/Komunikacja
+URL: http://irssi.org/
+Source0: http://irssi.org/irssi/files/%{name}-%{version}.tar.gz
+BuildRequires: glib-devel
+BuildRequires: ncurses-devel
+BuildRequires: imlib-devel
+BuildRequires: gtk+-devel
+BuildRequires: gnome-libs-devel
+BuildRequires: XFree86-devel
+BuildRoot: /tmp/%{name}-%{version}-root
+
+%define _sysconfdir /etc
+%define configure { CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ; CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ; FFLAGS="${FFLAGS:-%optflags}" ; export FFLAGS ; ./configure %{_target_platform} --prefix=%{_prefix} --exec-prefix=%{_exec_prefix} --bindir=%{_bindir} --sbindir=%{_sbindir} --sysconfdir=%{_sysconfdir} --datadir=%{_datadir} --includedir=%{_includedir} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --localstatedir=%{_localstatedir} --sharedstatedir=%{_sharedstatedir} --mandir=%{_mandir} --infodir=%{_infodir} }
+
+%description
+Irssi is a textUI IRC client with IPv6 support
+by Timo Sirainen <tss@iki.fi>.
+More information can be found at http://irssi.org/.
+
+%description -l pl
+Irssi jest tekstowym klientem IRC ze wsparciem dla IPv6.
+Napisany zosta³ przez Timo Strainen <tss@iki.fi>
+Wiêcej informacji mo¿na znale¶æ pod adresem
+http://irssi.org/
+
+%package GNOME
+Summary: GNOME version of irssi IRC client
+Summary(pl): Wersja dla ¶rodowiska GNOME klienta IRC irssi
+Group: X11/Applications/Communications
+Group(pl): X11/Aplikacje/Komunikacja
+Requires: %{name} = %{version}
+
+%description GNOME
+Irssi is a GTK based (with GNOME) GUI IRC client with IPv6 support
+by Timo Sirainen <tss@iki.fi>.
+More information can be found at http://irssi.org/.
+
+%description GNOME -l pl
+Irssi jest graficznym klientem IRC ze wsparciem dla IPv6 pracuj±cym
+w ¶rodowisku GNOME. Napisany zosta³ przez Timo Sirainen <tss@iki.fi>.
+Wiêcej informacji mo¿na znale¶æ pod adresem
+http://irssi.org/
+
+%prep
+%setup -q
+
+%build
+CPPFLAGS="-I/usr/X11R6/include"; export CPPFLAGS
+LDFLAGS="-s -L/usr/X11R6/lib"; export LDFLAGS
+%configure \
+ --with-gnome \
+ --with-gnome-panel \
+ --with-imlib \
+ --enable-ipv6 \
+ --with-textui=ncurses \
+ --without-socks \
+ --with-plugins
+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
+
+%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
+
+%dir %{_sysconfdir}/irssi
+%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/irssi/*
+
+%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
+
+%files GNOME
+%defattr (644,root,root,755)
+%attr(755,root,root) %{_bindir}/irssi
+
+%{_sysconfdir}/CORBA/servers/irssi.gnorba
+%{_datadir}/gnome/apps/Network/irssi.desktop
+%{_datadir}/gnome/help/irssi
+%{_datadir}/pixmaps/*
+
+%define date %(echo `LC_ALL="C" date +"%a %b %d %Y"`)
+%changelog
+* %{date} PLD Team <pld-list@pld.org.pl>
+All below listed persons can be reached on <cvs_login>@pld.org.pl
+
+$Log$
+Revision 1.1.1.1 2001/05/24 12:09:28 priikone
+ imported irssi.
+
+Revision 1.11 2001/03/29 14:38:28 cras
+http://irssi.org -> http://irssi.org/
+
+Revision 1.10 2000/06/02 01:55:01 cras
+Changed irssi's url to http://irssi.org/
+
+Revision 1.9 2000/04/17 09:37:05 kloczek
+- added pixmaps to %files (irssi have now own icon).
+
+Revision 1.8 2000/04/14 11:27:02 cras
+Sorry for a big update - I still don't have internet connection at home
+and this is what I've been doing a few weeks now.. :) You really shouldn't
+upgrade to this version without keeping a backup of the working one, since
+this will break everything and at least notify list is broken - probably
+something else too.
+
+* On the way to 0.8.0 .. Major rewriting/rearranging code. There's
+ some changes in behaviour because I'm trying to make Irssi a bit
+ more compatible with EPIC.
+
+* libPropList isn't needed anymore - I'm using my own configuration
+ library. This is mostly because different proplists worked a bit
+ differently everywhere and several people had problems with it.
+ It's also yet another extra library that you needed to compile
+ Irssi. New configuration library has several advantages:
+
+ You can add comments to configuration file and they also stay
+ there when it's saved.
+
+ It's not nearly as vulnerable as proplist. If some error occurs,
+ instead of just not reading anything it will try to continue if
+ possible. Also the error messages are written to irssi's text
+ window instead of stdout.
+
+ It can be managed more easily than proplist - setting/getting the
+ configuration is a lot more easier.
+
+* Coding style changes - I'm not using gint, gchar etc. anymore,
+ they're just extra pain when moving code to non-glib projects and
+ syntax hilighting doesn't work by default with most editors ;)
+
+ Indentation style was also changed to K&R because of some political
+ reasons ;) And I'm already starting to like it.. :) It forces me
+ to split code to different functions more often and the result is
+ that the code gets more readable.
+
+ And finally I'm also using nst' all over the place.
+
++ /EVAL <commands> - Expand all the special variables from string and
+ run it. Commands can be split with ; character. See
+ docs/SPECIAL_VARS for more info.
++ Aliases are parsed just like /EVAL - arguments are in $0..$9.
++ Text formats are also parsed like /EVAL, arguments used to be in
+ $1..$9, now they're in $0..$8 so it messes up existing themes..
++ /SET [key [value]] - no more the '=' character. Boolean values
+ also need to be changed with ON/OFF/TOGGLE values (not yes/no).
+ Settings aren't saved to disk until you use /SAVE.
++ /TOGGLE <key> [ON/OFF] - same as /SET <key> TOGGLE
+
+Revision 1.7 2000/02/25 17:03:15 cras
+Irssi 0.7.27 released.
+
+Revision 1.6 2000/01/27 19:03:28 cras
+fixes by vkoivula@saunalahti.fi
+
+Revision 1.6 2000/01/25 00:00:00 vkoivula@saunalahti.fi
+- requires libProbList-devel changed to libPropList (problist.h is actually in
+ this package)
+- fixed filelist
+
+Revision 1.5 2000/01/13 02:13:20 kloczek
+- irssi.desktop now this is not applet but application description file -
+ place them in $(datadir)/gnome/apps/Network.
+
+Revision 1.4 1999/10/16 13:05:25 wiget
+- polish translation
+
+Revision 1.3 1999/09/13 16:50:25 wiget
+- fixed %%configure macro
+
+Revision 1.2 1999/09/04 11:42:33 wiget
+- new way to update Version: field in spec
+- new target for make 'make rpm'
+
+Revision 1.4 1999/09/03 09:36:24 wiget
+- updated to 0.7.16alpha-1
+
+Revision 1.3 1999/09/02 17:27:36 wiget
+- added BuildRequires rules
+
+Revision 1.2 1999/09/02 17:22:51 wiget
+- rewrite to PLD style coding:
+-- correct Group and Group(pl)
+-- %%changelog moved to end
+-- splited to irssi and irssi-GNOME
+-- added patch to allow 'make install DESTDIR=/some/dir'
+-- added ./configure parameters
+-- striped unneeded symbol from plugins
+-- gziped docs
+-- corrected %%files section
+
+- based at spec from tarball (by JT Traub <jtraub@dragoncat.net>)
--- /dev/null
+if BUILD_TEXTUI
+TEXTUI=fe-text
+endif
+
+noinst_HEADERS = \
+ common.h
+
+SUBDIRS = lib-popt lib-config core silc fe-common $(TEXTUI)
--- /dev/null
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#define IRSSI_AUTHOR "Timo Sirainen <tss@iki.fi>"
+#define IRSSI_WEBSITE "http://irssi.org/"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <ctype.h>
+# ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#include <errno.h>
+#include <time.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#include <sys/stat.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+#include <fcntl.h>
+#ifdef WIN32
+# include <win32-compat.h>
+#endif
+
+#include <glib.h>
+#ifdef HAVE_GMODULE
+# include <gmodule.h>
+#endif
+
+#include "core/memdebug.h"
+
+#define g_free_not_null(a) \
+ G_STMT_START { \
+ if (a) g_free(a); \
+ } G_STMT_END
+
+#define g_free_and_null(a) \
+ G_STMT_START { \
+ if (a) { g_free(a); (a) = NULL; } \
+ } G_STMT_END
+
+#define G_INPUT_READ (1 << 0)
+#define G_INPUT_WRITE (1 << 1)
+
+typedef void (*GInputFunction) (void *data, GIOChannel *source, int condition);
+
+int g_input_add(GIOChannel *source, int condition,
+ GInputFunction function, void *data);
+int g_input_add_full(GIOChannel *source, int priority, int condition,
+ GInputFunction function, void *data);
+
+#define MAX_INT_STRLEN ((sizeof(int) * CHAR_BIT + 2) / 3 + 1)
+
+typedef struct _IPADDR IPADDR;
+typedef struct _CONFIG_REC CONFIG_REC;
+typedef struct _CONFIG_NODE CONFIG_NODE;
+
+typedef struct _LINEBUF_REC LINEBUF_REC;
+typedef struct _NET_SENDBUF_REC NET_SENDBUF_REC;
+typedef struct _RAWLOG_REC RAWLOG_REC;
+
+typedef struct _CHATNET_REC CHATNET_REC;
+typedef struct _SERVER_REC SERVER_REC;
+typedef struct _WI_ITEM_REC WI_ITEM_REC;
+typedef struct _CHANNEL_REC CHANNEL_REC;
+typedef struct _QUERY_REC QUERY_REC;
+typedef struct _NICK_REC NICK_REC;
+
+typedef struct _SERVER_CONNECT_REC SERVER_CONNECT_REC;
+typedef struct _SERVER_SETUP_REC SERVER_SETUP_REC;
+typedef struct _CHANNEL_SETUP_REC CHANNEL_SETUP_REC;
+
+#endif
--- /dev/null
+SUBDIRS = core silc
--- /dev/null
+INCLUDES = $(GLIB_CFLAGS) -I$(IRSSI_INCLUDE) -I$(IRSSI_INCLUDE)/src
+
+SILC_INCLUDE=../../../..
+IRSSI_INCLUDE=../../..
+
+INCLUDES = \
+ $(GLIB_CFLAGS) \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -I$(IRSSI_INCLUDE) -I$(IRSSI_INCLUDE)/src \
+ -I$(IRSSI_INCLUDE)/src/core \
+ -I$(IRSSI_INCLUDE)/src/silc \
+ -I$(IRSSI_INCLUDE)/src/silc/core \
+ -I$(SILC_INCLUDE)/includes \
+ -I$(SILC_INCLUDE)/lib/silccore \
+ -I$(SILC_INCLUDE)/lib/silccrypt \
+ -I$(SILC_INCLUDE)/lib/silcmath \
+ -I$(SILC_INCLUDE)/lib/silcske \
+ -I$(SILC_INCLUDE)/lib/silcsim \
+ -I$(SILC_INCLUDE)/lib/silcutil \
+ -I$(SILC_INCLUDE)/lib/silcclient \
+ -I$(SILC_INCLUDE)/lib/silcmath/gmp \
+ -I$(SILC_INCLUDE)/lib/trq
+
+noinst_LIBRARIES = libfe_common_silc.a
+
+libfe_common_silc_a_SOURCES = \
+ fe-channels.c \
+ fe-common-silc.c \
+ silc-modules.c
+
+noinst_HEADERS = \
+ fe-common-silc.h \
+ module.h
--- /dev/null
+/*
+ fe-channels.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
+*/
+
+#include "module.h"
+
+void fe_silc_channels_init(void)
+{
+}
+
+void fe_silc_channels_deinit(void)
+{
+}
--- /dev/null
+/*
+ fe-common-silc.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
+*/
+
+#include "module.h"
+#include "signals.h"
+
+void fe_silc_channels_init(void);
+void fe_silc_channels_deinit(void);
+
+void fe_silc_modules_init(void);
+void fe_silc_modules_deinit(void);
+
+void fe_common_silc_init(void)
+{
+ fe_silc_channels_init();
+ fe_silc_modules_init();
+}
+
+void fe_common_silc_deinit(void)
+{
+ fe_silc_modules_deinit();
+
+ fe_silc_channels_deinit();
+}
+
+void fe_common_silc_finish_init(void)
+{
+}
--- /dev/null
+#ifndef __FE_COMMON_SILC_H
+#define __FE_COMMON_SILC_H
+
+void fe_common_silc_init(void);
+void fe_common_silc_deinit(void);
+void fe_common_silc_finish_init(void);
+
+#endif
--- /dev/null
+/*
+ module-formats.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
+*/
+
+#include "module.h"
+#include "printtext.h"
+
+FORMAT_REC fecommon_irc_formats[] = {
+ { MODULE_NAME, "IRC", 0 },
+
+ /* ---- */
+ { NULL, "Server", 0 },
+
+ { "lag_disconnected", "No PONG reply from server %_$0%_ in $1 seconds, disconnecting", 2, { 0, 1 } },
+ { "disconnected", "Disconnected from %_$0%_ %K[%n$1%K]", 2, { 0, 0 } },
+ { "server_list", "%_$0%_: $1:$2 ($3)", 5, { 0, 0, 1, 0, 0 } },
+ { "server_lookup_list", "%_$0%_: $1:$2 ($3) (connecting...)", 5, { 0, 0, 1, 0, 0 } },
+ { "server_reconnect_list", "%_$0%_: $1:$2 ($3) ($5 left before reconnecting)", 6, { 0, 0, 1, 0, 0, 0 } },
+ { "server_reconnect_removed", "Removed reconnection to server %_$0%_ port %_$1%_", 3, { 0, 1, 0 } },
+ { "server_reconnect_not_found", "Reconnection tag %_$0%_ not found", 1, { 0 } },
+ { "query_server_changed", "Query with %_$2%_ changed to server %_$1%_", 3, { 0, 0, 0 } },
+ { "setupserver_added", "Server $0 saved", 2, { 0, 1 } },
+ { "setupserver_removed", "Server $0 removed", 2, { 0, 1 } },
+ { "setupserver_not_found", "Server $0 not found", 2, { 0, 1 } },
+ { "setupserver_header", "Server Port IRC Net Settings", 0 },
+ { "setupserver_line", "%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } },
+ { "setupserver_footer", "", 0 },
+ { "netsplit", "%RNetsplit%n %_$0%_ %_$1%_ quits: $2", 3, { 0, 0, 0 } },
+ { "netsplit_more", "%RNetsplit%n %_$0%_ %_$1%_ quits: $2 (+$3 more, use /NETSPLIT to show all of them)", 4, { 0, 0, 0, 1 } },
+ { "netsplit_join", "%CNetsplit%n over, joins: $0", 1, { 0 } },
+ { "netsplit_join_more", "%CNetsplit%n over, joins: $0 (+$1 more)", 2, { 0, 1 } },
+ { "no_netsplits", "There are no net splits", 0 },
+ { "netsplits_header", "Nick Channel Server Splitted server", 0 },
+ { "netsplits_line", "$[9]0 $[10]1 $[20]2 $3", 4, { 0, 0, 0, 0 } },
+ { "netsplits_footer", "", 0 },
+ { "ircnet_added", "Ircnet $0 saved", 1, { 0 } },
+ { "ircnet_removed", "Ircnet $0 removed", 1, { 0 } },
+ { "ircnet_not_found", "Ircnet $0 not found", 1, { 0 } },
+ { "ircnet_header", "Ircnets:", 0 },
+ { "ircnet_line", "$0: $1", 2, { 0, 0 } },
+ { "ircnet_footer", "", 0 },
+
+ /* ---- */
+ { NULL, "Channels", 0 },
+
+ { "join", "%c%_$0%_ %K[%c$1%K]%n has joined %_$2", 3, { 0, 0, 0 } },
+ { "part", "%c$0 %K[%n$1%K]%n has left %_$2%_ %K[%n$3%K]", 4, { 0, 0, 0, 0 } },
+ { "joinerror_toomany", "Cannot join to channel %_$0%_ %K(%nYou have joined to too many channels%K)", 1, { 0 } },
+ { "joinerror_full", "Cannot join to channel %_$0%_ %K(%nChannel is full%K)", 1, { 0 } },
+ { "joinerror_invite", "Cannot join to channel %_$0%_ %K(%nYou must be invited%K)", 1, { 0 } },
+ { "joinerror_banned", "Cannot join to channel %_$0%_ %K(%nYou are banned%K)", 1, { 0 } },
+ { "joinerror_bad_key", "Cannot join to channel %_$0%_ %K(%nBad channel key%K)", 1, { 0 } },
+ { "joinerror_bad_mask", "Cannot join to channel %_$0%_ %K(%nBad channel mask%K)", 1, { 0 } },
+ { "joinerror_unavail", "Cannot join to channel %_$0%_ %K(%nChannel is temporarily unavailable%K)", 1, { 0 } },
+ { "kick", "%c$0%n was kicked from %_$1%_ by %_$2%_ %K[%n$3%K]", 4, { 0, 0, 0, 0 } },
+ { "quit", "%c$0 %K[%n$1%K]%n has quit IRC %K[%n$2%K]", 3, { 0, 0, 0 } },
+ { "quit_once", "%_$3%_ %c$0 %K[%n$1%K]%n has quit IRC %K[%n$2%K]", 4, { 0, 0, 0, 0 } },
+ { "invite", "%_$0%_ invites you to %_$1", 2, { 0, 0 } },
+ { "inviting", "Inviting $0 to %_$1", 2, { 0, 0 } },
+ { "not_invited", "You have not been invited to a channel!", 0 },
+ { "names", "%K[%g%_Users%_%K(%g$0%K)]%n $1", 2, { 0, 0 } },
+ { "names_nick", "%K[%n%_$0%_$1%K] ", 2, { 0, 0 } },
+ { "endofnames", "%g%_$0%_%K:%n Total of %_$1%_ nicks %K[%n%_$2%_ ops, %_$3%_ voices, %_$4%_ normal%K]", 5, { 0, 1, 1, 1, 1 } },
+ { "channel_created", "Channel %_$0%_ created $1", 2, { 0, 0 } },
+ { "topic", "Topic for %c$0%K:%n $1", 2, { 0, 0 } },
+ { "no_topic", "No topic set for %c$0", 1, { 0 } },
+ { "new_topic", "%_$0%_ changed the topic of %c$1%n to%K:%n $2", 3, { 0, 0, 0 } },
+ { "topic_unset", "Topic unset by %_$0%_ on %c$1", 2, { 0, 0 } },
+ { "topic_info", "Topic set by %_$0%_ %K[%n$1%K]", 2, { 0, 0 } },
+ { "chanmode_change", "mode/%c$0 %K[%n$1%K]%n by %_$2", 3, { 0, 0, 0 } },
+ { "server_chanmode_change", "%RServerMode/%c$0 %K[%n$1%K]%n by %_$2", 3, { 0, 0, 0 } },
+ { "channel_mode", "mode/%c$0 %K[%n$1%K]", 2, { 0, 0 } },
+ { "bantype", "Ban type changed to %_$0", 1, { 0 } },
+ { "no_bans", "No bans in channel %_$0%_", 1, { 0 } },
+ { "banlist", "%_$0%_: ban %c$1", 2, { 0, 0 } },
+ { "banlist_long", "%_$0%_: ban %c$1 %K[%nby %_$2%_, $3 secs ago%K]", 4, { 0, 0, 0, 1 } },
+ { "ebanlist", "%_$0%_: ban exception %c$1", 2, { 0, 0 } },
+ { "ebanlist_long", "%_$0%_: ban exception %c$1 %K[%nby %_$2%_, $3 secs ago%K]", 4, { 0, 0, 0, 1 } },
+ { "invitelist", "%_$0%_: invite %c$1", 2, { 0, 0 } },
+ { "no_such_channel", "$0: No such channel", 1, { 0 } },
+ { "channel_synced", "Join to %_$0%_ was synced in %_$1%_ secs", 2, { 0, 2 } },
+ { "not_in_channels", "You are not on any channels", 0 },
+ { "current_channel", "Current channel $0", 1, { 0 } },
+ { "chanlist_header", "You are on the following channels:", 0 },
+ { "chanlist_line", "$[-10]0 %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } },
+ { "chansetup_not_found", "Channel $0 not found", 2, { 0, 0 } },
+ { "chansetup_added", "Channel $0 saved", 2, { 0, 0 } },
+ { "chansetup_removed", "Channel $0 removed", 2, { 0, 0 } },
+ { "chansetup_header", "Channel IRC net Password Settings", 0 },
+ { "chansetup_line", "$[15]0 %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } },
+ { "chansetup_footer", "", 0 },
+
+ /* ---- */
+ { NULL, "Nick", 0 },
+
+ { "usermode_change", "Mode change %K[%n%_$0%_%K]%n for user %c$1", 2, { 0, 0 } },
+ { "user_mode", "Your user mode is %K[%n%_$0%_%K]", 1, { 0 } },
+ { "away", "You have been marked as being away", 0 },
+ { "unaway", "You are no longer marked as being away", 0 },
+ { "nick_away", "$0 is away: $1", 2, { 0, 0 } },
+ { "no_such_nick", "$0: No such nick/channel", 1, { 0 } },
+ { "your_nick", "Your nickname is $0", 1, { 0 } },
+ { "your_nick_changed", "You're now known as %c$0", 1, { 0 } },
+ { "nick_changed", "%_$0%_ is now known as %c$1", 2, { 0, 0 } },
+ { "nick_in_use", "Nick %_$0%_ is already in use", 1, { 0 } },
+ { "nick_unavailable", "Nick %_$0%_ is temporarily unavailable", 1, { 0 } },
+ { "your_nick_owned", "Your nick is owned by %_$3%_ %K[%n$1@$2%K]", 4, { 0, 0, 0, 0 } },
+
+ /* ---- */
+ { NULL, "Who queries", 0 },
+
+ { "whois", "%_$0%_ %K[%n$1@$2%K]%n%: ircname : $3", 4, { 0, 0, 0, 0 } },
+ { "whowas", "%_$0%_ %K[%n$1@$2%K]%n%: ircname : $3", 4, { 0, 0, 0, 0 } },
+ { "whois_idle", " idle : $1 days $2 hours $3 mins $4 secs", 5, { 0, 1, 1, 1, 1 } },
+ { "whois_idle_signon", " idle : $1 days $2 hours $3 mins $4 secs %K[%nsignon: $5%K]", 6, { 0, 1, 1, 1, 1, 0 } },
+ { "whois_server", " server : $1 %K[%n$2%K]", 3, { 0, 0, 0 } },
+ { "whois_oper", " : %_IRC operator%_", 1, { 0 } },
+ { "whois_registered", " : has registered this nick", 1, { 0 } },
+ { "whois_channels", " channels : $1", 2, { 0, 0 } },
+ { "whois_away", " away : $1", 2, { 0, 0 } },
+ { "end_of_whois", "End of WHOIS", 1, { 0 } },
+ { "end_of_whowas", "End of WHOWAS", 1, { 0 } },
+ { "whois_not_found", "There is no such nick $0", 1, { 0 } },
+ { "who", "$[-10]0 %|%_$[!9]1%_ $[!3]2 $[!2]3 $4@$5 %K(%W$6%K)", 7, { 0, 0, 0, 0, 0, 0, 0 } },
+ { "end_of_who", "End of /WHO list", 1, { 0 } },
+
+ /* ---- */
+ { NULL, "Your messages", 0 },
+
+ { "own_msg", "%K<%n$2%W$0%K>%n %|$1", 3, { 0, 0, 0 } },
+ { "own_msg_channel", "%K<%n$3%W$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
+ { "own_msg_private", "%K[%rmsg%K(%R$0%K)]%n $1", 2, { 0, 0 } },
+ { "own_msg_private_query", "%K<%W$2%K>%n %|$1", 3, { 0, 0, 0 } },
+ { "own_notice", "%K[%rnotice%K(%R$0%K)]%n $1", 2, { 0, 0 } },
+ { "own_me", "%W * $0%n $1", 2, { 0, 0 } },
+ { "own_ctcp", "%K[%rctcp%K(%R$0%K)]%n $1 $2", 3, { 0, 0, 0 } },
+ { "own_wall", "%K[%WWall%K/%c$0%K]%n $1", 2, { 0, 0 } },
+
+ /* ---- */
+ { NULL, "Received messages", 0 },
+
+ { "pubmsg_me", "%K<%n$2%Y$0%K>%n %|$1", 3, { 0, 0, 0 } },
+ { "pubmsg_me_channel", "%K<%n$3%Y$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
+ { "pubmsg_hilight", "%K<%n$3$0$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
+ { "pubmsg_hilight_channel", "%K<%n$4$0$1%K:%c$2%K>%n %|$3", 5, { 0, 0, 0, 0, 0 } },
+ { "pubmsg", "%K<%n$2$0%K>%n %|$1", 3, { 0, 0, 0 } },
+ { "pubmsg_channel", "%K<%n$3$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
+ { "msg_private", "%K[%R$0%K(%r$1%K)]%n $2", 3, { 0, 0, 0 } },
+ { "msg_private_query", "%K<%R$0%K>%n %|$2", 3, { 0, 0, 0 } },
+ { "notice_server", "%g!$0%n $1", 2, { 0, 0 } },
+ { "notice_public", "%K-%M$0%K:%m$1%K-%n $2", 3, { 0, 0, 0 } },
+ { "notice_public_ops", "%K-%M$0%K:%m@$1%K-%n $2", 3, { 0, 0, 0 } },
+ { "notice_private", "%K-%M$0%K(%m$1%K)-%n $2", 3, { 0, 0, 0 } },
+ { "action_private", "%W (*) $0%n $2", 3, { 0, 0, 0 } },
+ { "action_private_query", "%W * $0%n $2", 3, { 0, 0, 0 } },
+ { "action_public", "%W * $0%n $1", 2, { 0, 0 } },
+ { "action_public_channel", "%W * $0%K:%c$1%n $2", 3, { 0, 0, 0 } },
+
+ /* ---- */
+ { NULL, "CTCPs", 0 },
+
+ { "ctcp_reply", "CTCP %_$0%_ reply from %_$1%_%K:%n $2", 3, { 0, 0, 0 } },
+ { "ctcp_reply_channel", "CTCP %_$0%_ reply from %_$1%_ in channel %_$3%_%K:%n $2", 4, { 0, 0, 0, 0 } },
+ { "ctcp_ping_reply", "CTCP %_PING%_ reply from %_$0%_: $1.$2 seconds", 3, { 0, 2, 2 } },
+ { "ctcp_requested", "%g>>> %_$0%_ %K[%g$1%K] %grequested %_$2%_ from %_$3", 4, { 0, 0, 0, 0 } },
+
+ /* ---- */
+ { NULL, "Other server events", 0 },
+
+ { "online", "Users online: %_$0", 1, { 0 } },
+ { "pong", "PONG received from $0: $1", 2, { 0, 0 } },
+ { "wallops", "%WWALLOP%n $0: $1", 2, { 0, 0 } },
+ { "action_wallops", "%WWALLOP * $0%n $1", 2, { 0, 0 } },
+ { "error", "%_ERROR%_ $0", 1, { 0 } },
+ { "unknown_mode", "Unknown mode character $0", 1, { 0 } },
+ { "not_chanop", "You're not channel operator in $0", 1, { 0 } },
+
+ /* ---- */
+ { NULL, "Misc", 0 },
+
+ { "ignored", "Ignoring %_$1%_ from %_$0%_", 2, { 0, 0 } },
+ { "unignored", "Unignored %_$0%_", 1, { 0 } },
+ { "ignore_not_found", "%_$0%_ is not being ignored", 1, { 0 } },
+ { "ignore_no_ignores", "There are no ignores", 0 },
+ { "ignore_header", "Ignorance List:", 0 },
+ { "ignore_line", "$[-4]0 $1: $2 $3 $4", 4, { 1, 0, 0, 0 } },
+ { "ignore_footer", "", 0 },
+ { "talking_in", "You are now talking in %_$0%_", 1, { 0 } },
+ { "query_start", "Starting query with %_$0%_", 1, { 0 } },
+ { "no_query", "No query with %_$0%_", 1, { 0 } },
+ { "no_msgs_got", "You have not received a message from anyone yet", 0 },
+ { "no_msgs_sent", "You have not sent a message to anyone yet", 0 },
+
+ { NULL, NULL, 0 }
+};
--- /dev/null
+#include "printtext.h"
+
+enum {
+ IRCTXT_MODULE_NAME,
+
+ IRCTXT_FILL_1,
+
+ IRCTXT_LAG_DISCONNECTED,
+ IRCTXT_DISCONNECTED,
+ IRCTXT_SERVER_LIST,
+ IRCTXT_SERVER_LOOKUP_LIST,
+ IRCTXT_SERVER_RECONNECT_LIST,
+ IRCTXT_RECONNECT_REMOVED,
+ IRCTXT_RECONNECT_NOT_FOUND,
+ IRCTXT_QUERY_SERVER_CHANGED,
+ IRCTXT_SETUPSERVER_ADDED,
+ IRCTXT_SETUPSERVER_REMOVED,
+ IRCTXT_SETUPSERVER_NOT_FOUND,
+ IRCTXT_SETUPSERVER_HEADER,
+ IRCTXT_SETUPSERVER_LINE,
+ IRCTXT_SETUPSERVER_FOOTER,
+ IRCTXT_NETSPLIT,
+ IRCTXT_NETSPLIT_MORE,
+ IRCTXT_NETSPLIT_JOIN,
+ IRCTXT_NETSPLIT_JOIN_MORE,
+ IRCTXT_NO_NETSPLITS,
+ IRCTXT_NETSPLITS_HEADER,
+ IRCTXT_NETSPLITS_LINE,
+ IRCTXT_NETSPLITS_FOOTER,
+ IRCTXT_IRCNET_ADDED,
+ IRCTXT_IRCNET_REMOVED,
+ IRCTXT_IRCNET_NOT_FOUND,
+ IRCTXT_IRCNET_HEADER,
+ IRCTXT_IRCNET_LINE,
+ IRCTXT_IRCNET_FOOTER,
+
+ IRCTXT_FILL_2,
+
+ IRCTXT_JOIN,
+ IRCTXT_PART,
+ IRCTXT_JOINERROR_TOOMANY,
+ IRCTXT_JOINERROR_FULL,
+ IRCTXT_JOINERROR_INVITE,
+ IRCTXT_JOINERROR_BANNED,
+ IRCTXT_JOINERROR_BAD_KEY,
+ IRCTXT_JOINERROR_BAD_MASK,
+ IRCTXT_JOINERROR_UNAVAIL,
+ IRCTXT_KICK,
+ IRCTXT_QUIT,
+ IRCTXT_QUIT_ONCE,
+ IRCTXT_INVITE,
+ IRCTXT_INVITING,
+ IRCTXT_NOT_INVITED,
+ IRCTXT_NAMES,
+ IRCTXT_NAMES_NICK,
+ IRCTXT_ENDOFNAMES,
+ IRCTXT_CHANNEL_CREATED,
+ IRCTXT_TOPIC,
+ IRCTXT_NO_TOPIC,
+ IRCTXT_NEW_TOPIC,
+ IRCTXT_TOPIC_UNSET,
+ IRCTXT_TOPIC_INFO,
+ IRCTXT_CHANMODE_CHANGE,
+ IRCTXT_SERVER_CHANMODE_CHANGE,
+ IRCTXT_CHANNEL_MODE,
+ IRCTXT_BANTYPE,
+ IRCTXT_NO_BANS,
+ IRCTXT_BANLIST,
+ IRCTXT_BANLIST_LONG,
+ IRCTXT_EBANLIST,
+ IRCTXT_EBANLIST_LONG,
+ IRCTXT_INVITELIST,
+ IRCTXT_NO_SUCH_CHANNEL,
+ IRCTXT_CHANNEL_SYNCED,
+ IRCTXT_NOT_IN_CHANNELS,
+ IRCTXT_CURRENT_CHANNEL,
+ IRCTXT_CHANLIST_HEADER,
+ IRCTXT_CHANLIST_LINE,
+ IRCTXT_CHANSETUP_NOT_FOUND,
+ IRCTXT_CHANSETUP_ADDED,
+ IRCTXT_CHANSETUP_REMOVED,
+ IRCTXT_CHANSETUP_HEADER,
+ IRCTXT_CHANSETUP_LINE,
+ IRCTXT_CHANSETUP_FOOTER,
+
+ IRCTXT_FILL_4,
+
+ IRCTXT_USERMODE_CHANGE,
+ IRCTXT_USER_MODE,
+ IRCTXT_AWAY,
+ IRCTXT_UNAWAY,
+ IRCTXT_NICK_AWAY,
+ IRCTXT_NO_SUCH_NICK,
+ IRCTXT_YOUR_NICK,
+ IRCTXT_YOUR_NICK_CHANGED,
+ IRCTXT_NICK_CHANGED,
+ IRCTXT_NICK_IN_USE,
+ IRCTXT_NICK_UNAVAILABLE,
+ IRCTXT_YOUR_NICK_OWNED,
+
+ IRCTXT_FILL_5,
+
+ IRCTXT_WHOIS,
+ IRCTXT_WHOWAS,
+ IRCTXT_WHOIS_IDLE,
+ IRCTXT_WHOIS_IDLE_SIGNON,
+ IRCTXT_WHOIS_SERVER,
+ IRCTXT_WHOIS_OPER,
+ IRCTXT_WHOIS_REGISTERED,
+ IRCTXT_WHOIS_CHANNELS,
+ IRCTXT_WHOIS_AWAY,
+ IRCTXT_END_OF_WHOIS,
+ IRCTXT_END_OF_WHOWAS,
+ IRCTXT_WHOIS_NOT_FOUND,
+ IRCTXT_WHO,
+ IRCTXT_END_OF_WHO,
+
+ IRCTXT_FILL_6,
+
+ IRCTXT_OWN_MSG,
+ IRCTXT_OWN_MSG_CHANNEL,
+ IRCTXT_OWN_MSG_PRIVATE,
+ IRCTXT_OWN_MSG_PRIVATE_QUERY,
+ IRCTXT_OWN_NOTICE,
+ IRCTXT_OWN_ME,
+ IRCTXT_OWN_CTCP,
+ IRCTXT_OWN_WALL,
+
+ IRCTXT_FILL_7,
+
+ IRCTXT_PUBMSG_ME,
+ IRCTXT_PUBMSG_ME_CHANNEL,
+ IRCTXT_PUBMSG_HILIGHT,
+ IRCTXT_PUBMSG_HILIGHT_CHANNEL,
+ IRCTXT_PUBMSG,
+ IRCTXT_PUBMSG_CHANNEL,
+ IRCTXT_MSG_PRIVATE,
+ IRCTXT_MSG_PRIVATE_QUERY,
+ IRCTXT_NOTICE_SERVER,
+ IRCTXT_NOTICE_PUBLIC,
+ IRCTXT_NOTICE_PUBLIC_OPS,
+ IRCTXT_NOTICE_PRIVATE,
+ IRCTXT_ACTION_PRIVATE,
+ IRCTXT_ACTION_PRIVATE_QUERY,
+ IRCTXT_ACTION_PUBLIC,
+ IRCTXT_ACTION_PUBLIC_CHANNEL,
+
+ IRCTXT_FILL_8,
+
+ IRCTXT_CTCP_REPLY,
+ IRCTXT_CTCP_REPLY_CHANNEL,
+ IRCTXT_CTCP_PING_REPLY,
+ IRCTXT_CTCP_REQUESTED,
+
+ IRCTXT_FILL_10,
+
+ IRCTXT_ONLINE,
+ IRCTXT_PONG,
+ IRCTXT_WALLOPS,
+ IRCTXT_ACTION_WALLOPS,
+ IRCTXT_ERROR,
+ IRCTXT_UNKNOWN_MODE,
+ IRCTXT_NOT_CHANOP,
+
+ IRCTXT_FILL_11,
+
+ IRCTXT_IGNORED,
+ IRCTXT_UNIGNORED,
+ IRCTXT_IGNORE_NOT_FOUND,
+ IRCTXT_IGNORE_NO_IGNORES,
+ IRCTXT_IGNORE_HEADER,
+ IRCTXT_IGNORE_LINE,
+ IRCTXT_IGNORE_FOOTER,
+ IRCTXT_TALKING_IN,
+ IRCTXT_QUERY_STARTED,
+ IRCTXT_NO_QUERY,
+ IRCTXT_NO_MSGS_GOT,
+ IRCTXT_NO_MSGS_SENT
+};
+
+extern FORMAT_REC fecommon_irc_formats[];
--- /dev/null
+#include "common.h"
+
+#define MODULE_NAME "fe-common/silc"
+
+#undef PACKAGE
+#undef VERSION
+#include "silcincludes.h"
+#include "clientlibincludes.h"
+#include "silc-core.h"
--- /dev/null
+/* this file is automatically generated by configure - don't change */
+void fe_silc_modules_init(void) { }
+void fe_silc_modules_deinit(void) { }
--- /dev/null
+bin_PROGRAMS = silc
+
+INCLUDES = \
+ $(GLIB_CFLAGS) \
+ -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/ \
+ $(CURSES_INCLUDEDIR) \
+ -DLOCALEDIR=\""$(datadir)/locale"\"
+
+silc_DEPENDENCIES = @COMMON_LIBS@
+
+silc_LDADD = \
+ @COMMON_LIBS@ \
+ @PERL_LINK_LIBS@ \
+ @PERL_FE_LINK_LIBS@ \
+ $(PROG_LIBS) \
+ $(CURSES_LIBS) \
+ -L../../../lib -lsilcclient -lsilc
+
+silc_SOURCES = \
+ gui-entry.c \
+ gui-expandos.c \
+ gui-printtext.c \
+ gui-readline.c \
+ gui-windows.c \
+ lastlog.c \
+ mainwindows.c \
+ mainwindow-activity.c \
+ mainwindows-save.c \
+ screen.c \
+ statusbar.c \
+ statusbar-items.c \
+ textbuffer.c \
+ textbuffer-commands.c \
+ textbuffer-view.c \
+ silc.c \
+ module-formats.c
+
+noinst_HEADERS = \
+ gui-entry.h \
+ gui-printtext.h \
+ gui-readline.h \
+ gui-windows.h \
+ mainwindows.h \
+ statusbar.h \
+ screen.h \
+ textbuffer.h \
+ textbuffer-view.h \
+ module.h \
+ module-formats.h
--- /dev/null
+/*
+ gui-entry.c : irssi
+
+ Copyright (C) 1999 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"
+
+#include "gui-printtext.h"
+#include "screen.h"
+
+static GString *entry;
+static int promptlen, permanent_prompt, pos, scrstart, scrpos;
+static int prompt_hidden;
+static char *prompt;
+
+static void entry_screenpos(void)
+{
+ if (pos-scrstart < COLS-2-promptlen && pos-scrstart > 0) {
+ scrpos = pos-scrstart;
+ return;
+ }
+
+ if (pos < COLS-1-promptlen) {
+ scrstart = 0;
+ scrpos = pos;
+ } else {
+ scrpos = (COLS-promptlen)*2/3;
+ scrstart = pos-scrpos;
+ }
+}
+
+static void entry_update(void)
+{
+ 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);
+ }
+ }
+ clrtoeol();
+
+ move_cursor(LINES-1, scrpos+promptlen);
+ screen_refresh(NULL);
+}
+
+void gui_entry_set_prompt(const char *str)
+{
+ if (str != NULL) {
+ if (permanent_prompt) return;
+
+ g_free_not_null(prompt);
+ prompt = g_strdup(str);
+ promptlen = format_get_length(prompt);
+ }
+
+ if (prompt != NULL)
+ gui_printtext(0, LINES-1, prompt);
+
+ entry_screenpos();
+ entry_update();
+}
+
+void gui_entry_set_perm_prompt(const char *str)
+{
+ g_return_if_fail(str != NULL);
+
+ g_free_not_null(prompt);
+ prompt = g_strdup(str);
+ promptlen = format_get_length(prompt);
+
+ permanent_prompt = TRUE;
+ gui_entry_set_prompt(NULL);
+}
+
+void gui_entry_set_hidden(int hidden)
+{
+ prompt_hidden = hidden;
+}
+
+void gui_entry_remove_perm_prompt(void)
+{
+ permanent_prompt = FALSE;
+}
+
+void gui_entry_set_text(const char *str)
+{
+ g_return_if_fail(str != NULL);
+
+ g_string_assign(entry, str);
+ pos = entry->len;
+
+ entry_screenpos();
+ entry_update();
+}
+
+char *gui_entry_get_text(void)
+{
+ return entry->str;
+}
+
+void gui_entry_insert_text(const char *str)
+{
+ g_return_if_fail(str != NULL);
+
+ g_string_insert(entry, pos, str);
+ pos += strlen(str);
+
+ entry_screenpos();
+ entry_update();
+}
+
+void gui_entry_insert_char(char chr)
+{
+ g_string_insert_c(entry, pos, chr);
+ pos++;
+
+ entry_screenpos();
+ entry_update();
+}
+
+void gui_entry_erase(int size)
+{
+ if (pos < size) return;
+
+#ifdef WANT_BIG5
+ if (is_big5(entry->str[pos-2], entry->str[pos-1]))
+ size++;
+#endif WANT_BIG5
+
+ pos -= size;
+ g_string_erase(entry, pos, size);
+
+ entry_screenpos();
+ entry_update();
+}
+
+void gui_entry_erase_word(void)
+{
+ int to;
+
+ if (pos == 0) return;
+
+ to = pos - 1;
+
+ while (entry->str[to] == ' ' && to > 0)
+ to--;
+
+ while (entry->str[to] != ' ' && to > 0)
+ to--;
+
+ if (entry->str[to] == ' ' && to > 0)
+ to++;
+
+ g_string_erase(entry, to, pos - to);
+ pos = to;
+
+ entry_screenpos();
+ entry_update();
+}
+
+void gui_entry_erase_next_word(void)
+{
+ int to = pos;
+
+ if (pos == entry->len) return;
+
+ while (entry->str[to] == ' ' && to < entry->len)
+ to++;
+
+ while (entry->str[to] != ' ' && to < entry->len)
+ to++;
+
+ g_string_erase(entry, pos, to - pos);
+
+ entry_screenpos();
+ entry_update();
+}
+
+int gui_entry_get_pos(void)
+{
+ return pos;
+}
+
+void gui_entry_set_pos(int p)
+{
+ if (p >= 0 && p <= entry->len)
+ pos = p;
+
+ entry_screenpos();
+ entry_update();
+}
+
+void gui_entry_move_pos(int p)
+{
+#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
+
+ if (pos+p >= 0 && pos+p <= entry->len)
+ pos += p;
+
+ entry_screenpos();
+ entry_update();
+}
+
+static void gui_entry_move_words_left(int count)
+{
+ if (pos == 0) return;
+
+ while (count > 0 && pos > 0) {
+ while (pos > 0 && entry->str[pos-1] == ' ')
+ pos--;
+ while (pos > 0 && entry->str[pos-1] != ' ')
+ pos--;
+ count--;
+ }
+}
+
+static void gui_entry_move_words_right(int count)
+{
+ 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++;
+ count--;
+ }
+}
+
+void gui_entry_move_words(int count)
+{
+ if (count < 0)
+ gui_entry_move_words_left(-count);
+ else if (count > 0)
+ gui_entry_move_words_right(count);
+
+ entry_screenpos();
+ entry_update();
+}
+
+void gui_entry_redraw(void)
+{
+ gui_entry_set_prompt(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);
+}
--- /dev/null
+#ifndef __GUI_ENTRY_H
+#define __GUI_ENTRY_H
+
+void gui_entry_set_prompt(const char *str);
+
+/* 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);
+
+void gui_entry_set_text(const char *str);
+char *gui_entry_get_text(void);
+
+void gui_entry_insert_text(const char *str);
+void gui_entry_insert_char(char chr);
+
+void gui_entry_erase(int size);
+void gui_entry_erase_word(void);
+void gui_entry_erase_next_word(void);
+
+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_redraw(void);
+
+void gui_entry_init(void);
+void gui_entry_deinit(void);
+
+#endif
--- /dev/null
+/*
+ gui-expandos.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
+*/
+
+#include "module.h"
+#include "expandos.h"
+
+#include "gui-entry.h"
+#include "gui-readline.h"
+
+/* idle time */
+static char *expando_idletime(SERVER_REC *server, void *item, int *free_ret)
+{
+ int diff;
+
+ *free_ret = TRUE;
+ diff = (int) (time(NULL) - get_idle_time());
+ return g_strdup_printf("%d", diff);
+}
+
+/* current contents of the input line */
+static char *expando_inputline(SERVER_REC *server, void *item, int *free_ret)
+{
+ return gui_entry_get_text();
+}
+
+/* value of cutbuffer */
+static char *expando_cutbuffer(SERVER_REC *server, void *item, int *free_ret)
+{
+ return cutbuffer;
+}
+
+void gui_expandos_init(void)
+{
+ expando_create("E", expando_idletime, NULL);
+ expando_create("L", expando_inputline, NULL);
+ expando_create("U", expando_cutbuffer, NULL);
+}
+
+void gui_expandos_deinit(void)
+{
+ expando_destroy("E", expando_idletime);
+ expando_destroy("L", expando_inputline);
+ expando_destroy("U", expando_cutbuffer);
+}
--- /dev/null
+/*
+ gui-printtext.c : irssi
+
+ Copyright (C) 1999 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 "printtext.h"
+
+#include "screen.h"
+#include "gui-windows.h"
+
+int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 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 next_xpos, next_ypos;
+
+void gui_printtext(int xpos, int ypos, const char *str)
+{
+ next_xpos = xpos;
+ next_ypos = ypos;
+
+ printtext_gui(str);
+
+ next_xpos = next_ypos = -1;
+}
+
+static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
+{
+ LINE_REC *line;
+ time_t old_time;
+
+ old_time = time(NULL)-(scrollback_hours*3600)+1;
+ if (view->buffer->lines_count >=
+ 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 */
+ break;
+ }
+ textbuffer_view_remove_line(view, line);
+ }
+ }
+}
+
+static void get_colors(int flags, int *fg, int *bg)
+{
+ if (flags & PRINTFLAG_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 (*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 == 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;
+}
+
+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;
+
+ /* color should never have last bit on or it would be treated as a
+ command! */
+ color = (fg & 0x0f) | ((bg & 0x07) << 4);
+ pos = 0;
+
+ if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != last_color) ||
+ ((fg & ATTR_COLOR8) && (fg & 0xf0) != (last_color & 0xf0))) {
+ data[pos++] = 0;
+ data[pos++] = color == 0 ? LINE_CMD_COLOR0 : color;
+ }
+
+ if ((flags & PRINTFLAG_UNDERLINE) != (last_flags & PRINTFLAG_UNDERLINE)) {
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_UNDERLINE;
+ }
+ if (fg & ATTR_COLOR8) {
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_COLOR8;
+ }
+ if (bg & 0x08) {
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_BLINK;
+ }
+ if (flags & PRINTFLAG_INDENT) {
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_INDENT;
+ }
+
+ *line = textbuffer_insert(buffer, *line, data, pos, NULL);
+
+ last_flags = flags;
+ last_color = fg | (bg << 4);
+}
+
+static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line)
+{
+ static const unsigned char eol[] = { 0, LINE_CMD_EOL };
+
+ *line = textbuffer_insert(view->buffer, *line, eol, 2, NULL);
+ textbuffer_view_insert_line(view, *line);
+}
+
+static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
+ void *bgcolor, void *pflags,
+ char *str, void *level)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+ LINE_REC *insert_after;
+ LINE_INFO_REC lineinfo;
+ int fg, bg, flags;
+
+ flags = GPOINTER_TO_INT(pflags);
+ fg = GPOINTER_TO_INT(fgcolor);
+ bg = GPOINTER_TO_INT(bgcolor);
+ get_colors(flags, &fg, &bg);
+
+ if (window == NULL) {
+ g_return_if_fail(next_xpos != -1);
+
+ wmove(stdscr, next_ypos, next_xpos);
+ set_color(stdscr, fg | (bg << 4));
+ addstr(str);
+ next_xpos += strlen(str);
+ return;
+ }
+
+ lineinfo.level = GPOINTER_TO_INT(level);
+ lineinfo.time = time(NULL);
+
+ view = WINDOW_GUI(window)->view;
+ insert_after = WINDOW_GUI(window)->use_insert_after ?
+ WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
+
+ if (flags & PRINTFLAG_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);
+}
+
+static void sig_printtext_finished(WINDOW_REC *window)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+ LINE_REC *insert_after;
+
+ last_color = 0;
+ last_flags = 0;
+
+ view = WINDOW_GUI(window)->view;
+ insert_after = WINDOW_GUI(window)->use_insert_after ?
+ WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
+
+ view_add_eol(view, &insert_after);
+ remove_old_lines(view);
+}
+
+static void sig_print_format(THEME_REC *theme, const char *module,
+ TEXT_DEST_REC *dest, void *formatnump,
+ char **args)
+{
+ FORMAT_REC *formats;
+ int formatnum, n;
+
+ if (!scrollback_save_formats)
+ return;
+
+ formatnum = GPOINTER_TO_INT(formatnump);
+ formats = g_hash_table_lookup(default_formats, module);
+
+ /* <module><format_name><arg...> */
+ g_string_truncate(format, 0);
+
+ g_string_append_c(format, '\0');
+ g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+ g_string_append(format, module);
+
+ g_string_append_c(format, '\0');
+ g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+ g_string_append(format, formats[formatnum].tag);
+
+ for (n = 0; n < formats[formatnum].params; n++) {
+ g_string_append_c(format, '\0');
+ g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+ g_string_append(format, args[n]);
+ }
+}
+
+static void read_settings(void)
+{
+ scrollback_lines = settings_get_int("scrollback_lines");
+ scrollback_hours = settings_get_int("scrollback_hours");
+ scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
+ scrollback_save_formats = settings_get_bool("scrollback_save_formats");
+}
+
+void gui_printtext_init(void)
+{
+ next_xpos = next_ypos = -1;
+ format = g_string_new(NULL);
+
+ 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("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_add("beep", (SIGNAL_FUNC) beep);
+
+ read_settings();
+}
+
+void gui_printtext_deinit(void)
+{
+ g_string_free(format, TRUE);
+
+ 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("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_remove("beep", (SIGNAL_FUNC) beep);
+}
--- /dev/null
+#ifndef __GUI_PRINTTEXT_H
+#define __GUI_PRINTTEXT_H
+
+#include "gui-windows.h"
+
+extern int mirc_colors[];
+
+void gui_printtext_init(void);
+void gui_printtext_deinit(void);
+
+void gui_printtext(int xpos, int ypos, const char *str);
+
+#endif
--- /dev/null
+/*
+ gui-readline.c : irssi
+
+ Copyright (C) 1999 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 "settings.h"
+#include "special-vars.h"
+
+#include "completion.h"
+#include "command-history.h"
+#include "keyboard.h"
+#include "translation.h"
+
+#include "screen.h"
+#include "gui-entry.h"
+#include "gui-windows.h"
+
+#include <signal.h>
+
+typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item);
+typedef void (*ENTRY_REDIRECT_ENTRY_FUNC) (const char *line, void *data, SERVER_REC *server, WI_ITEM_REC *item);
+
+typedef struct {
+ SIGNAL_FUNC func;
+ int flags;
+ void *data;
+} ENTRY_REDIRECT_REC;
+
+static KEYBOARD_REC *keyboard;
+static ENTRY_REDIRECT_REC *redir;
+
+char *cutbuffer;
+static int readtag;
+static time_t idle_time;
+
+static void handle_key_redirect(int key)
+{
+ ENTRY_REDIRECT_KEY_FUNC func;
+ void *data;
+
+ func = (ENTRY_REDIRECT_KEY_FUNC) redir->func;
+ data = redir->data;
+ g_free_and_null(redir);
+
+ if (func != NULL)
+ func(key, data, active_win->active_server, active_win->active);
+
+ gui_entry_remove_perm_prompt();
+ window_update_prompt();
+}
+
+static void handle_entry_redirect(const char *line)
+{
+ ENTRY_REDIRECT_ENTRY_FUNC func;
+ void *data;
+
+ gui_entry_set_hidden(FALSE);
+
+ func = (ENTRY_REDIRECT_ENTRY_FUNC) redir->func;
+ data = redir->data;
+ g_free_and_null(redir);
+
+ if (func != NULL) {
+ func(line, data, active_win->active_server,
+ active_win->active);
+ }
+
+ gui_entry_remove_perm_prompt();
+ window_update_prompt();
+}
+
+static int get_scroll_count(void)
+{
+ const char *str;
+ double count;
+
+ str = settings_get_str("scroll_page_count");
+ count = atof(str + (*str == '/'));
+ if (count <= 0)
+ count = 1;
+ else if (count < 1)
+ count = 1.0/count;
+
+ if (*str == '/')
+ count = WINDOW_GUI(active_win)->parent->height/count;
+ return (int)count;
+}
+
+static void window_prev_page(void)
+{
+ gui_window_scroll(active_win, -get_scroll_count());
+}
+
+static void window_next_page(void)
+{
+ gui_window_scroll(active_win, get_scroll_count());
+}
+
+void handle_key(int key)
+{
+ char str[3];
+
+ idle_time = time(NULL);
+
+ if (redir != NULL && redir->flags & ENTRY_REDIRECT_FLAG_HOTKEY) {
+ handle_key_redirect(key);
+ return;
+ }
+
+ if (key >= 0 && key < 32) {
+ /* control key */
+ str[0] = '^';
+ str[1] = key+'@';
+ str[2] = '\0';
+ } else if (key == 127) {
+ str[0] = '^';
+ str[1] = '?';
+ str[2] = '\0';
+ } else {
+ str[0] = key;
+ str[1] = '\0';
+ }
+
+ if (!key_pressed(keyboard, str)) {
+ /* key wasn't used for anything, print it */
+ gui_entry_insert_char((char) key);
+ }
+}
+
+static void key_send_line(void)
+{
+ int add_history;
+ char *str;
+
+ str = gui_entry_get_text();
+ if (*str == '\0') return;
+
+ 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;
+ handle_entry_redirect(str);
+ }
+
+ if (add_history) {
+ command_history_add(active_win, gui_entry_get_text(),
+ FALSE);
+ }
+ gui_entry_set_text("");
+ command_history_clear_pos(active_win);
+}
+
+static void key_combo(void)
+{
+}
+
+static void key_backward_history(void)
+{
+ const char *text;
+
+ text = command_history_prev(active_win, gui_entry_get_text());
+ gui_entry_set_text(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);
+}
+
+static void key_beginning_of_line(void)
+{
+ gui_entry_set_pos(0);
+}
+
+static void key_end_of_line(void)
+{
+ gui_entry_set_pos(strlen(gui_entry_get_text()));
+}
+
+static void key_backward_character(void)
+{
+ gui_entry_move_pos(-1);
+}
+
+static void key_forward_character(void)
+{
+ gui_entry_move_pos(1);
+}
+
+static void key_backward_word(void)
+{
+ gui_entry_move_words(-1);
+}
+
+static void key_forward_word(void)
+{
+ gui_entry_move_words(1);
+}
+
+static void key_erase_line(void)
+{
+ g_free_not_null(cutbuffer);
+ cutbuffer = g_strdup(gui_entry_get_text());
+
+ gui_entry_set_text("");
+}
+
+static void key_erase_to_beg_of_line(void)
+{
+ int pos;
+
+ pos = gui_entry_get_pos();
+ g_free_not_null(cutbuffer);
+ cutbuffer = g_strndup(gui_entry_get_text(), pos);
+
+ gui_entry_erase(pos);
+}
+
+static void key_erase_to_end_of_line(void)
+{
+ int pos;
+
+ pos = gui_entry_get_pos();
+ g_free_not_null(cutbuffer);
+ cutbuffer = g_strdup(gui_entry_get_text()+pos);
+
+ gui_entry_set_pos(strlen(gui_entry_get_text()));
+ gui_entry_erase(strlen(gui_entry_get_text()) - pos);
+}
+
+static void key_yank_from_cutbuffer(void)
+{
+ if (cutbuffer != NULL)
+ gui_entry_insert_text(cutbuffer);
+}
+
+static void key_transpose_characters(void)
+{
+ char *line, c;
+ int pos;
+
+ pos = gui_entry_get_pos();
+ line = gui_entry_get_text();
+ 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);
+}
+
+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);
+ }
+}
+
+static void key_backspace(void)
+{
+ gui_entry_erase(1);
+}
+
+static void key_delete_previous_word(void)
+{
+ gui_entry_erase_word();
+}
+
+static void key_delete_next_word(void)
+{
+ gui_entry_erase_next_word();
+}
+
+static void key_delete_to_previous_space(void)
+{
+ gui_entry_erase_word();
+}
+
+void readline(void)
+{
+ int key;
+
+ for (;;) {
+ key = getch();
+ if (key == ERR
+#ifdef KEY_RESIZE
+ || key == KEY_RESIZE
+#endif
+ ) break;
+
+ handle_key(key);
+ }
+}
+
+time_t get_idle_time(void)
+{
+ return idle_time;
+}
+
+static void key_scroll_backward(void)
+{
+ window_prev_page();
+}
+
+static void key_scroll_forward(void)
+{
+ window_next_page();
+}
+
+static void key_scroll_start(void)
+{
+ signal_emit("command scrollback home", 3, NULL, active_win->active_server, active_win->active);
+}
+
+static void key_scroll_end(void)
+{
+ signal_emit("command scrollback end", 3, NULL, active_win->active_server, active_win->active);
+}
+
+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)
+{
+ char *line;
+ int pos;
+
+ pos = gui_entry_get_pos();
+
+ line = word_complete(active_win, gui_entry_get_text(), &pos);
+ if (line != NULL) {
+ gui_entry_set_text(line);
+ gui_entry_set_pos(pos);
+ g_free(line);
+ }
+}
+
+static void key_check_replaces(void)
+{
+ char *line;
+ int pos;
+
+ pos = gui_entry_get_pos();
+
+ line = auto_word_complete(gui_entry_get_text(), &pos);
+ if (line != NULL) {
+ gui_entry_set_text(line);
+ gui_entry_set_pos(pos);
+ g_free(line);
+ }
+}
+
+static void key_previous_window(void)
+{
+ signal_emit("command window previous", 3, "", active_win->active_server, active_win->active);
+}
+
+static void key_next_window(void)
+{
+ signal_emit("command window next", 3, "", active_win->active_server, active_win->active);
+}
+
+static void key_left_window(void)
+{
+ signal_emit("command window left", 3, "", active_win->active_server, active_win->active);
+}
+
+static void key_right_window(void)
+{
+ signal_emit("command window right", 3, "", active_win->active_server, active_win->active);
+}
+
+static void key_upper_window(void)
+{
+ signal_emit("command window up", 3, "", active_win->active_server, active_win->active);
+}
+
+static void key_lower_window(void)
+{
+ signal_emit("command window down", 3, "", active_win->active_server, active_win->active);
+}
+
+static void key_active_window(void)
+{
+ signal_emit("command window goto", 3, "active", active_win->active_server, active_win->active);
+}
+
+static void key_previous_window_item(void)
+{
+ SERVER_REC *server;
+ GSList *pos;
+
+ if (active_win->items != NULL)
+ signal_emit("command window item prev", 3, "", active_win->active_server, active_win->active);
+ else if (servers != NULL) {
+ /* change server */
+ if (active_win->active_server == NULL)
+ server = servers->data;
+ else {
+ pos = g_slist_find(servers, active_win->active_server);
+ server = pos->next != NULL ? pos->next->data : servers->data;
+ }
+ signal_emit("command window server", 3, server->tag, active_win->active_server, active_win->active);
+ }
+}
+
+static void key_next_window_item(void)
+{
+ SERVER_REC *server;
+ int index;
+
+ if (active_win->items != NULL) {
+ signal_emit("command window item next", 3, "",
+ active_win->active_server, active_win->active);
+ }
+ else if (servers != NULL) {
+ /* change server */
+ if (active_win->active_server == NULL)
+ server = servers->data;
+ else {
+ index = g_slist_index(servers, active_win->active_server);
+ server = index > 0 ? g_slist_nth(servers, index-1)->data :
+ g_slist_last(servers)->data;
+ }
+ signal_emit("command window server", 3, server->tag,
+ active_win->active_server, active_win->active);
+ }
+}
+
+static void key_insert_text(const char *data)
+{
+ char *str;
+
+ str = parse_special_string(data, active_win->active_server,
+ active_win->active, "", NULL, 0);
+ gui_entry_insert_text(str);
+ g_free(str);
+}
+
+static void sig_window_auto_changed(void)
+{
+ command_history_next(active_win, gui_entry_get_text());
+ gui_entry_set_text("");
+}
+
+static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
+ void *flags, void *data)
+{
+ redir = g_new0(ENTRY_REDIRECT_REC, 1);
+ redir->func = func;
+ redir->flags = GPOINTER_TO_INT(flags);
+ redir->data = data;
+
+ if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
+ gui_entry_set_hidden(TRUE);
+ gui_entry_set_perm_prompt(entry);
+}
+
+void gui_readline_init(void)
+{
+ static char changekeys[] = "1234567890qwertyuio";
+ char *key, data[MAX_INT_STRLEN];
+ int n;
+
+ 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);
+
+ settings_add_str("history", "scroll_page_count", "/2");
+
+ keyboard = keyboard_create(NULL);
+ key_configure_freeze();
+
+ key_bind("key", NULL, "^M", "return", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "^J", "return", (SIGNAL_FUNC) key_combo);
+
+ /* meta */
+ key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta-[", "meta2", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta-O", "meta2", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta-[O", "meta2", (SIGNAL_FUNC) key_combo);
+
+ /* arrow keys */
+ key_bind("key", NULL, "meta2-A", "up", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-B", "down", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-C", "right", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-D", "left", (SIGNAL_FUNC) key_combo);
+
+ key_bind("key", NULL, "meta2-1~", "home", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-7~", "home", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-H", "home", (SIGNAL_FUNC) key_combo);
+
+ key_bind("key", NULL, "meta2-4~", "end", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-8~", "end", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-F", "end", (SIGNAL_FUNC) key_combo);
+
+ key_bind("key", NULL, "meta2-5~", "prior", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-I", "prior", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-6~", "next", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-G", "next", (SIGNAL_FUNC) key_combo);
+
+ key_bind("key", NULL, "meta2-2~", "insert", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta2-3~", "delete", (SIGNAL_FUNC) key_combo);
+
+ /* cursor movement */
+ key_bind("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("beginning_of_line", "", "home", NULL, (SIGNAL_FUNC) key_beginning_of_line);
+ key_bind("beginning_of_line", NULL, "^A", NULL, (SIGNAL_FUNC) key_beginning_of_line);
+ key_bind("end_of_line", "", "end", NULL, (SIGNAL_FUNC) key_end_of_line);
+ key_bind("end_of_line", NULL, "^E", NULL, (SIGNAL_FUNC) key_end_of_line);
+
+ /* history */
+ key_bind("backward_history", "", "up", NULL, (SIGNAL_FUNC) key_backward_history);
+ 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("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_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space);
+ key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line);
+ key_bind("erase_to_beg_of_line", "", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
+ key_bind("erase_to_end_of_line", "", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
+ key_bind("yank_from_cutbuffer", "", "^Y", NULL, (SIGNAL_FUNC) key_yank_from_cutbuffer);
+ key_bind("transpose_characters", "Swap current and previous character", "^T", NULL, (SIGNAL_FUNC) key_transpose_characters);
+
+ /* 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);
+
+ /* window managing */
+ key_bind("previous_window", "Previous window", "^P", NULL, (SIGNAL_FUNC) key_previous_window);
+ key_bind("left_window", "Window in left", "meta-left", NULL, (SIGNAL_FUNC) key_left_window);
+ key_bind("next_window", "Next window", "^N", NULL, (SIGNAL_FUNC) key_next_window);
+ key_bind("right_window", "Window in right", "meta-right", NULL, (SIGNAL_FUNC) key_right_window);
+ key_bind("upper_window", "Upper window", "meta-up", NULL, (SIGNAL_FUNC) key_upper_window);
+ key_bind("lower_window", "Lower window", "meta-down", NULL, (SIGNAL_FUNC) key_lower_window);
+ key_bind("active_window", "Go to next window with the highest activity", "meta-a", NULL, (SIGNAL_FUNC) key_active_window);
+ key_bind("next_window_item", "Next channel/query", "^X", NULL, (SIGNAL_FUNC) key_next_window_item);
+ key_bind("previous_window_item", "Previous channel/query", NULL, NULL, (SIGNAL_FUNC) key_previous_window_item);
+
+ key_bind("refresh_screen", "Redraw screen", "^L", NULL, (SIGNAL_FUNC) irssi_redraw);
+ key_bind("scroll_backward", "Previous page", "prior", NULL, (SIGNAL_FUNC) key_scroll_backward);
+ key_bind("scroll_backward", NULL, "meta-p", NULL, (SIGNAL_FUNC) key_scroll_backward);
+ key_bind("scroll_forward", "Next page", "next", NULL, (SIGNAL_FUNC) key_scroll_forward);
+ key_bind("scroll_forward", NULL, "meta-n", NULL, (SIGNAL_FUNC) key_scroll_forward);
+ key_bind("scroll_start", "Beginning of the window", "", NULL, (SIGNAL_FUNC) key_scroll_start);
+ key_bind("scroll_end", "End of the window", "", NULL, (SIGNAL_FUNC) key_scroll_end);
+
+ /* inserting special input characters to line.. */
+ key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text);
+
+ key_bind("multi", NULL, "return", "check_replaces;send_line", NULL);
+
+ for (n = 0; changekeys[n] != '\0'; n++) {
+ key = g_strdup_printf("meta-%c", changekeys[n]);
+ ltoa(data, n+1);
+ key_bind("change_window", "Change window", key, data, (SIGNAL_FUNC) key_change_window);
+ g_free(key);
+ }
+
+ key_configure_thaw();
+
+ signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
+ signal_add("gui entry redirect", (SIGNAL_FUNC) sig_gui_entry_redirect);
+}
+
+void gui_readline_deinit(void)
+{
+ g_free_not_null(cutbuffer);
+ g_source_remove(readtag);
+
+ key_configure_freeze();
+
+ key_unbind("backward_character", (SIGNAL_FUNC) key_backward_character);
+ 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("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line);
+ key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line);
+
+ key_unbind("backward_history", (SIGNAL_FUNC) key_backward_history);
+ key_unbind("forward_history", (SIGNAL_FUNC) key_forward_history);
+
+ key_unbind("backspace", (SIGNAL_FUNC) key_backspace);
+ 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_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space);
+ key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line);
+ key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
+ key_unbind("erase_to_end_of_line", (SIGNAL_FUNC) key_erase_to_end_of_line);
+ key_unbind("yank_from_cutbuffer", (SIGNAL_FUNC) key_yank_from_cutbuffer);
+ key_unbind("transpose_characters", (SIGNAL_FUNC) key_transpose_characters);
+
+ key_unbind("word_completion", (SIGNAL_FUNC) key_word_completion);
+ key_unbind("check_replaces", (SIGNAL_FUNC) key_check_replaces);
+
+ key_unbind("previous_window", (SIGNAL_FUNC) key_previous_window);
+ key_unbind("next_window", (SIGNAL_FUNC) key_next_window);
+ key_unbind("upper_window", (SIGNAL_FUNC) key_upper_window);
+ key_unbind("lower_window", (SIGNAL_FUNC) key_lower_window);
+ key_unbind("active_window", (SIGNAL_FUNC) key_active_window);
+ key_unbind("next_window_item", (SIGNAL_FUNC) key_next_window_item);
+ key_unbind("previous_window_item", (SIGNAL_FUNC) key_previous_window_item);
+
+ key_unbind("refresh_screen", (SIGNAL_FUNC) irssi_redraw);
+ key_unbind("scroll_backward", (SIGNAL_FUNC) key_scroll_backward);
+ key_unbind("scroll_forward", (SIGNAL_FUNC) key_scroll_forward);
+ key_unbind("scroll_start", (SIGNAL_FUNC) key_scroll_start);
+ key_unbind("scroll_end", (SIGNAL_FUNC) key_scroll_end);
+
+ key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text);
+ key_unbind("change_window", (SIGNAL_FUNC) key_change_window);
+ keyboard_destroy(keyboard);
+
+ key_configure_thaw();
+
+ signal_remove("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
+ signal_remove("gui entry redirect", (SIGNAL_FUNC) sig_gui_entry_redirect);
+}
--- /dev/null
+#ifndef __GUI_READLINE_H
+#define __GUI_READLINE_H
+
+extern char *cutbuffer;
+
+void readline(void);
+time_t get_idle_time(void);
+
+void gui_readline_init(void);
+void gui_readline_deinit(void);
+
+#endif
--- /dev/null
+/*
+ gui-windows.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 "signals.h"
+#include "misc.h"
+#include "settings.h"
+#include "special-vars.h"
+
+#include "screen.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;
+
+ gui = g_new0(GUI_WINDOW_REC, 1);
+ gui->parent = parent;
+ gui->view = textbuffer_view_create(textbuffer_create(),
+ window->width, window->height,
+ settings_get_int("indent"),
+ settings_get_bool("indent_always"));
+ return gui;
+}
+
+static void gui_window_deinit(GUI_WINDOW_REC *gui)
+{
+ textbuffer_view_destroy(gui->view);
+ g_free(gui);
+}
+
+static void sig_window_create_override(gpointer tab)
+{
+ window_create_override = GPOINTER_TO_INT(tab);
+}
+
+static void gui_window_created(WINDOW_REC *window)
+{
+ MAIN_WINDOW_REC *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();
+ 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;
+ }
+ 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);
+ }
+
+ if (parent->active == NULL) parent->active = window;
+ window->gui_data = gui_window_init(window, parent);
+ signal_emit("gui window created", 1, window);
+}
+
+static void gui_window_destroyed(WINDOW_REC *window)
+{
+ MAIN_WINDOW_REC *parent;
+ GUI_WINDOW_REC *gui;
+
+ g_return_if_fail(window != NULL);
+
+ gui = WINDOW_GUI(window);
+ parent = gui->parent;
+
+ 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);
+}
+
+void gui_window_resize(WINDOW_REC *window, int width, int height)
+{
+ GUI_WINDOW_REC *gui;
+
+ gui = WINDOW_GUI(window);
+
+ window->width = width;
+ window->height = height;
+ textbuffer_view_resize(gui->view, width, height);
+}
+
+void gui_window_scroll(WINDOW_REC *window, int lines)
+{
+ g_return_if_fail(window != NULL);
+
+ textbuffer_view_scroll(WINDOW_GUI(window)->view, lines);
+ signal_emit("gui page scrolled", 1, window);
+}
+
+void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line)
+{
+ g_return_if_fail(window != NULL);
+ g_return_if_fail(line != NULL);
+
+ textbuffer_view_scroll_line(WINDOW_GUI(window)->view, line);
+ signal_emit("gui page scrolled", 1, window);
+}
+
+void window_update_prompt(void)
+{
+ 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;
+ }
+
+ 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';
+ }
+
+ /* 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)
+{
+ if (window == active_win)
+ window_update_prompt();
+}
+
+static void window_update_prompt_window_item(WI_ITEM_REC *item)
+{
+ if (item == active_win->active)
+ window_update_prompt();
+}
+
+void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent)
+{
+ MAIN_WINDOW_REC *oldparent;
+
+ oldparent = WINDOW_GUI(window)->parent;
+ if (oldparent == parent)
+ return;
+
+ 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);
+}
+
+static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
+{
+ GSList *tmp;
+
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ if (rec->sticky_windows == NULL)
+ return rec;
+ }
+
+ /* all windows are sticky, fallback to active window */
+ return active_mainwin;
+}
+
+static void signal_window_changed(WINDOW_REC *window, WINDOW_REC *old_window)
+{
+ MAIN_WINDOW_REC *parent;
+
+ g_return_if_fail(window != NULL);
+
+ if (quitting) return;
+
+ parent = WINDOW_GUI(window)->parent;
+ if (is_window_visible(window)) {
+ /* already visible */
+ active_mainwin = parent;
+ } else if (active_mainwin == NULL) {
+ /* no main window set yet */
+ active_mainwin = parent;
+ } else if (g_slist_find(parent->sticky_windows, window) != NULL) {
+ /* 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) {
+ /* active mainwindow is sticky, we'll need to
+ set the window active somewhere else */
+ active_mainwin = mainwindow_find_unsticky();
+ }
+ gui_window_reparent(window, active_mainwin);
+ }
+ active_mainwin->active = window;
+
+ if (old_window != NULL && !is_window_visible(old_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();
+}
+
+static void sig_check_window_update(WINDOW_REC *window)
+{
+ if (window == active_win)
+ window_update_prompt();
+}
+
+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();
+}
+
+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] ");
+
+ prompt = NULL; prompt_window = NULL;
+ window_create_override = -1;
+
+ read_settings();
+ signal_add("gui window create override", (SIGNAL_FUNC) sig_window_create_override);
+ signal_add("window created", (SIGNAL_FUNC) gui_window_created);
+ signal_add("window destroyed", (SIGNAL_FUNC) gui_window_destroyed);
+ signal_add_first("window changed", (SIGNAL_FUNC) signal_window_changed);
+ signal_add("window item remove", (SIGNAL_FUNC) sig_check_window_update);
+ signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void gui_windows_deinit(void)
+{
+ g_free_not_null(prompt);
+ g_free_not_null(prompt_window);
+
+ while (windows != NULL)
+ window_destroy(windows->data);
+
+ signal_remove("gui window create override", (SIGNAL_FUNC) sig_window_create_override);
+ 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);
+}
--- /dev/null
+#ifndef __GUI_WINDOWS_H
+#define __GUI_WINDOWS_H
+
+#include "mainwindows.h"
+#include "textbuffer-view.h"
+
+#define WINDOW_GUI(a) ((GUI_WINDOW_REC *) ((a)->gui_data))
+
+#define is_window_visible(win) \
+ (WINDOW_GUI(win)->parent->active == (win))
+
+typedef struct {
+ MAIN_WINDOW_REC *parent;
+ TEXT_BUFFER_VIEW_REC *view;
+
+ unsigned int use_insert_after:1;
+ LINE_REC *insert_after;
+} GUI_WINDOW_REC;
+
+void gui_windows_init(void);
+void gui_windows_deinit(void);
+
+WINDOW_REC *gui_window_create(MAIN_WINDOW_REC *parent);
+
+void gui_window_resize(WINDOW_REC *window, int width, int height);
+void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent);
+
+#define gui_window_redraw(window) \
+ textbuffer_view_redraw(WINDOW_GUI(window)->view)
+
+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);
+
+#endif
--- /dev/null
+/*
+ lastlog.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 "signals.h"
+#include "commands.h"
+#include "misc.h"
+#include "levels.h"
+#include "settings.h"
+
+#include "module-formats.h"
+#include "printtext.h"
+
+#include "gui-windows.h"
+#include "gui-printtext.h"
+
+#define MAX_LINES_WITHOUT_FORCE 1000
+
+static void window_lastlog_clear(WINDOW_REC *window)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+ GList *tmp, *next;
+
+ screen_refresh_freeze();
+ view = WINDOW_GUI(window)->view;
+ for (tmp = textbuffer_view_get_lines(view); tmp != NULL; tmp = next) {
+ LINE_REC *line = tmp->data;
+
+ next = tmp->next;
+ if (line->info.level & MSGLEVEL_LASTLOG)
+ textbuffer_view_remove_line(view, line);
+ }
+ textbuffer_view_redraw(view);
+ screen_refresh_thaw();
+}
+
+/* Only unknown keys in `optlist' should be levels.
+ Returns -1 if unknown option was given. */
+int cmd_options_get_level(const char *cmd, GHashTable *optlist)
+{
+ GSList *list, *tmp, *next;
+ int level, retlevel;
+
+ /* get all the options, then remove the known ones. there should
+ be only one left - the server tag. */
+ list = hashtable_get_keys(optlist);
+ if (cmd != NULL) {
+ for (tmp = list; tmp != NULL; tmp = next) {
+ char *option = tmp->data;
+ next = tmp->next;
+
+ if (command_have_option(cmd, option))
+ list = g_slist_remove(list, option);
+ }
+ }
+
+ retlevel = 0;
+ while (list != NULL) {
+ level = level_get(list->data);
+ if (level == 0) {
+ /* unknown option */
+ signal_emit("error command", 2,
+ GINT_TO_POINTER(CMDERR_OPTION_UNKNOWN),
+ list->data);
+ retlevel = -1;
+ break;
+ }
+
+ retlevel |= level;
+ list = g_slist_remove(list, list->data);
+ }
+
+ return retlevel;
+}
+
+static void show_lastlog(const char *searchtext, GHashTable *optlist,
+ int start, int count, int fhandle)
+{
+ WINDOW_REC *window;
+ LINE_REC *startline;
+ GList *list, *tmp;
+ GString *line;
+ char *str;
+ int level, len;
+
+ level = cmd_options_get_level("lastlog", optlist);
+ if (level == -1) return; /* error in options */
+ if (level == 0) level = MSGLEVEL_ALL;
+
+ if (g_hash_table_lookup(optlist, "clear") != NULL) {
+ window_lastlog_clear(active_win);
+ if (*searchtext == '\0')
+ return;
+ }
+
+ /* which window's lastlog to look at? */
+ window = active_win;
+ str = g_hash_table_lookup(optlist, "window");
+ if (str != NULL) {
+ window = is_numeric(str, '\0') ?
+ window_find_refnum(atoi(str)) :
+ window_find_item(NULL, str);
+ if (window == NULL) {
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ TXT_REFNUM_NOT_FOUND, str);
+ return;
+ }
+ }
+
+ if (g_hash_table_lookup(optlist, "new") != NULL)
+ startline = textbuffer_view_get_bookmark(WINDOW_GUI(window)->view, "lastlog_last_check");
+ else if (g_hash_table_lookup(optlist, "away") != NULL)
+ startline = textbuffer_view_get_bookmark(WINDOW_GUI(window)->view, "lastlog_last_away");
+ else
+ startline = NULL;
+
+ if (startline == NULL) {
+ list = textbuffer_view_get_lines(WINDOW_GUI(window)->view);
+ startline = list == NULL ? NULL : list->data;
+ }
+
+ list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline,
+ level, MSGLEVEL_LASTLOG,
+ searchtext,
+ g_hash_table_lookup(optlist, "regexp") != NULL,
+ g_hash_table_lookup(optlist, "word") != NULL,
+ g_hash_table_lookup(optlist, "case") != NULL);
+
+ len = g_list_length(list);
+ if (count <= 0)
+ tmp = list;
+ else {
+ int pos = len-count;
+
+ if (pos < 0) pos = 0;
+ pos += start;
+
+ tmp = pos > len ? NULL : g_list_nth(list, pos);
+ len = g_list_length(tmp);
+ }
+
+ if (len > MAX_LINES_WITHOUT_FORCE && fhandle == -1 &&
+ g_hash_table_lookup(optlist, "force") == NULL) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+ TXT_LASTLOG_TOO_LONG, len);
+ g_list_free(list);
+ return;
+ }
+
+ if (fhandle == -1 && g_hash_table_lookup(optlist, "-") == NULL)
+ printformat(NULL, NULL, MSGLEVEL_LASTLOG, TXT_LASTLOG_START);
+
+ line = g_string_new(NULL);
+ while (tmp != NULL && (count < 0 || count > 0)) {
+ LINE_REC *rec = tmp->data;
+
+ /* get the line text */
+ textbuffer_line2text(rec, fhandle == -1, line);
+ if (!settings_get_bool("timestamps")) {
+ struct tm *tm = localtime(&rec->info.time);
+ char timestamp[10];
+
+ g_snprintf(timestamp, sizeof(timestamp),
+ "%02d:%02d ",
+ tm->tm_hour, tm->tm_min);
+ g_string_prepend(line, timestamp);
+ }
+
+ /* write to file/window */
+ if (fhandle != -1) {
+ write(fhandle, line->str, line->len);
+ write(fhandle, "\n", 1);
+ } else {
+ printtext_window(active_win, MSGLEVEL_LASTLOG,
+ "%s", line->str);
+ }
+
+ count--;
+ tmp = tmp->next;
+ }
+ g_string_free(line, TRUE);
+
+ if (fhandle == -1 && g_hash_table_lookup(optlist, "-") == NULL)
+ printformat(NULL, NULL, MSGLEVEL_LASTLOG, TXT_LASTLOG_END);
+
+ textbuffer_view_set_bookmark_bottom(WINDOW_GUI(window)->view,
+ "lastlog_last_check");
+
+ textbuffer_line_unref_list(WINDOW_GUI(window)->view->buffer, list);
+ g_list_free(list);
+}
+
+/* SYNTAX: LASTLOG [-] [-file <filename>] [-clear] [-<level> -<level...>]
+ [-new | -away] [-regexp | -word] [-case]
+ [-window <ref#|name>] [<pattern>] [<count> [<start>]] */
+static void cmd_lastlog(const char *data)
+{
+ GHashTable *optlist;
+ char *text, *countstr, *start, *fname;
+ void *free_arg;
+ int count, fhandle;
+
+ 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))
+ return;
+
+ if (*start == '\0' && is_numeric(text, 0)) {
+ if (is_numeric(countstr, 0))
+ start = countstr;
+ countstr = text;
+ text = "";
+ }
+ count = atoi(countstr);
+ if (count == 0) count = -1;
+
+ /* target where to print it */
+ fhandle = -1;
+ fname = g_hash_table_lookup(optlist, "file");
+ if (fname != NULL) {
+ fname = convert_home(fname);
+ fhandle = open(fname, O_WRONLY | O_APPEND | O_CREAT,
+ octal2dec(settings_get_int("log_create_mode")));
+ g_free(fname);
+ }
+
+ if (fname != NULL && fhandle == -1) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "%s", g_strerror(errno));
+ } else {
+ show_lastlog(text, optlist, atoi(start), count, fhandle);
+ if (fhandle != -1)
+ close(fhandle);
+ }
+
+ cmd_params_free(free_arg);
+}
+
+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");
+}
+
+void lastlog_deinit(void)
+{
+ command_unbind("lastlog", (SIGNAL_FUNC) cmd_lastlog);
+}
--- /dev/null
+/*
+ mainwindow-activity.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
+*/
+
+#include "module.h"
+#include "signals.h"
+
+#include "gui-windows.h"
+
+/* Don't send window activity if window is already visible in
+ another mainwindow */
+static void sig_activity(WINDOW_REC *window)
+{
+ GSList *tmp;
+
+ if (!is_window_visible(window) || window->data_level == 0)
+ return;
+
+ window->data_level = 0;
+ window->hilight_color = 0;
+
+ for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+ WI_ITEM_REC *item = tmp->data;
+
+ item->data_level = 0;
+ item->hilight_color = 0;
+ }
+ signal_stop();
+}
+
+void mainwindow_activity_init(void)
+{
+ signal_add_first("window hilight", (SIGNAL_FUNC) sig_activity);
+ signal_add_first("window activity", (SIGNAL_FUNC) sig_activity);
+}
+
+void mainwindow_activity_deinit(void)
+{
+ signal_remove("window hilight", (SIGNAL_FUNC) sig_activity);
+ signal_remove("window activity", (SIGNAL_FUNC) sig_activity);
+}
--- /dev/null
+/*
+ mainwindows-save.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"
+
+static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node)
+{
+ GSList *tmp;
+ GString *str;
+ 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);
+
+ str = g_string_new(NULL);
+ for (tmp = window->sticky_windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *rec = tmp->data;
+ g_string_sprintfa(str, "%d ", rec->refnum);
+ }
+ if (str->len > 1) {
+ g_string_truncate(str, str->len-1);
+ iconfig_node_set_str(node, "sticky", str->str);
+ }
+ g_string_free(str, TRUE);
+}
+
+static void sig_windows_saved(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;
+}
+
+static GSList *read_sorted_windows(CONFIG_NODE *node)
+{
+ GSList *tmp, *output;
+
+ output = NULL;
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ output = g_slist_insert_sorted(output, tmp->data,
+ (GCompareFunc) window_node_cmp);
+ }
+
+ return output;
+}
+
+static void restore_sticky_windows(CONFIG_NODE *node,
+ MAIN_WINDOW_REC *mainwindow)
+{
+ WINDOW_REC *window;
+ char **sticky_list, **sticky;
+
+ sticky_list = g_strsplit(config_node_get_str(node, "sticky", ""), " ", -1);
+ for (sticky = sticky_list; *sticky != NULL; sticky++) {
+ window = window_find_refnum(atoi(*sticky));
+ if (window != NULL) {
+ mainwindow->sticky_windows =
+ g_slist_append(mainwindow->sticky_windows,
+ window);
+ }
+ }
+ g_strfreev(sticky_list);
+}
+
+static WINDOW_REC *window_find_hidden(void)
+{
+ GSList *tmp;
+
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *rec = tmp->data;
+
+ if (!is_window_visible(rec))
+ return rec;
+ }
+
+ return NULL;
+}
+
+static void sig_windows_restored(void)
+{
+ MAIN_WINDOW_REC *mainwindow, *lowerwin;
+ WINDOW_REC *window;
+ CONFIG_NODE *node;
+ GSList *tmp, *tmp2, *sorted_windows, *sorted_config;
+ int count, newsize;
+
+ node = iconfig_node_traverse("mainwindows", FALSE);
+ if (node == NULL) return;
+
+ /* create all windows, shrink the lower windows to minimum size */
+ lowerwin = mainwindows->data;
+ count = g_slist_length(node->value);
+ while (count > 1) {
+ window = window_find_hidden();
+ if (window == NULL)
+ break;
+
+ mainwindow = mainwindow_create();
+ if (mainwindow == NULL)
+ break;
+
+ mainwindow->active = window;
+ WINDOW_GUI(window)->parent = mainwindow;
+
+ active_mainwin = NULL;
+ window_set_active(window);
+
+ if (lowerwin->height > WINDOW_MIN_SIZE)
+ mainwindow_set_size(lowerwin, WINDOW_MIN_SIZE);
+ count--;
+
+ lowerwin = mainwindow;
+ }
+
+ sorted_config = read_sorted_windows(node);
+ sorted_windows = mainwindows_get_sorted(FALSE);
+ for (tmp = sorted_windows, tmp2 = sorted_config;
+ tmp != NULL && tmp2 != NULL;
+ tmp = tmp->next, tmp2 = tmp2->next) {
+ MAIN_WINDOW_REC *mainwindow = tmp->data;
+ CONFIG_NODE *node = tmp2->data;
+
+ window = window_find_refnum(atoi(node->key));
+ if (window == NULL) {
+ mainwindow_destroy(mainwindow);
+ continue;
+ }
+
+ if (is_window_visible(window)) {
+ active_mainwin = WINDOW_GUI(window)->parent;
+ window_set_active(window_find_hidden());
+ }
+
+ active_mainwin = mainwindow;
+ window_set_active(window);
+
+ restore_sticky_windows(node, mainwindow);
+
+ newsize = config_node_get_int(node, "lines", 0);
+ if (newsize > 0)
+ mainwindow_set_size(mainwindow, newsize);
+ }
+ g_slist_free(sorted_windows);
+ g_slist_free(sorted_config);
+
+ irssi_redraw();
+}
+
+void mainwindows_save_init(void)
+{
+ signal_add("windows saved", (SIGNAL_FUNC) sig_windows_saved);
+ signal_add("windows restored", (SIGNAL_FUNC) sig_windows_restored);
+}
+
+void mainwindows_save_deinit(void)
+{
+ signal_remove("windows saved", (SIGNAL_FUNC) sig_windows_saved);
+ signal_remove("windows restored", (SIGNAL_FUNC) sig_windows_restored);
+}
--- /dev/null
+/*
+ mainwindows.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 "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "settings.h"
+#include "printtext.h"
+
+#include "screen.h"
+#include "statusbar.h"
+#include "gui-windows.h"
+
+#define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1)
+
+GSList *mainwindows;
+MAIN_WINDOW_REC *active_mainwin;
+
+static int reserved_up, reserved_down;
+static int screen_width, screen_height;
+
+static MAIN_WINDOW_REC *find_window_with_room(void)
+{
+ MAIN_WINDOW_REC *biggest_rec;
+ GSList *tmp;
+ int space, biggest;
+
+ biggest = 0; biggest_rec = NULL;
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ space = rec->height;
+ if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) {
+ biggest = space;
+ biggest_rec = rec;
+ }
+ }
+
+ return biggest_rec;
+}
+
+#ifdef USE_CURSES_WINDOWS
+static void create_curses_window(MAIN_WINDOW_REC *window)
+{
+ window->curses_win = newwin(window->height, window->width,
+ window->first_line, 0);
+ idlok(window->curses_win, 1);
+}
+#endif
+
+static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff)
+{
+ GSList *tmp;
+
+ if (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
+
+ 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);
+ }
+
+ textbuffer_view_set_window(WINDOW_GUI(window->active)->view,
+ window->curses_win);
+ signal_emit("mainwindow resized", 1, window);
+}
+
+void mainwindows_recreate(void)
+{
+ GSList *tmp;
+
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+#ifdef USE_CURSES_WINDOWS
+ create_curses_window(rec);
+#endif
+ textbuffer_view_set_window(WINDOW_GUI(rec->active)->view,
+ rec->curses_win);
+ }
+}
+
+MAIN_WINDOW_REC *mainwindow_create(void)
+{
+ MAIN_WINDOW_REC *rec, *parent;
+ int space;
+
+ rec = g_new0(MAIN_WINDOW_REC, 1);
+ rec->width = screen_width;
+ rec->statusbar_lines = 1;
+
+ if (mainwindows == NULL) {
+ active_mainwin = rec;
+
+ rec->first_line = reserved_up;
+ rec->last_line = screen_height-1 -
+ reserved_down-rec->statusbar_lines;
+ 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 = find_window_with_room();
+ if (parent == NULL)
+ return NULL; /* not enough space */
+
+ space = (parent->height-parent->statusbar_lines)/2;
+ rec->first_line = parent->first_line;
+ rec->last_line = rec->first_line + space-rec->statusbar_lines;
+ rec->height = rec->last_line-rec->first_line+1;
+ parent->first_line = rec->last_line+1+rec->statusbar_lines;
+ 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
+
+ mainwindows = g_slist_append(mainwindows, rec);
+ signal_emit("mainwindow created", 1, rec);
+ return rec;
+}
+
+static MAIN_WINDOW_REC *mainwindows_find_lower(int line)
+{
+ MAIN_WINDOW_REC *best;
+ GSList *tmp;
+
+ best = NULL;
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ if (rec->first_line > line &&
+ (best == NULL || rec->first_line < best->first_line))
+ best = rec;
+ }
+
+ return best;
+}
+
+static MAIN_WINDOW_REC *mainwindows_find_upper(int line)
+{
+ MAIN_WINDOW_REC *best;
+ GSList *tmp;
+
+ best = NULL;
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ if (rec->last_line < line &&
+ (best == NULL || rec->last_line > best->last_line))
+ best = rec;
+ }
+
+ return best;
+}
+
+static void mainwindows_add_space(int first_line, int last_line)
+{
+ MAIN_WINDOW_REC *rec;
+ int size;
+
+ if (last_line < first_line)
+ return;
+
+ size = last_line-first_line+1;
+
+ rec = mainwindows_find_lower(last_line);
+ if (rec != NULL) {
+ rec->first_line = first_line;
+ mainwindow_resize(rec, 0, size);
+ return;
+ }
+
+ rec = mainwindows_find_upper(first_line);
+ if (rec != NULL) {
+ rec->last_line = last_line-rec->statusbar_lines;
+ mainwindow_resize(rec, 0, size);
+ }
+}
+
+static void gui_windows_remove_parent(MAIN_WINDOW_REC *window)
+{
+ MAIN_WINDOW_REC *new_parent;
+ GSList *tmp;
+
+ new_parent = mainwindows->data;
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *rec = tmp->data;
+
+ if (rec->gui_data != NULL && WINDOW_GUI(rec)->parent == window)
+ gui_window_reparent(rec, new_parent);
+ }
+}
+
+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);
+
+ if (!quitting && mainwindows != NULL) {
+ gui_windows_remove_parent(window);
+ mainwindows_add_space(window->first_line, window->last_line+window->statusbar_lines);
+
+ mainwindows_redraw();
+ statusbar_redraw(NULL);
+ }
+ g_free(window);
+
+ if (active_mainwin == window) active_mainwin = NULL;
+}
+
+void mainwindows_redraw(void)
+{
+ GSList *tmp;
+
+ screen_refresh_freeze();
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ gui_window_redraw(rec->active);
+ }
+ screen_refresh_thaw();
+}
+
+static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2)
+{
+ return w1->first_line < w2->first_line ? -1 : 1;
+}
+
+static int mainwindows_compare_reverse(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2)
+{
+ return w1->first_line < w2->first_line ? 1 : -1;
+}
+
+GSList *mainwindows_get_sorted(int reverse)
+{
+ GSList *tmp, *list;
+
+ list = NULL;
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ list = g_slist_insert_sorted(list, tmp->data, (GCompareFunc)
+ (reverse ? mainwindows_compare_reverse : mainwindows_compare));
+ }
+
+ 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)
+{
+ GSList *sorted, *tmp;
+ int space;
+
+ space = 0;
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ space += rec->height-WINDOW_MIN_SIZE;
+ }
+
+ if (space < -ydiff) {
+ /* not enough space, use different algorithm */
+ mainwindows_resize_too_small(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;
+
+ space = rec->height-WINDOW_MIN_SIZE;
+ if (space <= 0) {
+ rec->first_line += ydiff;
+ rec->last_line += ydiff;
+ signal_emit("mainwindow moved", 1, rec);
+ continue;
+ }
+
+ if (space <= 0) space = 1;
+ if (space > -ydiff) space = -ydiff;
+ rec->last_line += ydiff;
+ ydiff += space;
+ rec->first_line += ydiff;
+
+ mainwindow_resize(rec, xdiff, -space);
+ }
+ g_slist_free(sorted);
+}
+
+static void mainwindows_resize_bigger(int xdiff, int ydiff)
+{
+ GSList *sorted, *tmp;
+ int moved, space;
+
+ sorted = mainwindows_get_sorted(FALSE);
+ moved = 0;
+ for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ space = rec->height-WINDOW_MIN_SIZE;
+ if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) {
+ if (moved > 0) {
+ rec->first_line += moved;
+ rec->last_line += moved;
+ signal_emit("mainwindow moved", 1, rec);
+ }
+ continue;
+ }
+
+ if (space < 0 && tmp->next != NULL) {
+ /* space below minimum */
+ space = -space;
+ if (space > ydiff) space = ydiff;
+ } else {
+ /* lowest window - give all the extra space for it */
+ space = ydiff;
+ }
+ ydiff -= space;
+ rec->first_line += moved;
+ moved += space;
+ rec->last_line += moved;
+
+ mainwindow_resize(rec, xdiff, space);
+ }
+ g_slist_free(sorted);
+}
+
+void mainwindows_resize_horiz(int xdiff)
+{
+ GSList *tmp;
+
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ mainwindow_resize(rec, xdiff, 0);
+ }
+}
+
+void mainwindows_resize(int width, int height)
+{
+ int xdiff, ydiff;
+
+ xdiff = width-screen_width;
+ ydiff = height-screen_height;
+ screen_width = width;
+ screen_height = height;
+
+ screen_refresh_freeze();
+ if (ydiff < 0)
+ mainwindows_resize_smaller(xdiff, ydiff);
+ else if (ydiff > 0)
+ mainwindows_resize_bigger(xdiff, ydiff);
+ else if (xdiff != 0)
+ mainwindows_resize_horiz(xdiff);
+
+ irssi_redraw();
+ screen_refresh_thaw();
+}
+
+int mainwindows_reserve_lines(int count, int up)
+{
+ MAIN_WINDOW_REC *window;
+ int ret;
+
+ if (up) {
+ g_return_val_if_fail(count > 0 || reserved_up > count, -1);
+
+ ret = reserved_up;
+ reserved_up += count;
+
+ window = mainwindows_find_lower(-1);
+ if (window != NULL) window->first_line += count;
+ } else {
+ g_return_val_if_fail(count > 0 || reserved_down > count, -1);
+
+ ret = reserved_down;
+ reserved_down += count;
+
+ window = mainwindows_find_upper(screen_height);
+ if (window != NULL) window->last_line -= count;
+ }
+
+ if (window != NULL)
+ mainwindow_resize(window, 0, -count);
+
+ return ret;
+}
+
+static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win,
+ MAIN_WINDOW_REC *shrink_win, int count)
+{
+ mainwindow_resize(grow_win, 0, count);
+ mainwindow_resize(shrink_win, 0, -count);
+ gui_window_redraw(grow_win->active);
+ gui_window_redraw(shrink_win->active);
+ statusbar_redraw(grow_win->statusbar);
+ statusbar_redraw(shrink_win->statusbar);
+}
+
+static int mainwindow_grow(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) {
+ 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;
+
+ window->first_line -= count;
+ shrink_win->last_line -= count;
+ }
+
+ mainwindows_resize_two(window, shrink_win, count);
+ return TRUE;
+}
+
+static int mainwindow_shrink(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;
+
+ window->first_line += count;
+ grow_win->last_line += count;
+ }
+
+ mainwindows_resize_two(grow_win, window, count);
+ return TRUE;
+}
+
+void mainwindow_set_size(MAIN_WINDOW_REC *window, int size)
+{
+ size -= window->height;
+ if (size < 0)
+ mainwindow_shrink(window, size);
+ else
+ mainwindow_grow(window, size);
+}
+
+/* SYNTAX: WINDOW GROW [<lines>] */
+static void cmd_window_grow(const char *data)
+{
+ MAIN_WINDOW_REC *window;
+ int count;
+
+ count = *data == '\0' ? 1 : atoi(data);
+ window = WINDOW_GUI(active_win)->parent;
+
+ if (!mainwindow_grow(window, count)) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+ TXT_WINDOW_TOO_SMALL);
+ }
+}
+
+/* SYNTAX: WINDOW SHRINK [<lines>] */
+static void cmd_window_shrink(const char *data)
+{
+ MAIN_WINDOW_REC *window;
+ int count;
+
+ count = *data == '\0' ? 1 : atoi(data);
+ window = WINDOW_GUI(active_win)->parent;
+
+ if (!mainwindow_shrink(window, count)) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+ TXT_WINDOW_TOO_SMALL);
+ }
+}
+
+/* SYNTAX: WINDOW SIZE <lines> */
+static void cmd_window_size(const char *data)
+{
+ char sizestr[MAX_INT_STRLEN];
+ int size;
+
+ if (!is_numeric(data, 0)) return;
+ size = atoi(data);
+
+ size -= WINDOW_GUI(active_win)->parent->height;
+ if (size == 0) return;
+
+ ltoa(sizestr, size < 0 ? -size : size);
+ if (size < 0)
+ cmd_window_shrink(sizestr);
+ else
+ cmd_window_grow(sizestr);
+}
+
+/* SYNTAX: WINDOW BALANCE */
+static void cmd_window_balance(void)
+{
+ GSList *sorted, *tmp;
+ int avail_size, unit_size, bigger_units;
+ int windows, last_line, old_size;
+
+ windows = g_slist_length(mainwindows);
+ if (windows == 1) return;
+
+ avail_size = screen_height - reserved_up-reserved_down;
+ unit_size = avail_size/windows;
+ bigger_units = avail_size%windows;
+
+ sorted = mainwindows_get_sorted(FALSE);
+ last_line = 0;
+ 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;
+
+ if (bigger_units > 0) {
+ rec->last_line++;
+ bigger_units--;
+ }
+ last_line = rec->last_line + rec->statusbar_lines;
+
+ mainwindow_resize(rec, 0, rec->height-old_size);
+ }
+ g_slist_free(sorted);
+
+ mainwindows_redraw();
+ statusbar_redraw(NULL);
+}
+
+/* SYNTAX: WINDOW HIDE [<number>|<name>] */
+static void cmd_window_hide(const char *data)
+{
+ WINDOW_REC *window;
+
+ if (mainwindows->next == NULL) {
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+ TXT_CANT_HIDE_LAST);
+ return;
+ }
+
+ if (*data == '\0')
+ window = active_win;
+ else if (is_numeric(data, 0)) {
+ window = window_find_refnum(atoi(data));
+ if (window == NULL) {
+ printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+ TXT_REFNUM_NOT_FOUND, data);
+ }
+ } else {
+ window = window_find_item(active_win->active_server, data);
+ }
+
+ if (window == NULL || !is_window_visible(window))
+ return;
+
+ if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
+ printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+ TXT_CANT_HIDE_STICKY_WINDOWS);
+ return;
+ }
+
+ mainwindow_destroy(WINDOW_GUI(window)->parent);
+
+ if (active_mainwin == NULL) {
+ active_mainwin = WINDOW_GUI(active_win)->parent;
+ window_set_active(active_mainwin->active);
+ }
+}
+
+/* SYNTAX: WINDOW SHOW <number>|<name> */
+static void cmd_window_show(const char *data)
+{
+ MAIN_WINDOW_REC *parent;
+ WINDOW_REC *window;
+
+ if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+ if (is_numeric(data, '\0')) {
+ window = window_find_refnum(atoi(data));
+ if (window == NULL) {
+ printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+ TXT_REFNUM_NOT_FOUND, data);
+ }
+ } else {
+ window = window_find_item(active_win->active_server, data);
+ }
+
+ if (window == NULL || is_window_visible(window))
+ return;
+
+ if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
+ 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;
+ gui_window_reparent(window, parent);
+
+ active_mainwin = NULL;
+ window_set_active(window);
+}
+
+/* SYNTAX: WINDOW UP */
+static void cmd_window_up(void)
+{
+ MAIN_WINDOW_REC *rec;
+
+ rec = mainwindows_find_upper(active_mainwin->first_line);
+ if (rec != NULL)
+ window_set_active(rec->active);
+}
+
+/* SYNTAX: WINDOW DOWN */
+static void cmd_window_down(void)
+{
+ MAIN_WINDOW_REC *rec;
+
+ rec = mainwindows_find_lower(active_mainwin->last_line);
+ if (rec != NULL)
+ window_set_active(rec->active);
+}
+
+/* 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;
+ }
+ }
+
+ if (window != NULL)
+ window_set_active(window);
+}
+
+/* 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;
+ }
+ }
+
+ if (window != NULL)
+ window_set_active(window);
+}
+
+static void mainwindow_change_window(MAIN_WINDOW_REC *mainwin,
+ WINDOW_REC *window)
+{
+ MAIN_WINDOW_REC *parent;
+ GSList *tmp;
+
+ if (mainwin->sticky_windows != NULL) {
+ /* sticky window */
+ window_set_active(mainwin->sticky_windows->data);
+ return;
+ }
+
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *rec = tmp->data;
+
+ parent = WINDOW_GUI(rec)->parent;
+ if (rec != window &&
+ g_slist_find(parent->sticky_windows, rec) == NULL) {
+ window_set_active(rec);
+ return;
+ }
+ }
+
+ /* no more non-sticky windows, remove main window */
+ mainwindow_destroy(mainwin);
+}
+
+/* SYNTAX: WINDOW STICK [ON|OFF|<ref#>] */
+static void cmd_window_stick(const char *data)
+{
+ MAIN_WINDOW_REC *window = active_mainwin;
+
+ if (is_numeric(data, '\0')) {
+ WINDOW_REC *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;
+ }
+
+ if (g_strncasecmp(data, "OF", 2) == 0 || toupper(*data) == 'N') {
+ /* unset sticky */
+ if (g_slist_find(window->sticky_windows, active_win) == NULL) {
+ printformat_window(active_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,
+ 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);
+ }
+
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+ TXT_WINDOW_SET_STICKY);
+ }
+}
+
+void mainwindows_init(void)
+{
+ screen_width = COLS;
+ screen_height = LINES;
+
+ mainwindows = NULL;
+ active_mainwin = NULL;
+ reserved_up = reserved_down = 0;
+
+ /* for entry line */
+ mainwindows_reserve_lines(1, FALSE);
+
+ command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow);
+ command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink);
+ command_bind("window size", NULL, (SIGNAL_FUNC) cmd_window_size);
+ command_bind("window balance", NULL, (SIGNAL_FUNC) cmd_window_balance);
+ command_bind("window hide", NULL, (SIGNAL_FUNC) cmd_window_hide);
+ command_bind("window show", NULL, (SIGNAL_FUNC) cmd_window_show);
+ command_bind("window up", NULL, (SIGNAL_FUNC) cmd_window_up);
+ command_bind("window down", NULL, (SIGNAL_FUNC) cmd_window_down);
+ 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);
+}
+
+void mainwindows_deinit(void)
+{
+ while (mainwindows != NULL)
+ mainwindow_destroy(mainwindows->data);
+
+ command_unbind("window grow", (SIGNAL_FUNC) cmd_window_grow);
+ command_unbind("window shrink", (SIGNAL_FUNC) cmd_window_shrink);
+ command_unbind("window size", (SIGNAL_FUNC) cmd_window_size);
+ command_unbind("window balance", (SIGNAL_FUNC) cmd_window_balance);
+ command_unbind("window hide", (SIGNAL_FUNC) cmd_window_hide);
+ command_unbind("window show", (SIGNAL_FUNC) cmd_window_show);
+ command_unbind("window up", (SIGNAL_FUNC) cmd_window_up);
+ command_unbind("window down", (SIGNAL_FUNC) cmd_window_down);
+ 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);
+}
--- /dev/null
+#ifndef __MAINWINDOWS_H
+#define __MAINWINDOWS_H
+
+#include "fe-windows.h"
+#include "screen.h"
+
+#define WINDOW_MIN_SIZE 2
+
+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;
+} MAIN_WINDOW_REC;
+
+extern GSList *mainwindows;
+extern MAIN_WINDOW_REC *active_mainwin;
+
+void mainwindows_init(void);
+void mainwindows_deinit(void);
+
+MAIN_WINDOW_REC *mainwindow_create(void);
+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);
+void mainwindows_resize(int width, int height);
+
+int mainwindows_reserve_lines(int count, int up);
+GSList *mainwindows_get_sorted(int reverse);
+
+#endif
--- /dev/null
+/*
+ module-formats.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
+*/
+
+#include "module.h"
+#include "formats.h"
+
+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_start", "{hilight Lastlog}:", 0 },
+ { "lastlog_end", "{hilight End of Lastlog}", 0 },
+
+ { "refnum_not_found", "Window number $0 not found", 1, { 0 } },
+ { "window_too_small", "Not enough room to resize this window", 0 },
+ { "cant_hide_last", "You can't hide the last window", 0 },
+ { "cant_hide_sticky_windows", "You can't hide sticky windows (use /WINDOW STICK OFF)", 0 },
+ { "cant_show_sticky_windows", "You can't show sticky windows (use /WINDOW STICK OFF)", 0 },
+ { "window_not_sticky", "Window is not sticky", 0 },
+ { "window_set_sticky", "Window set sticky", 0 },
+ { "window_unset_sticky", "Window is not sticky anymore", 0 },
+
+ { NULL, NULL, 0 }
+};
--- /dev/null
+#include "formats.h"
+
+enum {
+ TXT_MODULE_NAME,
+
+ TXT_LASTLOG_TOO_LONG,
+ TXT_LASTLOG_START,
+ TXT_LASTLOG_END,
+
+ TXT_REFNUM_NOT_FOUND,
+ TXT_WINDOW_TOO_SMALL,
+ TXT_CANT_HIDE_LAST,
+ TXT_CANT_HIDE_STICKY_WINDOWS,
+ TXT_CANT_SHOW_STICKY_WINDOWS,
+ TXT_WINDOW_NOT_STICKY,
+ TXT_WINDOW_SET_STICKY,
+ TXT_WINDOW_UNSET_STICKY
+};
+
+extern FORMAT_REC gui_text_formats[];
--- /dev/null
+#include "common.h"
+
+#define MODULE_NAME "fe-text"
+
+extern int quitting;
+void irssi_redraw(void);
--- /dev/null
+/*
+ screen.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 "settings.h"
+
+#include "screen.h"
+#include "gui-readline.h"
+#include "mainwindows.h"
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <signal.h>
+
+#ifndef COLOR_PAIRS
+#define COLOR_PAIRS 64
+#endif
+
+#define MIN_SCREEN_WIDTH 20
+
+static int scrx, scry;
+static int use_colors;
+static int freeze_refresh;
+
+static int init_screen_int(void);
+static void deinit_screen_int(void);
+
+#ifdef SIGWINCH
+
+static void sig_winch(int p)
+{
+#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM)
+ struct winsize ws;
+
+ /* Get new window size */
+ if (ioctl(0, TIOCGWINSZ, &ws) < 0)
+ return;
+
+ if (ws.ws_row == LINES && ws.ws_col == COLS) {
+ /* Same size, abort. */
+ return;
+ }
+
+ if (ws.ws_col < MIN_SCREEN_WIDTH)
+ ws.ws_col = MIN_SCREEN_WIDTH;
+
+ /* Resize curses terminal */
+ resizeterm(ws.ws_row, ws.ws_col);
+#else
+ deinit_screen_int();
+ init_screen_int();
+ mainwindows_recreate();
+#endif
+
+ mainwindows_resize(COLS, LINES);
+}
+#endif
+
+static void read_signals(void)
+{
+#ifndef WIN32
+ int signals[] = {
+ SIGHUP, SIGINT, SIGQUIT, SIGTERM,
+ SIGALRM, SIGUSR1, SIGUSR2
+ };
+ 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);
+ }
+#endif
+}
+
+static void read_settings(void)
+{
+ int old_colors = use_colors;
+
+ use_colors = settings_get_bool("colors");
+ read_signals();
+ if (use_colors && !has_colors())
+ use_colors = FALSE;
+
+ if (use_colors != old_colors)
+ irssi_redraw();
+}
+
+static int init_curses(void)
+{
+ char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+ int num;
+#ifndef WIN32
+ struct sigaction act;
+#endif
+
+ if (!initscr())
+ return FALSE;
+
+ if (COLS < MIN_SCREEN_WIDTH)
+ COLS = MIN_SCREEN_WIDTH;
+
+#ifdef SIGWINCH
+ sigemptyset (&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = sig_winch;
+ sigaction(SIGWINCH, &act, NULL);
+#endif
+ raw(); noecho(); idlok(stdscr, 1);
+#ifdef HAVE_CURSES_IDCOK
+ idcok(stdscr, 1);
+#endif
+ intrflush(stdscr, FALSE); nodelay(stdscr, TRUE);
+
+ if (has_colors())
+ start_color();
+ else if (use_colors)
+ 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 init_screen_int(void)
+{
+ use_colors = settings_get_bool("colors");
+ read_signals();
+
+ scrx = scry = 0;
+ freeze_refresh = 0;
+
+ return init_curses();
+}
+
+static void deinit_screen_int(void)
+{
+ endwin();
+}
+
+/* Initialize screen, detect screen length */
+int init_screen(void)
+{
+ settings_add_bool("lookandfeel", "colors", TRUE);
+ settings_add_str("misc", "ignore_signals", "");
+ signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+ return init_screen_int();
+}
+
+/* Deinitialize screen */
+void deinit_screen(void)
+{
+ deinit_screen_int();
+ signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void set_color(WINDOW *window, int col)
+{
+ int attr;
+
+ if (!use_colors)
+ attr = (col & 0x70) ? A_REVERSE : 0;
+ else if (col & ATTR_COLOR8)
+ attr = (A_DIM | COLOR_PAIR(63));
+ else if ((col & 0x77) == 0)
+ attr = A_NORMAL;
+ else
+ attr = (COLOR_PAIR((col&7) + (col&0x70)/2));
+
+ if (col & 0x08) attr |= A_BOLD;
+ if (col & 0x80) attr |= A_BLINK;
+
+ if (col & ATTR_UNDERLINE) attr |= A_UNDERLINE;
+ if (col & ATTR_REVERSE) attr |= A_REVERSE;
+
+ wattrset(window, attr);
+}
+
+void set_bg(WINDOW *window, int col)
+{
+ int attr;
+
+ if (!use_colors)
+ attr = (col & 0x70) ? A_REVERSE : 0;
+ else {
+ attr = (col == 8) ?
+ (A_DIM | COLOR_PAIR(63)) :
+ (COLOR_PAIR((col&7) + (col&0x70)/2));
+ }
+
+ if (col & 0x08) attr |= A_BOLD;
+ if (col & 0x80) attr |= A_BLINK;
+
+ wbkgdset(window, ' ' | attr);
+}
+
+void move_cursor(int y, int x)
+{
+ scry = y;
+ scrx = x;
+}
+
+void screen_refresh_freeze(void)
+{
+ freeze_refresh++;
+}
+
+void screen_refresh_thaw(void)
+{
+ if (freeze_refresh > 0) {
+ freeze_refresh--;
+ if (freeze_refresh == 0) screen_refresh(NULL);
+ }
+}
+
+void screen_refresh(WINDOW *window)
+{
+ if (window != NULL)
+ wnoutrefresh(window);
+ if (freeze_refresh == 0) {
+ move(scry, scrx);
+ wnoutrefresh(stdscr);
+ doupdate();
+ }
+}
--- /dev/null
+#ifndef __SCREEN_H
+#define __SCREEN_H
+
+#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+# include <ncurses.h>
+#else
+# include <curses.h>
+#endif
+
+/* Some curseses include term.h, which #defines some things breaking irssi */
+#undef lines
+#undef key_backspace
+#undef tab
+
+#define ATTR_UNDERLINE 0x100
+#define ATTR_COLOR8 0x200
+#define ATTR_REVERSE 0x400
+
+/* XXX I hope this could be integrated into BX.
+ * XXX Well, this should be done via libc,
+ * but FreeBSD libc support is quite LAME.
+ * Macro below are copied from lynx.
+ *
+ * clive@FreeBSD.org
+ */
+#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 WANT_BIG5
+
+int init_screen(void); /* Initialize screen, detect screen length */
+void deinit_screen(void); /* Deinitialize screen */
+
+void set_color(WINDOW *window, int col);
+void set_bg(WINDOW *window, int col);
+
+void move_cursor(int y, int x);
+
+void screen_refresh_freeze(void);
+void screen_refresh_thaw(void);
+void screen_refresh(WINDOW *window);
+
+#endif
--- /dev/null
+/*
+ irssi.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 "module-formats.h"
+#include "args.h"
+#include "signals.h"
+#include "levels.h"
+#include "core.h"
+#include "settings.h"
+
+#include "printtext.h"
+#include "fe-common-core.h"
+#include "fe-common-silc.h"
+#include "themes.h"
+
+#include "screen.h"
+#include "gui-entry.h"
+#include "mainwindows.h"
+#include "gui-printtext.h"
+#include "gui-readline.h"
+#include "statusbar.h"
+#include "gui-windows.h"
+
+#include <signal.h>
+
+#ifdef HAVE_STATIC_PERL
+void perl_core_init(void);
+void perl_core_deinit(void);
+
+void fe_perl_init(void);
+void fe_perl_deinit(void);
+#endif
+
+void silc_init(void);
+void silc_deinit(void);
+
+void gui_expandos_init(void);
+void gui_expandos_deinit(void);
+
+void textbuffer_commands_init(void);
+void textbuffer_commands_deinit(void);
+
+void lastlog_init(void);
+void lastlog_deinit(void);
+
+void mainwindow_activity_init(void);
+void mainwindow_activity_deinit(void);
+
+void mainwindows_save_init(void);
+void mainwindows_save_deinit(void);
+
+static GMainLoop *main_loop;
+int quitting;
+
+static const char *firsttimer_text =
+ "Looks like this is the first time you run irssi.\n"
+ "This is just a reminder that you really should go read\n"
+ "startup-HOWTO if you haven't already. Irssi's default\n"
+ "settings aren't probably what you've used to, and you\n"
+ "shouldn't judge the whole client as crap based on them.\n\n"
+ "You can find startup-HOWTO and more irssi beginner info at\n"
+ "http://irssi.org/beginner/";
+static int display_firsttimer = FALSE;
+
+
+static void sig_exit(void)
+{
+ g_main_quit(main_loop);
+}
+
+/* redraw irssi's screen.. */
+void irssi_redraw(void)
+{
+ clear();
+ refresh();
+
+ /* windows */
+ mainwindows_redraw();
+ /* statusbar */
+ statusbar_redraw(NULL);
+ /* entry line */
+ gui_entry_redraw();
+}
+
+static void textui_init(void)
+{
+ static struct poptOption options[] = {
+ POPT_AUTOHELP
+ { NULL, '\0', 0, NULL }
+ };
+
+ args_register(options);
+
+ irssi_gui = IRSSI_GUI_TEXT;
+ core_init();
+ silc_init();
+ fe_common_core_init();
+ fe_common_silc_init();
+
+ theme_register(gui_text_formats);
+ signal_add("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();
+
+ settings_check();
+
+ fe_common_core_finish_init();
+
+#ifdef HAVE_STATIC_PERL
+ perl_core_init();
+ fe_perl_init();
+#endif
+ signal_emit("irssi init finished", 0);
+
+ if (display_firsttimer) {
+ printtext_window(active_win, MSGLEVEL_CLIENTNOTICE,
+ "%s", firsttimer_text);
+ }
+
+ screen_refresh_thaw();
+}
+
+static void textui_deinit(void)
+{
+ quitting = TRUE;
+ signal(SIGINT, SIG_DFL);
+
+ screen_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();
+#endif
+
+ theme_unregister();
+
+ fe_common_silc_deinit();
+ fe_common_core_deinit();
+ silc_deinit();
+ core_deinit();
+}
+
+static void check_oldcrap(void)
+{
+ FILE *f;
+ char *path, str[256];
+ int found;
+
+ /* check that default.theme is up-to-date */
+ path = g_strdup_printf("%s/.irssi/default.theme", g_get_home_dir());
+ f = fopen(path, "r+");
+ if (f == NULL) {
+ g_free(path);
+ return;
+ }
+ found = FALSE;
+ while (!found && fgets(str, sizeof(str), f) != NULL)
+ found = strstr(str, "abstracts = ") != NULL;
+ fclose(f);
+
+ if (found) {
+ g_free(path);
+ return;
+ }
+
+ printf("\nYou seem to have old default.theme in ~/.irssi/ directory.\n");
+ printf("Themeing system has changed a bit since last irssi release,\n");
+ printf("you should either delete your old default.theme or manually\n");
+ printf("merge it with the new default.theme.\n\n");
+ printf("Do you want to delete the old theme now? (Y/n)\n");
+
+ str[0] = '\0';
+ fgets(str, sizeof(str), stdin);
+ if (toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0')
+ remove(path);
+ g_free(path);
+}
+
+static void check_files(void)
+{
+ struct stat statbuf;
+ char *path;
+
+ path = g_strdup_printf("%s/.irssi", g_get_home_dir());
+ if (stat(path, &statbuf) != 0) {
+ /* ~/.irssi doesn't exist, first time running irssi */
+ display_firsttimer = TRUE;
+ } else {
+ check_oldcrap();
+ }
+ g_free(path);
+}
+
+#ifdef WIN32
+static void winsock_init(void)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 2);
+
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) {
+ printf("Error initializing winsock\n");
+ exit(1);
+ }
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ check_files();
+#ifdef WIN32
+ winsock_init();
+#endif
+#ifdef HAVE_SOCKS
+ SOCKSinit(argv[0]);
+#endif
+#ifdef ENABLE_NLS
+ /* initialize the i18n stuff */
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ textui_init();
+ args_execute(argc, argv);
+
+ if (!init_screen())
+ g_error("Can't initialize screen handling, quitting.\n");
+
+ textui_finish_init();
+ main_loop = g_main_new(TRUE);
+ g_main_run(main_loop);
+ g_main_destroy(main_loop);
+ textui_deinit();
+
+#ifdef MEM_DEBUG
+ ig_mem_profile();
+#endif
+
+ return 0;
+}
--- /dev/null
+/*
+ statusbar-items.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 "signals.h"
+#include "misc.h"
+#include "settings.h"
+#include "special-vars.h"
+
+#include "window-items.h"
+#include "formats.h"
+
+#include "statusbar.h"
+#include "gui-printtext.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;
+
+/* 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)
+{
+ 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;
+
+ t = time(NULL);
+ tm = localtime(&t);
+
+ if (tm->tm_min != min) {
+ /* minute changed, redraw! */
+ clock_last = t;
+ statusbar_item_redraw(clock_item);
+ }
+ return 1;
+}
+
+/* redraw nick */
+static void statusbar_nick(SBAR_ITEM_REC *item, int get_size_only)
+{
+ item_default(item, get_size_only,
+ "{sb $P$N{sbmode $usermode}{sbaway $A}}", "");
+}
+
+static void sig_statusbar_nick_redraw(void)
+{
+ statusbar_item_redraw(nick_item);
+}
+
+/* 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}}", "");
+ }
+}
+
+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)
+{
+ GString *str;
+ GList *tmp;
+ char *ret;
+ int is_det;
+
+ str = g_string_new(NULL);
+
+ for (tmp = activity_list; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *window = tmp->data;
+
+ is_det = window->data_level >= DATA_LEVEL_HILIGHT;
+ if ((!is_det && !normal) || (is_det && !hilight))
+ continue;
+
+ g_string_append(str, "%c");
+ if (str->len > 2)
+ g_string_append_c(str, ',');
+
+ switch (window->data_level) {
+ case DATA_LEVEL_NONE:
+ case DATA_LEVEL_TEXT:
+ break;
+ case DATA_LEVEL_MSG:
+ g_string_append(str, "%W");
+ break;
+ default:
+ g_string_append(str, window->hilight_color == NULL ?
+ "%M" : window->hilight_color);
+ break;
+ }
+ g_string_sprintfa(str, "%d", window->refnum);
+
+ /* make sure the background is returned to default */
+ g_string_append(str, "%n");
+ }
+
+ ret = str->len == 0 ? NULL : str->str;
+ g_string_free(str, ret == NULL);
+ return ret;
+}
+
+/* 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)
+{
+ char *actlist, *detlist, *data;
+
+ 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) {
+ 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);
+
+ g_free_not_null(actlist);
+ g_free_not_null(detlist);
+}
+
+static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel)
+{
+ GList *tmp;
+ int inspos;
+
+ g_return_if_fail(window != NULL);
+
+ if (settings_get_bool("actlist_moves")) {
+ /* Move the window to the first in the activity list */
+ if (g_list_find(activity_list, window) != NULL)
+ 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);
+ return;
+ }
+
+ if (g_list_find(activity_list, window) != NULL) {
+ /* already in activity list */
+ if (window->data_level == 0) {
+ /* remove from activity list */
+ activity_list = g_list_remove(activity_list, window);
+ statusbar_item_redraw(activity_item);
+ } 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);
+ }
+ return;
+ }
+
+ if (window->data_level == 0)
+ return;
+
+ /* add window to activity list .. */
+ inspos = 0;
+ for (tmp = activity_list; tmp != NULL; tmp = tmp->next, inspos++) {
+ WINDOW_REC *rec = tmp->data;
+
+ if (window->refnum < rec->refnum) {
+ activity_list =
+ g_list_insert(activity_list, window, inspos);
+ break;
+ }
+ }
+ if (tmp == NULL)
+ activity_list = g_list_append(activity_list, window);
+
+ statusbar_item_redraw(activity_item);
+}
+
+static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window)
+{
+ g_return_if_fail(window != NULL);
+
+ if (g_list_find(activity_list, window) != NULL)
+ activity_list = g_list_remove(activity_list, window);
+ statusbar_item_redraw(activity_item);
+}
+
+static void sig_statusbar_activity_updated(void)
+{
+ statusbar_item_redraw(activity_item);
+}
+
+/* redraw -- more -- */
+static void statusbar_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;
+
+ if (more_item != NULL && WINDOW_GUI(window)->view->bottom) {
+ statusbar_item_remove(more_item);
+ more_item = NULL;
+ }
+}
+
+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 (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);
+}
+
+static void sig_statusbar_lag_redraw(void)
+{
+ statusbar_item_redraw(lag_item);
+}
+
+static int statusbar_lag_timeout(void)
+{
+ /* refresh statusbar every 10 seconds */
+ if (time(NULL)-lag_last_draw < LAG_REFRESH_TIME)
+ return 1;
+
+ statusbar_item_redraw(lag_item);
+ return 1;
+}
+
+/* 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;
+ 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;
+ }
+
+ 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--;
+ }
+ }
+
+ fclose(f);
+ mail_last_count = count;
+ return count;
+}
+
+static void statusbar_mail(SBAR_ITEM_REC *item, int get_size_only)
+{
+ char countstr[MAX_INT_STRLEN];
+ int mail_count;
+
+ mail_count = settings_get_bool("mail_counter") ? get_mail_count() : 0;
+
+ if (mail_count <= 0) {
+ 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;
+
+ 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);
+ }
+}
+
+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);
+}
+
+static void topicbar_destroy(void)
+{
+ if (topic_bar == NULL)
+ return;
+
+ statusbar_destroy(topic_bar);
+ topic_item = NULL;
+ topic_bar = NULL;
+
+ 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);
+}
+
+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);
+}
+
+static void mainbar_add_items(MAIN_WINDOW_REC *window)
+{
+ 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);
+}
+
+static void sidebar_add_items(MAIN_WINDOW_REC *window)
+{
+ window->statusbar_window_item =
+ statusbar_item_create(window->statusbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window);
+}
+
+static void sidebar_remove_items(MAIN_WINDOW_REC *window)
+{
+ if (window->statusbar_window_item != NULL) {
+ statusbar_item_remove(window->statusbar_window_item);
+ window->statusbar_window_item = NULL;
+ }
+}
+
+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);
+}
+
+static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window)
+{
+ if (window == mainbar_window) {
+ mainbar = NULL;
+ mainbar_window = NULL;
+ }
+
+ if (window->statusbar != NULL)
+ statusbar_destroy(window->statusbar);
+}
+
+static void sig_main_statusbar_changed(WINDOW_REC *window)
+{
+ MAIN_WINDOW_REC *parent;
+
+ if (window == NULL)
+ return;
+
+ parent = WINDOW_GUI(window)->parent;
+ if (mainbar == parent->statusbar)
+ return;
+
+ if (mainbar != NULL) {
+ mainbar_remove_items();
+ sidebar_add_items(mainbar_window);
+ }
+ 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);
+}
+
+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 */
+ 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);
+
+ 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);
+}
+
+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 */
+ 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);
+}
--- /dev/null
+/*
+ statusbar.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 "signals.h"
+
+#include "themes.h"
+
+#include "statusbar.h"
+#include "gui-windows.h"
+
+static int backs[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* FIXME: should be in some more generic place.. */
+
+void statusbar_items_init(void);
+void statusbar_items_deinit(void);
+
+static GSList *statusbars;
+static int sbar_uppest, sbar_lowest, sbars_up, sbars_down;
+
+static void statusbar_item_destroy(SBAR_ITEM_REC *rec)
+{
+ rec->bar->items = g_slist_remove(rec->bar->items, rec);
+ g_free(rec);
+}
+
+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;
+}
+
+static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
+{
+ GSList *tmp;
+
+ for (tmp = items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ size -= (rec->max_size-rec->min_size);
+ rec->size = rec->min_size;
+
+ if (size <= max_width) {
+ rec->size += max_width-size;
+ break;
+ }
+
+ if (rec->size == 0) {
+ /* min_size was 0, item removed.
+ remove the marginal too */
+ size--;
+ }
+ }
+
+ return size;
+}
+
+static void statusbar_shrink_forced(GSList *items, int size, int max_width)
+{
+ GSList *tmp;
+
+ for (tmp = items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ if (size-rec->size > max_width) {
+ /* remove the whole item */
+ size -= rec->size+1; /* +1 == the marginal */
+ rec->size = 0;
+ } else {
+ /* shrink the item */
+ rec->size -= size-max_width;
+ break;
+ }
+ }
+}
+
+static void statusbar_get_sizes(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 */
+ for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ rec->func(rec, TRUE);
+ rec->size = rec->max_size;
+
+ if (rec->size > 0) {
+ /* +1 == marginal between items */
+ width += rec->max_size+1;
+
+ prior_sorted = g_slist_insert_sorted(prior_sorted, rec,
+ (GCompareFunc)
+ sbar_item_cmp);
+ }
+ }
+
+ if (width > max_width) {
+ /* too big, start shrinking from items with lowest priority
+ and shrink until everything fits or until we've shrinked
+ all items. */
+ width = statusbar_shrink_to_min(prior_sorted, width,
+ max_width);
+ if (width > max_width) {
+ /* still need to shrink, remove the items with lowest
+ priority until everything fits to screen */
+ statusbar_shrink_forced(prior_sorted, width,
+ max_width);
+ }
+ }
+
+ g_slist_free(prior_sorted);
+}
+
+static void statusbar_redraw_line(STATUSBAR_REC *bar)
+{
+ WINDOW_REC *old_active_win;
+ GSList *tmp;
+ int xpos, rxpos;
+
+ old_active_win = active_win;
+ if (bar->window != NULL)
+ active_win = bar->window->active;
+
+ statusbar_get_sizes(bar, COLS-2);
+
+ xpos = 1;
+ 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);
+ }
+ }
+
+ rxpos = COLS-1;
+ 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);
+ }
+ }
+
+ active_win = old_active_win;
+}
+
+static void statusbar_redraw_all(void)
+{
+ screen_refresh_freeze();
+ g_slist_foreach(statusbars, (GFunc) statusbar_redraw, NULL);
+ screen_refresh_thaw();
+}
+
+STATUSBAR_REC *statusbar_find(int pos, int line)
+{
+ GSList *tmp;
+
+ for (tmp = statusbars; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_REC *rec = tmp->data;
+
+ if (rec->pos == pos && rec->line == line)
+ return rec;
+ }
+
+ return NULL;
+}
+
+void statusbar_redraw(STATUSBAR_REC *bar)
+{
+ if (bar == NULL) {
+ statusbar_redraw_all();
+ return;
+ }
+
+ set_bg(stdscr, backs[bar->color] << 4);
+ move(bar->ypos, 0); clrtoeol();
+ set_bg(stdscr, 0);
+
+ statusbar_redraw_line(bar);
+
+ screen_refresh(NULL);
+}
+
+void statusbar_item_redraw(SBAR_ITEM_REC *item)
+{
+ g_return_if_fail(item != NULL);
+
+ item->func(item, TRUE);
+ if (item->max_size != item->size)
+ statusbar_redraw(item->bar);
+ else {
+ item->func(item, FALSE);
+ screen_refresh(NULL);
+ }
+}
+
+static int get_last_bg(const char *str)
+{
+ int last = -1;
+
+ while (*str != '\0') {
+ if (*str == '%' && str[1] != '\0') {
+ str++;
+ if (*str >= '0' && *str <= '7')
+ last = *str-'0';
+ }
+ str++;
+ }
+
+ return last;
+}
+
+/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */
+STATUSBAR_REC *statusbar_create(int pos, int ypos)
+{
+ STATUSBAR_REC *rec;
+ char *str;
+
+ rec = g_new0(STATUSBAR_REC, 1);
+ statusbars = g_slist_append(statusbars, rec);
+
+ 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;
+
+ /* 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);
+
+ rec->color = get_last_bg(rec->color_string);
+ if (rec->color < 0) rec->color = current_theme->default_real_color;
+
+ 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;
+ }
+
+ set_bg(stdscr, backs[rec->color] << 4);
+ move(rec->ypos, 0); clrtoeol();
+ set_bg(stdscr, 0);
+
+ return rec;
+}
+
+static void statusbars_pack(int pos, int line)
+{
+ GSList *tmp;
+
+ for (tmp = statusbars; 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);
+ }
+ }
+}
+
+void statusbar_destroy(STATUSBAR_REC *bar)
+{
+ g_return_if_fail(bar != NULL);
+
+ if (bar->pos != STATUSBAR_POS_MIDDLE)
+ mainwindows_reserve_lines(-1, bar->pos == STATUSBAR_POS_UP);
+
+ if (bar->pos == STATUSBAR_POS_UP) sbars_up--;
+ if (bar->pos == STATUSBAR_POS_DOWN) sbars_down--;
+ statusbars = g_slist_remove(statusbars, bar);
+
+ while (bar->items != NULL)
+ statusbar_item_destroy(bar->items->data);
+
+ if (bar->pos != STATUSBAR_POS_MIDDLE)
+ statusbars_pack(bar->pos, bar->pos);
+ g_free(bar->color_string);
+ g_free(bar);
+
+ if (!quitting) statusbar_redraw_all();
+}
+
+SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
+ int priority, int right_justify,
+ STATUSBAR_FUNC func)
+{
+ SBAR_ITEM_REC *rec;
+
+ g_return_val_if_fail(bar != NULL, NULL);
+ g_return_val_if_fail(func != 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;
+
+ return rec;
+}
+
+void statusbar_item_remove(SBAR_ITEM_REC *item)
+{
+ g_return_if_fail(item != NULL);
+
+ statusbar_item_destroy(item);
+ if (!quitting) statusbar_redraw_all();
+}
+
+static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
+{
+ STATUSBAR_REC *rec;
+
+ rec = window->statusbar;
+ rec->ypos = window->first_line+window->height;
+}
+
+void statusbar_init(void)
+{
+ statusbars = NULL;
+ sbars_up = sbars_down = 0;
+
+ statusbar_items_init();
+ signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+}
+
+void statusbar_deinit(void)
+{
+ statusbar_items_deinit();
+
+ while (statusbars != NULL)
+ statusbar_destroy(statusbars->data);
+
+ signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+}
--- /dev/null
+#ifndef __STATUSBAR_H
+#define __STATUSBAR_H
+
+#include "mainwindows.h"
+
+#define SBAR_PRIORITY_HIGH 100
+#define SBAR_PRIORITY_NORMAL 0
+#define SBAR_PRIORITY_LOW -100
+
+enum {
+ STATUSBAR_POS_UP,
+ STATUSBAR_POS_MIDDLE,
+ STATUSBAR_POS_DOWN
+};
+
+typedef struct SBAR_ITEM_REC SBAR_ITEM_REC;
+typedef void (*STATUSBAR_FUNC) (SBAR_ITEM_REC *item, int get_size_only);
+
+typedef struct {
+ MAIN_WINDOW_REC *window;
+
+ int pos;
+ int line;
+
+ char *color_string;
+ int color;
+
+ int ypos; /* real position in screen at the moment */
+ GSList *items;
+} STATUSBAR_REC;
+
+struct SBAR_ITEM_REC {
+ STATUSBAR_REC *bar;
+ STATUSBAR_FUNC func;
+
+ /* what item wants */
+ int priority;
+ int min_size, max_size;
+ unsigned int right_justify:1;
+
+ /* what item gets */
+ int xpos, size;
+};
+
+/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */
+STATUSBAR_REC *statusbar_create(int pos, int ypos);
+void statusbar_destroy(STATUSBAR_REC *bar);
+
+STATUSBAR_REC *statusbar_find(int pos, int line);
+
+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);
+
+/* redraw statusbar, NULL = all */
+void statusbar_redraw(STATUSBAR_REC *bar);
+void statusbar_item_redraw(SBAR_ITEM_REC *item);
+
+void statusbar_init(void);
+void statusbar_deinit(void);
+
+#endif
--- /dev/null
+/*
+ textbuffer-commands.c : Text buffer 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 "commands.h"
+#include "misc.h"
+#include "levels.h"
+
+#include "printtext.h"
+#include "gui-windows.h"
+
+/* SYNTAX: CLEAR */
+static void cmd_clear(const char *data)
+{
+ GHashTable *optlist;
+ void *free_arg;
+ GSList *tmp;
+
+ g_return_if_fail(data != NULL);
+
+ if (!cmd_get_params(data, &free_arg, PARAM_FLAG_OPTIONS,
+ "clear", &optlist)) return;
+
+ if (g_hash_table_lookup(optlist, "all") == NULL) {
+ /* clear active window */
+ textbuffer_view_clear(WINDOW_GUI(active_win)->view);
+ } else {
+ /* clear all windows */
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *window = tmp->data;
+
+ textbuffer_view_clear(WINDOW_GUI(window)->view);
+ }
+ }
+
+ cmd_params_free(free_arg);
+}
+
+static void cmd_scrollback(const char *data, SERVER_REC *server,
+ WI_ITEM_REC *item)
+{
+ command_runsub("scrollback", data, server, item);
+}
+
+/* SYNTAX: SCROLLBACK CLEAR */
+static void cmd_scrollback_clear(void)
+{
+ textbuffer_view_remove_all_lines(WINDOW_GUI(active_win)->view);
+}
+
+static void scrollback_goto_line(int linenum)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+
+ view = WINDOW_GUI(active_win)->view;
+ if (view->buffer->lines_count == 0)
+ return;
+
+ textbuffer_view_scroll_line(view, view->buffer->lines->data);
+ gui_window_scroll(active_win, linenum);
+}
+
+static void scrollback_goto_time(const char *datearg, const char *timearg)
+{
+ GList *tmp;
+ struct tm tm;
+ time_t now, stamp;
+ int day, month;
+
+ /* [dd[.mm] | -<days ago>] hh:mi[:ss] */
+ now = stamp = time(NULL);
+ if (*datearg == '-') {
+ /* -<days ago> */
+ stamp -= atoi(datearg+1) * 3600*24;
+ memcpy(&tm, localtime(&stamp), sizeof(struct tm));
+ } else if (*timearg != '\0') {
+ /* dd[.mm] */
+ memcpy(&tm, localtime(&stamp), sizeof(struct tm));
+
+ day = month = 0;
+ sscanf(datearg, "%d.%d", &day, &month);
+ if (day <= 0) return;
+
+ if (month <= 0) {
+ /* month not given */
+ if (day > tm.tm_mday) {
+ /* last month's day */
+ if (tm.tm_mon > 0)
+ tm.tm_mon--;
+ else {
+ /* last year's day.. */
+ tm.tm_year--;
+ tm.tm_mon = 11;
+ }
+ }
+ } else {
+ month--;
+ if (month > tm.tm_mon)
+ tm.tm_year--;
+ tm.tm_mon = month;
+ }
+
+ tm.tm_mday = day;
+ stamp = mktime(&tm);
+ }
+ else
+ {
+ /* only time given, move it to timearg */
+ timearg = datearg;
+ }
+
+ /* hh:mi[:ss] */
+ memcpy(&tm, localtime(&stamp), sizeof(struct tm));
+ tm.tm_sec = 0;
+ sscanf(timearg, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+ stamp = mktime(&tm);
+
+ if (stamp > now && timearg == datearg) {
+ /* we used /SB GOTO 23:59 or something, we want to jump to
+ previous day's 23:59 time instead of into future. */
+ stamp -= 3600*24;
+ }
+
+ if (stamp > now) {
+ /* we're still looking into future, don't bother checking */
+ return;
+ }
+
+ /* 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);
+ break;
+ }
+ }
+}
+
+/* SYNTAX: SCROLLBACK GOTO <+|-linecount>|<linenum>|<timestamp> */
+static void cmd_scrollback_goto(const char *data)
+{
+ char *datearg, *timearg;
+ void *free_arg;
+ int lines;
+
+ if (!cmd_get_params(data, &free_arg, 2, &datearg, &timearg))
+ return;
+
+ if (*timearg == '\0' && (*datearg == '-' || *datearg == '+')) {
+ /* go forward/backward n lines */
+ lines = atoi(datearg + (*datearg == '-' ? 0 : 1));
+ gui_window_scroll(active_win, lines);
+ } else if (*timearg == '\0' && is_numeric(datearg, '\0')) {
+ /* go to n'th line. */
+ scrollback_goto_line(atoi(datearg));
+ } else {
+ /* should be timestamp */
+ scrollback_goto_time(datearg, timearg);
+ }
+
+ cmd_params_free(free_arg);
+}
+
+/* SYNTAX: SCROLLBACK HOME */
+static void cmd_scrollback_home(const char *data)
+{
+ TEXT_BUFFER_REC *buffer;
+
+ buffer = WINDOW_GUI(active_win)->view->buffer;
+ if (buffer->lines_count > 0)
+ gui_window_scroll_line(active_win, buffer->lines->data);
+}
+
+/* SYNTAX: SCROLLBACK END */
+static void cmd_scrollback_end(const char *data)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+
+ view = WINDOW_GUI(active_win)->view;
+ if (view->bottom_startline == NULL)
+ return;
+
+ textbuffer_view_scroll_line(view, view->bottom_startline->data);
+ 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;
+
+ 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);
+ }
+
+ gui_window_redraw(active_win);
+ screen_refresh_thaw();
+#endif
+}
+
+static void cmd_scrollback_status(void)
+{
+ GSList *tmp;
+ int window_kb, total_lines, total_kb;
+
+ total_lines = 0; total_kb = 0;
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *window = tmp->data;
+ TEXT_BUFFER_VIEW_REC *view;
+
+ view = WINDOW_GUI(window)->view;
+
+ window_kb = g_slist_length(view->buffer->text_chunks)*
+ LINE_TEXT_CHUNK_SIZE/1024;
+ total_lines += view->buffer->lines_count;
+ total_kb += window_kb;
+ printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+ "Window %d: %d lines, %dkB of data",
+ window->refnum, view->buffer->lines_count,
+ window_kb);
+ }
+
+ printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+ "Total: %d lines, %dkB of data",
+ total_lines, total_kb);
+}
+
+static void sig_away_changed(SERVER_REC *server)
+{
+ GSList *tmp;
+
+ if (!server->usermode_away)
+ return;
+
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *rec = tmp->data;
+
+ textbuffer_view_set_bookmark_bottom(WINDOW_GUI(rec)->view,
+ "lastlog_last_away");
+ }
+}
+
+void textbuffer_commands_init(void)
+{
+ command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
+ command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback);
+ command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear);
+ command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto);
+ command_bind("scrollback home", NULL, (SIGNAL_FUNC) cmd_scrollback_home);
+ command_bind("scrollback end", NULL, (SIGNAL_FUNC) cmd_scrollback_end);
+ command_bind("scrollback redraw", NULL, (SIGNAL_FUNC) cmd_scrollback_redraw);
+ command_bind("scrollback status", NULL, (SIGNAL_FUNC) cmd_scrollback_status);
+
+ command_set_options("clear", "all");
+
+ signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
+
+void textbuffer_commands_deinit(void)
+{
+ command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
+ command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback);
+ command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear);
+ command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto);
+ command_unbind("scrollback home", (SIGNAL_FUNC) cmd_scrollback_home);
+ command_unbind("scrollback end", (SIGNAL_FUNC) cmd_scrollback_end);
+ command_unbind("scrollback redraw", (SIGNAL_FUNC) cmd_scrollback_redraw);
+ command_unbind("scrollback status", (SIGNAL_FUNC) cmd_scrollback_status);
+
+ signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
--- /dev/null
+/*
+ textbuffer-view.c : Text buffer 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 "textbuffer-view.h"
+#include "screen.h"
+
+typedef struct {
+ char *name;
+ LINE_REC *line;
+} BOOKMARK_REC;
+
+/* how often to scan line cache for lines not accessed for a while (ms) */
+#define LINE_CACHE_CHECK_TIME (5*60*1000)
+/* how long to keep line cache in memory (seconds) */
+#define LINE_CACHE_KEEP_TIME (10*60)
+
+static int linecache_tag;
+static GSList *views;
+
+#define view_is_bottom(view) \
+ ((view)->ypos >= -1 && (view)->ypos < (view)->height)
+
+#define view_get_linecount(view, line) \
+ textbuffer_view_get_line_cache(view, line)->count
+
+static GSList *textbuffer_get_views(TEXT_BUFFER_REC *buffer)
+{
+ GSList *tmp, *list;
+
+ for (tmp = views; tmp != NULL; tmp = tmp->next) {
+ TEXT_BUFFER_VIEW_REC *view = tmp->data;
+
+ if (view->buffer == buffer) {
+ list = g_slist_copy(view->siblings);
+ return g_slist_prepend(list, view);
+ }
+ }
+
+ return NULL;
+}
+
+static TEXT_BUFFER_CACHE_REC *
+textbuffer_cache_get(GSList *views, int width)
+{
+ TEXT_BUFFER_CACHE_REC *cache;
+
+ /* check if there's existing cache with correct width */
+ while (views != NULL) {
+ TEXT_BUFFER_VIEW_REC *view = views->data;
+
+ if (view->width == width) {
+ view->cache->refcount++;
+ return view->cache;
+ }
+ views = views->next;
+ }
+
+ /* create new cache */
+ cache = g_new0(TEXT_BUFFER_CACHE_REC, 1);
+ cache->refcount = 1;
+ cache->width = width;
+ cache->line_cache = g_hash_table_new((GHashFunc) g_direct_hash,
+ (GCompareFunc) g_direct_equal);
+ return cache;
+}
+
+static int line_cache_destroy(void *key, LINE_CACHE_REC *cache)
+{
+ g_free(cache);
+ return TRUE;
+}
+
+static void textbuffer_cache_destroy(TEXT_BUFFER_CACHE_REC *cache)
+{
+ g_hash_table_foreach(cache->line_cache,
+ (GHFunc) line_cache_destroy, NULL);
+ g_hash_table_destroy(cache->line_cache);
+ g_free(cache);
+}
+
+static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache)
+{
+ if (--cache->refcount == 0)
+ textbuffer_cache_destroy(cache);
+}
+
+static LINE_CACHE_REC *
+view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
+{
+ LINE_CACHE_REC *rec;
+ LINE_CACHE_SUB_REC *sub;
+ GSList *lines;
+ unsigned char cmd;
+ 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;
+ last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
+
+ linecount = 1;
+ lines = NULL;
+ for (ptr = line->text;;) {
+ if (*ptr == '\0') {
+ /* command */
+ ptr++;
+ cmd = *ptr;
+ ptr++;
+
+ if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT)
+ break;
+
+ if (cmd == LINE_CMD_CONTINUE) {
+ 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:
+ /* 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;
+ }
+ continue;
+ }
+
+ if (xpos == view->width && sub != NULL &&
+ (last_space <= indent_pos || last_space <= 10) &&
+ !view->longword_noindent) {
+ /* long word, remove the indentation from this line */
+ xpos -= sub->indent;
+ sub->indent = 0;
+ }
+
+ if (xpos == view->width) {
+ xpos = indent_pos;
+
+ sub = g_new0(LINE_CACHE_SUB_REC, 1);
+ if (last_space > indent_pos && last_space > 10) {
+ /* go back to last space */
+ color = last_color;
+ ptr = last_space_ptr;
+ while (*ptr == ' ') ptr++;
+ } else if (!view->longword_noindent) {
+ /* long word, no indentation in next line */
+ xpos = 0;
+ sub->continues = TRUE;
+ }
+
+ sub->start = ptr;
+ sub->indent = xpos;
+ sub->color = color;
+
+ lines = g_slist_append(lines, sub);
+ linecount++;
+
+ last_space = 0;
+ continue;
+ }
+
+ xpos++;
+ if (*ptr++ == ' ') {
+ last_space = xpos-1;
+ last_space_ptr = ptr;
+ last_color = color;
+ }
+ }
+
+ rec = g_malloc(sizeof(LINE_CACHE_REC)-sizeof(LINE_CACHE_SUB_REC) +
+ sizeof(LINE_CACHE_SUB_REC) * (linecount-1));
+ rec->last_access = time(NULL);
+ rec->count = linecount;
+
+ if (rec->count > 1) {
+ for (pos = 0; lines != NULL; pos++) {
+ memcpy(&rec->lines[pos], lines->data,
+ sizeof(LINE_CACHE_SUB_REC));
+
+ g_free(lines->data);
+ lines = g_slist_remove(lines, lines->data);
+ }
+ }
+
+ g_hash_table_insert(view->cache->line_cache, line, rec);
+ return rec;
+}
+
+static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+ int subline, int ypos, int max)
+{
+ LINE_CACHE_REC *cache;
+ const unsigned char *text, *text_newline;
+ char *tmp;
+ int xpos, color, drawcount, first;
+
+ cache = textbuffer_view_get_line_cache(view, line);
+ if (subline >= cache->count)
+ return 0;
+
+ xpos = color = drawcount = 0; first = TRUE;
+ text_newline = text =
+ subline == 0 ? line->text : cache->lines[subline-1].start;
+ for (;;) {
+ if (text == text_newline) {
+ if (first)
+ first = FALSE;
+ else {
+ ypos++;
+ if (--max == 0)
+ break;
+ }
+
+ if (subline > 0) {
+ xpos = cache->lines[subline-1].indent;
+ color = cache->lines[subline-1].color;
+ }
+
+ set_color(view->window, 0);
+ wmove(view->window, ypos, 0);
+ wclrtoeol(view->window);
+
+ wmove(view->window, ypos, xpos);
+ set_color(view->window, color);
+
+ /* get the beginning of the next subline */
+ text_newline = subline == cache->count-1 ? NULL :
+ cache->lines[subline].start;
+
+ drawcount++;
+ subline++;
+ }
+
+ if (*text == '\0') {
+ /* command */
+ text++;
+ 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) {
+ /* 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;
+ }
+ 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);
+ }
+ text++;
+ }
+
+ return drawcount;
+}
+
+/* Recalculate view's bottom line information - try to keep the
+ original if possible */
+static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
+{
+ GList *tmp;
+ int linecount, total;
+
+ if (view->empty_linecount == 0) {
+ /* no empty lines in screen, no need to try to keep
+ the old bottom startline */
+ view->bottom_startline = NULL;
+ }
+
+ total = 0;
+ tmp = g_list_last(view->buffer->lines);
+ for (; tmp != NULL; tmp = tmp->prev) {
+ LINE_REC *line = tmp->data;
+
+ linecount = view_get_linecount(view, line);
+ if (tmp == view->bottom_startline) {
+ /* keep the old one, make sure that subline is ok */
+ if (view->bottom_subline > linecount)
+ view->bottom_subline = linecount;
+ view->empty_linecount = view->height - total -
+ (linecount-view->bottom_subline);
+ return;
+ }
+
+ total += linecount;
+ if (total >= view->height) {
+ view->bottom_startline = tmp;
+ view->bottom_subline = total - view->height;
+ view->empty_linecount = 0;
+ return;
+ }
+ }
+
+ /* not enough lines so we must be at the beginning of the buffer */
+ view->bottom_startline = view->buffer->lines;
+ view->bottom_subline = 0;
+ view->empty_linecount = view->height - total;
+}
+
+static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view)
+{
+ GList *tmp;
+
+ 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);
+}
+
+/* Create new view. */
+TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
+ int width, int height,
+ int default_indent,
+ int longword_noindent)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+
+ g_return_val_if_fail(buffer != NULL, NULL);
+ g_return_val_if_fail(width > 0, NULL);
+
+ view = g_new0(TEXT_BUFFER_VIEW_REC, 1);
+ view->buffer = buffer;
+ view->siblings = textbuffer_get_views(buffer);
+
+ view->width = width;
+ view->height = height;
+ view->default_indent = default_indent;
+ view->longword_noindent = longword_noindent;
+
+ view->cache = textbuffer_cache_get(view->siblings, width);
+ textbuffer_view_init_bottom(view);
+
+ view->startline = view->bottom_startline;
+ view->subline = view->bottom_subline;
+ view->bottom = TRUE;
+
+ textbuffer_view_init_ypos(view);
+
+ view->bookmarks = g_hash_table_new((GHashFunc) g_str_hash,
+ (GCompareFunc) g_str_equal);
+
+ views = g_slist_append(views, view);
+ return view;
+}
+
+/* Destroy the view. */
+void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view)
+{
+ GSList *tmp;
+
+ g_return_if_fail(view != NULL);
+
+ views = g_slist_remove(views, view);
+
+ if (view->siblings == NULL) {
+ /* last view for textbuffer, destroy */
+ textbuffer_destroy(view->buffer);
+ } else {
+ /* remove ourself from siblings lists */
+ for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
+ TEXT_BUFFER_VIEW_REC *rec = tmp->data;
+
+ rec->siblings = g_slist_remove(rec->siblings, view);
+ }
+ g_slist_free(view->siblings);
+ }
+
+ g_hash_table_foreach(view->bookmarks, (GHFunc) g_free, NULL);
+ g_hash_table_destroy(view->bookmarks);
+
+ textbuffer_cache_unref(view->cache);
+ g_free(view);
+}
+
+/* Change the default indent position */
+void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
+ int default_indent,
+ int longword_noindent)
+{
+ view->default_indent = default_indent;
+ view->longword_noindent = longword_noindent;
+}
+
+static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, GList *lines)
+{
+ int linecount;
+
+ linecount = 0;
+ while (lines != NULL) {
+ linecount += view_get_linecount(view, lines->data);
+ lines = lines->next;
+ }
+
+ return linecount;
+}
+
+static void view_draw(TEXT_BUFFER_VIEW_REC *view, GList *line,
+ int subline, int ypos, int lines)
+{
+ int linecount;
+
+ while (line != NULL && lines > 0) {
+ LINE_REC *rec = line->data;
+
+ linecount = view_line_draw(view, rec, 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--;
+ }
+}
+
+#define view_draw_top(view, lines) \
+ view_draw(view, (view)->startline, (view)->subline, 0, lines)
+
+static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
+{
+ GList *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);
+ ypos += linecount;
+ if (ypos > maxline) {
+ subline = maxline-(ypos-linecount);
+ break;
+ }
+ line = line->next;
+ }
+
+ view_draw(view, line, subline, maxline, lines);
+}
+
+/* Returns number of lines actually scrolled */
+static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
+ int scrollcount, int draw_nonclean)
+{
+ int linecount, realcount, scroll_visible;
+
+ if (*lines == NULL)
+ return 0;
+
+ /* scroll down */
+ scroll_visible = lines == &view->startline;
+
+ realcount = -*subline;
+ scrollcount += *subline;
+ *subline = 0;
+ while (scrollcount > 0) {
+ linecount = view_get_linecount(view, (*lines)->data);
+
+ if ((scroll_visible && *lines == view->bottom_startline) &&
+ (scrollcount >= view->bottom_subline)) {
+ *subline = view->bottom_subline;
+ realcount += view->bottom_subline;
+ scrollcount = 0;
+ break;
+ }
+
+ realcount += linecount;
+ scrollcount -= linecount;
+ if (scrollcount < 0) {
+ realcount += scrollcount;
+ *subline = linecount+scrollcount;
+ scrollcount = 0;
+ break;
+ }
+
+ *lines = (*lines)->next;
+ }
+
+ /* scroll up */
+ while (scrollcount < 0 && (*lines)->prev != NULL) {
+ *lines = (*lines)->prev;
+ linecount = view_get_linecount(view, (*lines)->data);
+
+ realcount -= linecount;
+ scrollcount += linecount;
+ if (scrollcount > 0) {
+ realcount += scrollcount;
+ *subline = scrollcount;
+ break;
+ }
+ }
+
+ if (scroll_visible && realcount != 0 && view->window != NULL) {
+ if (realcount <= -view->height || realcount >= view->height) {
+ /* scrolled more than screenful, redraw the
+ whole view */
+ textbuffer_view_redraw(view);
+ } else {
+ scrollok(view->window, TRUE);
+ wscrl(view->window, realcount);
+ scrollok(view->window, FALSE);
+
+ if (draw_nonclean) {
+ if (realcount < 0)
+ view_draw_top(view, -realcount);
+ else
+ view_draw_bottom(view, realcount);
+ }
+
+ screen_refresh(view->window);
+ }
+ }
+
+ return realcount >= 0 ? realcount : -realcount;
+}
+
+/* Resize the view. */
+void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
+{
+ int linecount;
+
+ g_return_if_fail(view != NULL);
+ g_return_if_fail(width > 0);
+
+ if (view->buffer->lines == NULL)
+ return;
+
+ if (view->width != width) {
+ /* line cache needs to be recreated */
+ textbuffer_cache_unref(view->cache);
+ view->cache = textbuffer_cache_get(view->siblings, width);
+ }
+
+ view->width = width;
+ view->height = height;
+
+ 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) {
+ view->startline = view->bottom_startline;
+ view->subline = view->bottom_subline;
+ } else if (view->startline == view->bottom_startline &&
+ view->subline > view->bottom_subline) {
+ view->subline = view->bottom_subline;
+ } else {
+ /* make sure the subline is still in allowed range */
+ linecount = view_get_linecount(view, view->startline->data);
+ if (view->subline > linecount)
+ view->subline = linecount;
+ }
+
+ textbuffer_view_init_ypos(view);
+ if (view->bottom && !view_is_bottom(view)) {
+ /* we scrolled to far up, need to get down. go right over
+ the empty lines if there's any */
+ view->startline = view->bottom_startline;
+ view->subline = view->bottom_subline;
+ if (view->empty_linecount > 0) {
+ view_scroll(view, &view->startline, &view->subline,
+ -view->empty_linecount, FALSE);
+ }
+ textbuffer_view_init_ypos(view);
+ }
+
+ view->bottom = view_is_bottom(view);
+ if (view->bottom) {
+ /* check if we left empty space at the bottom.. */
+ linecount = view_get_linecount_all(view, view->startline) -
+ view->subline;
+ if (view->empty_linecount < view->height-linecount)
+ view->empty_linecount = view->height-linecount;
+ }
+
+ textbuffer_view_redraw(view);
+}
+
+/* Clear the view, don't actually remove any lines from buffer. */
+void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view)
+{
+ g_return_if_fail(view != NULL);
+
+ view->ypos = -1;
+ view->bottom_startline = view->startline =
+ g_list_last(view->buffer->lines);
+ 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;
+
+ textbuffer_view_redraw(view);
+}
+
+/* Scroll the view up/down */
+void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines)
+{
+ int count;
+
+ g_return_if_fail(view != NULL);
+
+ count = view_scroll(view, &view->startline, &view->subline,
+ lines, TRUE);
+ view->ypos += lines < 0 ? count : -count;
+ view->bottom = view_is_bottom(view);
+
+ if (view->window != NULL)
+ screen_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) {
+ 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;
+ }
+ }
+ }
+
+ textbuffer_view_init_ypos(view);
+ view->bottom = view_is_bottom(view);
+
+ textbuffer_view_redraw(view);
+}
+
+/* Return line cache */
+LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
+ LINE_REC *line)
+{
+ LINE_CACHE_REC *cache;
+
+ g_return_val_if_fail(view != NULL, NULL);
+ g_return_val_if_fail(line != NULL, NULL);
+
+ cache = g_hash_table_lookup(view->cache->line_cache, line);
+ if (cache == NULL)
+ cache = view_update_line_cache(view, line);
+ else
+ cache->last_access = time(NULL);
+
+ 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_startline == NULL) {
+ view->startline = view->bottom_startline =
+ view->buffer->lines;
+ }
+
+ if (view->buffer->cur_line != line &&
+ g_list_find(view->bottom_startline, line) == NULL)
+ return;
+
+ linecount = view->cache->last_linecount;
+ view->ypos += linecount;
+ if (view->empty_linecount > 0) {
+ view->empty_linecount -= linecount;
+ if (view->empty_linecount >= 0)
+ linecount = 0;
+ else {
+ linecount = -view->empty_linecount;
+ view->empty_linecount = 0;
+ }
+ }
+
+ if (linecount > 0) {
+ view_scroll(view, &view->bottom_startline,
+ &view->bottom_subline, linecount, FALSE);
+ }
+
+ if (view->bottom) {
+ if (view->ypos >= view->height) {
+ linecount = view->ypos-view->height+1;
+ view_scroll(view, &view->startline,
+ &view->subline, linecount, FALSE);
+ view->ypos -= linecount;
+ }
+
+ if (view->window != NULL) {
+ ypos = view->ypos+1 - view->cache->last_linecount;
+ if (ypos >= 0)
+ subline = 0;
+ else {
+ subline = -ypos;
+ ypos = 0;
+ }
+ view_line_draw(view, line, subline, ypos,
+ view->height - ypos);
+ }
+ }
+
+ if (view->window != NULL)
+ screen_refresh(view->window);
+}
+
+/* Update some line in the buffer which has been modified using
+ textbuffer_append() or textbuffer_insert(). */
+void textbuffer_view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
+{
+ GSList *tmp;
+ unsigned char update_counter;
+
+ g_return_if_fail(view != NULL);
+ g_return_if_fail(line != NULL);
+
+ if (!view->buffer->last_eol)
+ return;
+
+ update_counter = view->cache->update_counter+1;
+ view_update_cache(view, line, update_counter);
+ view_insert_line(view, line);
+
+ for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
+ TEXT_BUFFER_VIEW_REC *rec = tmp->data;
+
+ view_update_cache(rec, line, update_counter);
+ view_insert_line(rec, line);
+ }
+}
+
+typedef struct {
+ LINE_REC *remove_line;
+ GSList *remove_list;
+} BOOKMARK_FIND_REC;
+
+static void bookmark_check_remove(char *key, LINE_REC *line,
+ BOOKMARK_FIND_REC *rec)
+{
+ if (line == rec->remove_line)
+ rec->remove_list = g_slist_append(rec->remove_list, key);
+}
+
+static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
+{
+ BOOKMARK_FIND_REC rec;
+ LINE_REC *newline;
+ GSList *tmp;
+
+ rec.remove_line = line;
+ rec.remove_list = NULL;
+ g_hash_table_foreach(view->bookmarks,
+ (GHFunc) bookmark_check_remove, &rec);
+
+ if (rec.remove_list != NULL) {
+ GList *pos = g_list_find(view->buffer->lines, line);
+
+ newline = pos == NULL || pos->prev == NULL ? NULL :
+ (pos->next == NULL ? pos->prev->data :
+ pos->next->data);
+ for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) {
+ g_hash_table_remove(view->bookmarks, tmp->data);
+ if (newline != NULL) {
+ g_hash_table_insert(view->bookmarks,
+ tmp->data, newline);
+ }
+ }
+ g_slist_free(rec.remove_list);
+ }
+}
+
+/* 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 *skip_line)
+{
+ int height, linecount;
+
+ height = -subline;
+ while (lines != NULL && height < view->height) {
+ LINE_REC *line = lines->data;
+
+ if (line != skip_line) {
+ linecount = view_get_linecount(view, line);
+ height += linecount;
+ }
+ lines = lines->next;
+ }
+
+ return height < view->height ? height : view->height;
+}
+
+static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+ int linecount)
+{
+ int realcount, scroll;
+
+ view_bookmarks_check(view, line);
+
+ if (view->buffer->cur_line == line) {
+ /* the last line is being removed */
+ LINE_REC *prevline;
+
+ prevline = view->buffer->lines->data == line ? NULL :
+ g_list_last(view->bottom_startline)->data;
+ view->cache->last_linecount = prevline == NULL ? 0 :
+ view_get_linecount(view, prevline);
+ }
+
+ if (line == view->buffer->lines->data) {
+ /* first line in the buffer - this is the most commonly
+ removed line.. */
+ if (view->bottom_startline->data == line) {
+ /* very small scrollback.. */
+ view->bottom_startline = view->bottom_startline->next;
+ view->bottom_subline = 0;
+ }
+
+ if (view->startline->data == line) {
+ /* removing the first line in screen */
+ realcount = view_scroll(view, &view->startline,
+ &view->subline,
+ linecount, TRUE);
+ 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;
+ }
+ }
+ view->empty_linecount += linecount-realcount;
+ }
+
+ view->bottom = view_is_bottom(view);
+ if (view->window != NULL)
+ screen_refresh(view->window);
+}
+
+/* Remove one line from buffer. */
+void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
+{
+ GSList *tmp;
+ unsigned char update_counter;
+ int linecount;
+
+ g_return_if_fail(view != NULL);
+ g_return_if_fail(line != NULL);
+
+ linecount = view_get_linecount(view, line);
+ update_counter = view->cache->update_counter+1;
+
+ view_remove_line(view, line, linecount);
+ view_remove_cache(view, line, update_counter);
+
+ for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
+ TEXT_BUFFER_VIEW_REC *rec = tmp->data;
+
+ view_remove_line(rec, line, linecount);
+ view_remove_cache(rec, line, update_counter);
+ }
+
+ textbuffer_remove(view->buffer, line);
+}
+
+/* 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);
+
+ /* recreate caches, clear screens */
+ view->cache = textbuffer_cache_get(view->siblings, view->width);
+ 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);
+ }
+}
+
+/* Set a bookmark in view */
+void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view,
+ const char *name, LINE_REC *line)
+{
+ gpointer key, value;
+
+ g_return_if_fail(view != NULL);
+ g_return_if_fail(name != NULL);
+
+ if (g_hash_table_lookup_extended(view->bookmarks, name,
+ &key, &value)) {
+ g_hash_table_remove(view->bookmarks, key);
+ g_free(key);
+ }
+
+ g_hash_table_insert(view->bookmarks, g_strdup(name), line);
+}
+
+/* Set a bookmark in view to the bottom line */
+void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
+ const char *name)
+{
+ LINE_REC *line;
+
+ g_return_if_fail(view != NULL);
+ g_return_if_fail(name != NULL);
+
+ if (view->bottom_startline != NULL) {
+ line = g_list_last(view->bottom_startline)->data;
+ textbuffer_view_set_bookmark(view, name, line);
+ }
+}
+
+/* Return the line for bookmark */
+LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
+ const char *name)
+{
+ g_return_val_if_fail(view != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return g_hash_table_lookup(view->bookmarks, name);
+}
+
+/* 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)
+{
+ g_return_if_fail(view != NULL);
+
+ if (view->window != window) {
+ view->window = window;
+ if (window != NULL)
+ textbuffer_view_redraw(view);
+ }
+}
+
+/* Redraw a view to window */
+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);
+ }
+}
+
+static int line_cache_check_remove(void *key, LINE_CACHE_REC *cache,
+ time_t *now)
+{
+ if (cache->last_access+LINE_CACHE_KEEP_TIME > *now)
+ return FALSE;
+
+ line_cache_destroy(NULL, cache);
+ return TRUE;
+}
+
+static int sig_check_linecache(void)
+{
+ GSList *tmp, *caches;
+ time_t now;
+
+ now = time(NULL); caches = NULL;
+ for (tmp = views; tmp != NULL; tmp = tmp->next) {
+ TEXT_BUFFER_VIEW_REC *rec = tmp->data;
+
+ if (g_slist_find(caches, rec->cache) != NULL)
+ continue;
+
+ caches = g_slist_append(caches, rec->cache);
+ g_hash_table_foreach_remove(rec->cache->line_cache,
+ (GHRFunc) line_cache_check_remove,
+ &now);
+ }
+ return 1;
+}
+
+void textbuffer_view_init(void)
+{
+ linecache_tag = g_timeout_add(LINE_CACHE_CHECK_TIME, (GSourceFunc) sig_check_linecache, NULL);
+}
+
+void textbuffer_view_deinit(void)
+{
+ g_source_remove(linecache_tag);
+}
--- /dev/null
+#ifndef __TEXTBUFFER_VIEW_H
+#define __TEXTBUFFER_VIEW_H
+
+#include "textbuffer.h"
+#include "screen.h"
+
+typedef struct {
+ unsigned char *start;
+ int indent;
+ int color;
+
+ /* first word in line belong to the end of the last word in
+ previous line */
+ unsigned int continues:1;
+} LINE_CACHE_SUB_REC;
+
+typedef struct {
+ time_t last_access;
+
+ int count; /* number of real lines */
+
+ /* variable sized array, actually. starts from the second line,
+ so size of it is count-1 */
+ LINE_CACHE_SUB_REC lines[1];
+} LINE_CACHE_REC;
+
+typedef struct {
+ int refcount;
+ int width;
+
+ GHashTable *line_cache;
+
+ /* should contain the same value for each cache that uses the
+ same buffer */
+ unsigned char update_counter;
+ /* number of real lines used by the last line in buffer */
+ int last_linecount;
+} TEXT_BUFFER_CACHE_REC;
+
+typedef struct {
+ TEXT_BUFFER_REC *buffer;
+ GSList *siblings; /* other views that use the same buffer */
+
+ WINDOW *window;
+ int width, height;
+
+ int default_indent;
+ int longword_noindent:1;
+
+ TEXT_BUFFER_CACHE_REC *cache;
+ int ypos; /* cursor position - visible area is 0..height-1 */
+
+ GList *startline; /* line at the top of the screen */
+ int subline; /* number of "real lines" to skip from `startline' */
+
+ /* marks the bottom of the text buffer */
+ GList *bottom_startline;
+ int bottom_subline;
+
+ /* how many empty lines are in screen. a screenful when started
+ or used /CLEAR */
+ int empty_linecount;
+ /* window is at the bottom of the text buffer */
+ unsigned int bottom: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);
+/* 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);
+
+/* Resize the view. */
+void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height);
+/* Clear the view, don't actually remove any lines from buffer. */
+void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view);
+
+#define textbuffer_view_get_lines(view) \
+ ((view)->buffer->lines)
+
+/* Scroll the view up/down */
+void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines);
+/* Scroll to specified line */
+void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line);
+/* Return line cache */
+LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
+ LINE_REC *line);
+
+/*
+ Functions for manipulating the text buffer, using these commands update
+ all views that use the buffer.
+*/
+
+/* Update some line in the buffer which has been modified using
+ textbuffer_append() or textbuffer_insert(). */
+void textbuffer_view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line);
+/* Remove one line from buffer. */
+void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line);
+/* Remove all lines from buffer. */
+void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view);
+
+/* Set a bookmark in view */
+void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view,
+ const char *name, LINE_REC *line);
+/* Set a bookmark in view to the bottom line */
+void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
+ const char *name);
+/* Return the line for bookmark */
+LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
+ const char *name);
+
+/* 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);
+/* Redraw the view */
+void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view);
+
+void textbuffer_view_init(void);
+void textbuffer_view_deinit(void);
+
+#endif
--- /dev/null
+/*
+ textbuffer.c : Text buffer 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 "misc.h"
+#include "formats.h"
+
+#include "textbuffer.h"
+
+#ifdef HAVE_REGEX_H
+# include <regex.h>
+#endif
+
+#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*))
+
+static GMemChunk *buffer_chunk, *line_chunk, *text_chunk;
+
+TEXT_BUFFER_REC *textbuffer_create(void)
+{
+ TEXT_BUFFER_REC *buffer;
+
+ buffer = g_mem_chunk_alloc0(buffer_chunk);
+ buffer->last_eol = TRUE;
+ return buffer;
+}
+
+void textbuffer_destroy(TEXT_BUFFER_REC *buffer)
+{
+ g_return_if_fail(buffer != NULL);
+
+ textbuffer_remove_all_lines(buffer);
+ g_mem_chunk_free(buffer_chunk, buffer);
+}
+
+static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer,
+ const unsigned char *data)
+{
+ GSList *tmp;
+
+ for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) {
+ TEXT_CHUNK_REC *rec = tmp->data;
+
+ if (data >= rec->buffer &&
+ data < rec->buffer+sizeof(rec->buffer))
+ return rec;
+ }
+
+ return NULL;
+}
+
+#define mark_temp_eol(chunk) G_STMT_START { \
+ (chunk)->buffer[(chunk)->pos] = 0; \
+ (chunk)->buffer[(chunk)->pos+1] = LINE_CMD_EOL; \
+ } G_STMT_END
+
+static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
+{
+ TEXT_CHUNK_REC *rec;
+ char *buf, *ptr, **pptr;
+
+ rec = g_mem_chunk_alloc(text_chunk);
+ rec->pos = 0;
+ rec->refcount = 0;
+
+ if (buffer->cur_line != NULL && buffer->cur_line->text != NULL) {
+ /* create a link to new block from the old block */
+ buf = buffer->cur_text->buffer + buffer->cur_text->pos;
+ *buf++ = 0; *buf++ = (char) LINE_CMD_CONTINUE;
+
+ /* we want to store pointer to beginning of the new text
+ block to char* buffer. this probably isn't ANSI-C
+ compatible, and trying this without the pptr variable
+ breaks at least NetBSD/Alpha, so don't go "optimize"
+ it :) */
+ ptr = rec->buffer; pptr = &ptr;
+ memcpy(buf, pptr, sizeof(char *));
+ } else {
+ /* just to be safe */
+ mark_temp_eol(rec);
+ }
+
+ buffer->cur_text = rec;
+ buffer->text_chunks = g_slist_append(buffer->text_chunks, rec);
+ return rec;
+}
+
+static void text_chunk_destroy(TEXT_BUFFER_REC *buffer, TEXT_CHUNK_REC *chunk)
+{
+ buffer->text_chunks = g_slist_remove(buffer->text_chunks, chunk);
+ g_mem_chunk_free(text_chunk, chunk);
+}
+
+static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
+{
+ TEXT_CHUNK_REC *chunk;
+ const unsigned char *text;
+ unsigned char *tmp = NULL;
+
+ for (text = line->text;; text++) {
+ if (*text != '\0')
+ continue;
+
+ text++;
+ if (*text == LINE_CMD_CONTINUE || *text == LINE_CMD_EOL) {
+ if (*text == LINE_CMD_CONTINUE)
+ memcpy(&tmp, text+1, sizeof(char *));
+
+ /* free the previous block */
+ chunk = text_chunk_find(buffer, text);
+ if (--chunk->refcount == 0) {
+ if (buffer->cur_text == chunk)
+ chunk->pos = 0;
+ else
+ text_chunk_destroy(buffer, chunk);
+ }
+
+ if (*text == LINE_CMD_EOL)
+ break;
+
+ text = tmp-1;
+ }
+ }
+}
+
+static void text_chunk_append(TEXT_BUFFER_REC *buffer,
+ const char *data, int len)
+{
+ TEXT_CHUNK_REC *chunk;
+ int left;
+
+ if (len == 0)
+ return;
+
+ 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 */
+
+ memcpy(chunk->buffer + chunk->pos, data, left);
+ chunk->pos += left;
+
+ chunk = text_chunk_create(buffer);
+ chunk->refcount++;
+ len -= left; data += left;
+ }
+
+ memcpy(chunk->buffer + chunk->pos, data, len);
+ chunk->pos += len;
+
+ mark_temp_eol(chunk);
+}
+
+static LINE_REC *textbuffer_line_create(TEXT_BUFFER_REC *buffer)
+{
+ LINE_REC *rec;
+
+ if (buffer->cur_text == NULL)
+ text_chunk_create(buffer);
+
+ rec = g_mem_chunk_alloc(line_chunk);
+ rec->refcount = 1;
+ rec->text = buffer->cur_text->buffer + buffer->cur_text->pos;
+
+ buffer->cur_text->refcount++;
+ return rec;
+}
+
+static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
+ LINE_REC *prev)
+{
+ 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);
+ } else {
+ buffer->lines = g_list_insert(buffer->lines, line,
+ g_list_index(buffer->lines, prev)+1);
+ }
+ buffer->lines_count++;
+
+ return line;
+}
+
+void textbuffer_line_ref(LINE_REC *line)
+{
+ g_return_if_fail(line != NULL);
+
+ if (++line->refcount == 255)
+ g_error("line reference counter wrapped - shouldn't happen");
+}
+
+void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line)
+{
+ g_return_if_fail(buffer != NULL);
+ g_return_if_fail(line != NULL);
+
+ if (--line->refcount == 0) {
+ text_chunk_line_free(buffer, line);
+ g_mem_chunk_free(line_chunk, line);
+ }
+}
+
+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);
+ list = list->next;
+ }
+}
+
+LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info)
+{
+ return textbuffer_insert(buffer, buffer->cur_line, data, len, info);
+}
+
+LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info)
+{
+ LINE_REC *line;
+
+ g_return_val_if_fail(buffer != NULL, NULL);
+ g_return_val_if_fail(data != NULL, NULL);
+
+ line = !buffer->last_eol ? insert_after :
+ textbuffer_line_insert(buffer, insert_after);
+
+ if (info != NULL)
+ memcpy(&line->info, info, sizeof(line->info));
+
+ text_chunk_append(buffer, data, len);
+
+ buffer->last_eol = len >= 2 &&
+ data[len-2] == 0 && data[len-1] == LINE_CMD_EOL;
+
+ return line;
+}
+
+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->cur_line == line) {
+ buffer->cur_line = buffer->lines == NULL ? NULL :
+ g_list_last(buffer->lines)->data;
+ }
+
+ buffer->lines_count--;
+ textbuffer_line_unref(buffer, line);
+}
+
+/* Removes all lines from buffer, ignoring reference counters */
+void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
+{
+ GSList *tmp;
+
+ 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;
+
+ g_list_free(buffer->lines);
+ buffer->lines = NULL;
+
+ buffer->cur_line = NULL;
+ buffer->lines_count = 0;
+}
+
+void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
+{
+ unsigned char cmd;
+ char *ptr, *tmp;
+
+ g_return_if_fail(line != NULL);
+ g_return_if_fail(str != NULL);
+
+ g_string_truncate(str, 0);
+
+ for (ptr = line->text;;) {
+ if (*ptr != 0) {
+ g_string_append_c(str, *ptr);
+ ptr++;
+ continue;
+ }
+
+ ptr++;
+ cmd = (unsigned char) *ptr;
+ ptr++;
+
+ if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
+ /* end of line */
+ break;
+ }
+
+ if (cmd == LINE_CMD_CONTINUE) {
+ /* line continues in another address.. */
+ memcpy(&tmp, ptr, sizeof(char *));
+ ptr = tmp;
+ continue;
+ }
+
+ if (!coloring) {
+ /* no colors, skip coloring commands */
+ continue;
+ }
+
+ if ((cmd & 0x80) == 0) {
+ /* set color */
+ g_string_sprintfa(str, "\004%c%c",
+ (cmd & 0x0f)+'0',
+ ((cmd & 0xf0) >> 4)+'0');
+ } else switch (cmd) {
+ case LINE_CMD_UNDERLINE:
+ g_string_append_c(str, 31);
+ 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;
+ }
+ }
+}
+
+GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
+ int level, int nolevel, const char *text,
+ int regexp, int fullword, int case_sensitive)
+{
+#ifdef HAVE_REGEX_H
+ regex_t preg;
+#endif
+ GList *line, *tmp;
+ GList *matches;
+ GString *str;
+
+ g_return_val_if_fail(buffer != NULL, NULL);
+ g_return_val_if_fail(text != NULL, NULL);
+
+ if (regexp) {
+#ifdef HAVE_REGEX_H
+ int flags = REG_EXTENDED | REG_NOSUB |
+ (case_sensitive ? 0 : REG_ICASE);
+ if (regcomp(&preg, text, flags) != 0)
+ return NULL;
+#else
+ return NULL;
+#endif
+ }
+
+ matches = NULL;
+ str = g_string_new(NULL);
+
+ line = g_list_find(buffer->lines, startline);
+ if (line == NULL)
+ line = buffer->lines;
+
+ for (tmp = line; tmp != NULL; tmp = tmp->next) {
+ LINE_REC *rec = tmp->data;
+
+ if ((rec->info.level & level) == 0 ||
+ (rec->info.level & nolevel) != 0)
+ continue;
+
+ if (*text == '\0') {
+ /* no search word, everything matches */
+ textbuffer_line_ref(rec);
+ matches = g_list_append(matches, rec);
+ continue;
+ }
+
+ textbuffer_line2text(rec, FALSE, str);
+
+ if (
+#ifdef HAVE_REGEX_H
+ regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+#endif
+ fullword ? strstr_full_case(str->str, text,
+ !case_sensitive) != NULL :
+ case_sensitive ? strstr(str->str, text) != NULL :
+ stristr(str->str, text) != NULL) {
+ /* matched */
+ textbuffer_line_ref(rec);
+ matches = g_list_append(matches, rec);
+ }
+ }
+#ifdef HAVE_REGEX_H
+ if (regexp) regfree(&preg);
+#endif
+ g_string_free(str, TRUE);
+ return matches;
+}
+
+#if 0 /* FIXME: saving formats is broken */
+static char *line_read_format(unsigned const char **text)
+{
+ GString *str;
+ char *ret;
+
+ str = g_string_new(NULL);
+ for (;;) {
+ if (**text == '\0') {
+ if ((*text)[1] == LINE_CMD_EOL) {
+ /* leave text at \0<eof> */
+ break;
+ }
+ if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
+ /* leave text at \0<format_cont> */
+ break;
+ }
+ (*text)++;
+
+ if (**text == LINE_CMD_FORMAT) {
+ /* move text to start after \0<format> */
+ (*text)++;
+ break;
+ }
+
+ if (**text == LINE_CMD_CONTINUE) {
+ unsigned char *tmp;
+
+ memcpy(&tmp, (*text)+1, sizeof(char *));
+ *text = tmp;
+ continue;
+ } else if (**text & 0x80)
+ (*text)++;
+ continue;
+ }
+
+ g_string_append_c(str, (char) **text);
+ (*text)++;
+ }
+
+ ret = str->str;
+ g_string_free(str, FALSE);
+ return ret;
+}
+
+static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
+ GString *raw)
+{
+ const unsigned char *text;
+ char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
+ TEXT_DEST_REC dest;
+ int formatnum, argcount;
+
+ text = (const unsigned char *) line->text;
+
+ /* skip the beginning of the line until we find the format */
+ g_free(line_read_format(&text));
+ if (text[1] == LINE_CMD_FORMAT_CONT) {
+ 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->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);
+}
+#endif
+
+void textbuffer_init(void)
+{
+ buffer_chunk = g_mem_chunk_new("text buffer chunk",
+ sizeof(TEXT_BUFFER_REC),
+ sizeof(TEXT_BUFFER_REC)*32, G_ALLOC_AND_FREE);
+ line_chunk = g_mem_chunk_new("line chunk", sizeof(LINE_REC),
+ sizeof(LINE_REC)*1024, G_ALLOC_AND_FREE);
+ text_chunk = g_mem_chunk_new("text chunk", sizeof(TEXT_CHUNK_REC),
+ sizeof(TEXT_CHUNK_REC)*32, G_ALLOC_AND_FREE);
+}
+
+void textbuffer_deinit(void)
+{
+ g_mem_chunk_destroy(buffer_chunk);
+ g_mem_chunk_destroy(line_chunk);
+ g_mem_chunk_destroy(text_chunk);
+}
--- /dev/null
+#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
+
+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_INDENT, /* if line is split, indent it at this position */
+ LINE_CMD_BLINK, /* blinking background */
+ LINE_CMD_FORMAT, /* end of line, but next will come the format that was used to create the
+ text in format <module><format_name><arg><arg2...> - fields are separated
+ with \0<format> and last argument ends with \0<eol>. \0<continue> is allowed
+ anywhere */
+ LINE_CMD_FORMAT_CONT /* multiline format, continues to next line */
+};
+
+typedef struct {
+ int level;
+ 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.
+
+ DO NOT ADD BLACK WITH \0\0 - this will break things. Use
+ LINE_CMD_COLOR0 instead. */
+ unsigned char *text;
+ unsigned char refcount;
+ LINE_INFO_REC info;
+} LINE_REC;
+
+typedef struct {
+ unsigned char buffer[LINE_TEXT_CHUNK_SIZE];
+ int pos;
+ int refcount;
+} TEXT_CHUNK_REC;
+
+typedef struct {
+ GSList *text_chunks;
+ GList *lines;
+ int lines_count;
+
+ LINE_REC *cur_line;
+ TEXT_CHUNK_REC *cur_text;
+
+ unsigned int last_eol:1;
+} TEXT_BUFFER_REC;
+
+/* Create new buffer */
+TEXT_BUFFER_REC *textbuffer_create(void);
+/* Destroy the buffer */
+void textbuffer_destroy(TEXT_BUFFER_REC *buffer);
+
+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);
+
+/* Append text to buffer. When \0<EOL> is found at the END OF DATA, a new
+ line is created. You must send the EOL command before you can do anything
+ else with the buffer. */
+LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info);
+LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info);
+
+void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line);
+/* Removes all lines from buffer, ignoring reference counters */
+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 regexp, int fullword, int case_sensitive);
+
+void textbuffer_init(void);
+void textbuffer_deinit(void);
+
+#endif
--- /dev/null
+noinst_LIBRARIES = libirssi_config.a
+
+INCLUDES = \
+ $(GLIB_CFLAGS) \
+ -I$(top_srcdir)/src
+
+libirssi_config_a_SOURCES = \
+ get.c \
+ set.c \
+ parse.c \
+ write.c
+
+noinst_HEADERS = \
+ iconfig.h \
+ module.h
--- /dev/null
+/*
+ get.c : irssi configuration - get settings from memory
+
+ Copyright (C) 1999 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"
+
+CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key)
+{
+ GSList *tmp;
+
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(is_node_list(node), NULL);
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ CONFIG_NODE *node = tmp->data;
+
+ if (node->key != NULL && g_strcasecmp(node->key, key) == 0)
+ return node;
+ }
+
+ return NULL;
+}
+
+/* find the section from node - if not found create it unless new_type is -1.
+ you can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */
+CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type)
+{
+ CONFIG_NODE *node;
+
+ g_return_val_if_fail(parent != NULL, NULL);
+ g_return_val_if_fail(is_node_list(parent), NULL);
+
+ node = key == NULL ? NULL : config_node_find(parent, key);
+ if (node != NULL) {
+ g_return_val_if_fail(new_type == -1 || new_type == node->type, NULL);
+ return node;
+ }
+
+ if (new_type == -1)
+ return NULL;
+
+ node = g_new0(CONFIG_NODE, 1);
+ parent->value = g_slist_append(parent->value, node);
+
+ node->type = new_type;
+ node->key = key == NULL ? NULL : g_strdup(key);
+
+ return node;
+}
+
+/* find the section with the whole path.
+ create the path if necessary `create' is TRUE. */
+CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int create)
+{
+ CONFIG_NODE *node;
+ char **list, **tmp, *str;
+ int is_list, new_type;
+
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ if (section == NULL || *section == '\0')
+ return rec->mainnode;
+
+ /* check if it already exists in cache */
+ node = g_hash_table_lookup(rec->cache, section);
+ if (node != NULL) return node;
+
+ new_type = -1;
+
+ node = rec->mainnode;
+ list = g_strsplit(section, "/", -1);
+ for (tmp = list; *tmp != NULL; tmp++) {
+ is_list = **tmp == '(';
+ if (create) new_type = is_list ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+
+ node = config_node_section(node, *tmp + is_list, new_type);
+ if (node == NULL) {
+ g_strfreev(list);
+ return NULL;
+ }
+ }
+ g_strfreev(list);
+
+ /* save to cache */
+ str = g_strdup(section);
+ g_hash_table_insert(rec->cache, str, node);
+ g_hash_table_insert(rec->cache_nodes, node, str);
+ return node;
+}
+
+char *config_get_str(CONFIG_REC *rec, const char *section, const char *key, const char *def)
+{
+ CONFIG_NODE *parent, *node;
+ char *path;
+
+ g_return_val_if_fail(rec != NULL, (char *) def);
+ g_return_val_if_fail(key != NULL, (char *) def);
+
+ /* check if it already exists in cache */
+ path = g_strconcat(section == NULL ? "" : section, "/", key, NULL);
+ node = g_hash_table_lookup(rec->cache, path);
+
+ if (node != NULL)
+ g_free(path);
+ else {
+ parent = config_node_traverse(rec, section, FALSE);
+ node = parent == NULL ? NULL :
+ config_node_find(parent, key);
+
+ /* save to cache */
+ if (node == NULL)
+ g_free(path);
+ else {
+ g_hash_table_insert(rec->cache, path, node);
+ g_hash_table_insert(rec->cache_nodes, node, path);
+ }
+ }
+
+ return (node == NULL || !has_node_value(node)) ? (char *) def : node->value;
+}
+
+int config_get_int(CONFIG_REC *rec, const char *section, const char *key, int def)
+{
+ char *str;
+
+ str = config_get_str(rec, section, key, NULL);
+ if (str == NULL) return def;
+
+ return atoi(str);
+}
+
+int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int def)
+{
+ char *str;
+
+ str = config_get_str(rec, section, key, NULL);
+ if (str == NULL) return def;
+
+ return toupper(*str) == 'T' || toupper(*str) == 'Y';
+}
+
+/* Return value of key `value_key' from list item where `key' is `value' */
+const char *config_list_find(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key)
+{
+ CONFIG_NODE *node;
+
+ node = config_list_find_node(rec, section, key, value, value_key);
+ return node != NULL && node->type == NODE_TYPE_KEY ?
+ node->value : NULL;
+}
+
+/* Like config_list_find(), but return node instead of it's value */
+CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key)
+{
+ CONFIG_NODE *node, *keynode;
+ GSList *tmp;
+
+ g_return_val_if_fail(rec != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(value_key != NULL, NULL);
+
+ node = config_node_traverse(rec, section, FALSE);
+ if (node == NULL || !is_node_list(node)) return NULL;
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ node = tmp->data;
+
+ if (node->type != NODE_TYPE_BLOCK)
+ continue;
+
+ /* key matches value? */
+ keynode = config_node_find(node, key);
+ if (keynode == NULL || keynode->type != NODE_TYPE_KEY ||
+ g_strcasecmp(keynode->value, value) != 0) continue;
+
+ return config_node_find(node, value_key);
+ }
+
+ return NULL;
+}
+
+char *config_node_get_str(CONFIG_NODE *parent, const char *key, const char *def)
+{
+ CONFIG_NODE *node;
+
+ if (parent == NULL) return (char *) def;
+
+ node = config_node_find(parent, key);
+ return (char *) ((node != NULL && has_node_value(node)) ?
+ node->value : def);
+}
+
+int config_node_get_int(CONFIG_NODE *parent, const char *key, int def)
+{
+ char *str;
+
+ str = config_node_get_str(parent, key, NULL);
+ if (str == NULL) return def;
+
+ return atoi(str);
+}
+
+int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def)
+{
+ char *str;
+
+ 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');
+}
+
+/* Get the value of keys `key' and `key_value' and put them to
+ `ret_key' and `ret_value'. Returns -1 if not found. */
+int config_node_get_keyvalue(CONFIG_NODE *node, const char *key, const char *value_key, char **ret_key, char **ret_value)
+{
+ CONFIG_NODE *keynode, *valuenode;
+ GSList *tmp;
+
+ g_return_val_if_fail(node != NULL, -1);
+ g_return_val_if_fail(key != NULL, -1);
+ g_return_val_if_fail(value_key != NULL, -1);
+ g_return_val_if_fail(ret_key != NULL, -1);
+ g_return_val_if_fail(ret_value != NULL, -1);
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ node = tmp->data;
+
+ if (node->type != NODE_TYPE_BLOCK)
+ continue;
+
+ keynode = config_node_find(node, key);
+ if (keynode == NULL || keynode->type != NODE_TYPE_KEY)
+ continue;
+
+ valuenode = config_node_find(node, value_key);
+
+ *ret_key = keynode->key;
+ *ret_value = valuenode != NULL && valuenode->type == NODE_TYPE_KEY ?
+ valuenode->value : NULL;
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Return all values from from the list `node' in a g_strsplit() array */
+char **config_node_get_list(CONFIG_NODE *node)
+{
+ GString *values;
+ GSList *tmp;
+ char **ret;
+
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(is_node_list(node), NULL);
+
+ /* put values to string */
+ values = g_string_new(NULL);
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ node = tmp->data;
+
+ if (node->type == NODE_TYPE_VALUE)
+ g_string_sprintfa(values, "%s ", (char *) node->value);
+ }
+
+ /* split the values to **str array */
+ if (values->len == 0)
+ ret = NULL;
+ else {
+ g_string_truncate(values, values->len-1);
+ ret = g_strsplit(values->str, " ", -1);
+ }
+
+ g_string_free(values, TRUE);
+ return ret;
+}
+
+/* Returns n'th node from list. */
+CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index)
+{
+ GSList *tmp;
+
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(is_node_list(node), NULL);
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next, index--) {
+ if (index == 0)
+ return tmp->data;
+ }
+
+ return NULL;
+}
--- /dev/null
+#ifndef __ICONFIG_H
+#define __ICONFIG_H
+
+enum {
+ NODE_TYPE_KEY,
+ NODE_TYPE_VALUE,
+ NODE_TYPE_BLOCK,
+ NODE_TYPE_LIST,
+ NODE_TYPE_COMMENT
+};
+
+#define has_node_value(a) \
+ ((a)->type == NODE_TYPE_KEY || (a)->type == NODE_TYPE_VALUE)
+#define is_node_list(a) \
+ ((a)->type == NODE_TYPE_BLOCK || (a)->type == NODE_TYPE_LIST)
+
+struct _CONFIG_NODE {
+ int type;
+ char *key;
+ void *value;
+};
+
+/* a = { x=y; y=z; }
+
+ node1: type = NODE_TYPE_BLOCK, key = "a", value = (GSList *) nodes
+ nodes: (node2, node3)
+ node2: type = NODE_TYPE_KEY, key = "x", value = (char *) "y"
+ node3: type = NODE_TYPE_KEY, key = "y", value = (char *) "z"
+
+ b = ( a, { b=c; d=e; } )
+
+ node1: type = NODE_TYPE_LIST, key = "b", value = (GSList *) nodes
+ nodes: (node2, node3)
+ node2: type = NODE_TYPE_VALUE, key = NULL, value = (char *) "a"
+ node4: type = NODE_TYPE_BLOCK, key = NULL, value = (GSList *) nodes2
+ nodes2: (node4, node5)
+ node4: type = NODE_TYPE_KEY, key = "b", value = (char *) "c"
+ node5: type = NODE_TYPE_KEY, key = "d", value = (char *) "e"
+
+ Comments node has key=NULL and value is the comment line. Empty lines are
+ also in comments so they won't be forgotten when the config file is
+ written.
+
+*/
+
+struct _CONFIG_REC {
+ char *fname;
+ int handle;
+ int create_mode;
+ int modifycounter; /* increase every time something is changed */
+
+ char *last_error;
+ CONFIG_NODE *mainnode;
+ GHashTable *cache; /* path -> node (for querying) */
+ GHashTable *cache_nodes; /* node -> path (for removing) */
+
+ GScanner *scanner;
+
+ /* while writing to configuration file.. */
+ int tmp_indent_level; /* indentation position */
+ int tmp_last_lf; /* last character was a line feed */
+};
+
+/* Open configuration. The file is created if it doesn't exist, unless
+ `create_mode' is -1. `fname' can be NULL if you just want to use
+ config_parse_data() */
+CONFIG_REC *config_open(const char *fname, int create_mode);
+/* Release all memory used by configuration */
+void config_close(CONFIG_REC *rec);
+/* Change file name of config file */
+void config_change_file_name(CONFIG_REC *rec, const char *fname, int create_mode);
+
+/* Parse configuration file */
+int config_parse(CONFIG_REC *rec);
+/* Parse configuration found from `data'. `input_name' is specifies the
+ "configuration name" which is displayed in error messages. */
+int config_parse_data(CONFIG_REC *rec, const char *data, const char *input_name);
+
+/* Write configuration file. Write to `fname' if it's not NULL.
+ If `create_mode' is -1, use the one that was given to config_open(). */
+int config_write(CONFIG_REC *rec, const char *fname, int create_mode);
+
+#define config_last_error(rec) \
+ (rec)->last_error
+
+/* Getting values
+
+ `section' is something like "maingroup/key/subkey", or with lists
+ "maingroup/(list/subkey"
+
+ `def' is returned if the value is not found. */
+char *config_get_str(CONFIG_REC *rec, const char *section, const char *key, const char *def);
+int config_get_int(CONFIG_REC *rec, const char *section, const char *key, int def);
+int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int def);
+
+/* Return value of key `value_key' from list item where `key' is `value' */
+const char *config_list_find(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key);
+/* Like config_list_find(), but return node instead of it's value */
+CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key);
+/* Returns n'th node from list. */
+CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index);
+
+/* 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);
+int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int value);
+
+/* Handling the configuration directly with nodes -
+ useful when you need to read all values in a block/list. */
+CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key);
+/* Find the section from node - if not found create it unless new_type is -1.
+ You can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */
+CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type);
+/* Find the section with the whole path.
+ Create the path if necessary `create' is TRUE. */
+CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int create);
+/* Get the value of keys `key' and `key_value' and put them to
+ `ret_key' and `ret_value'. Returns -1 if not found. */
+int config_node_get_keyvalue(CONFIG_NODE *node, const char *key, const char *value_key, char **ret_key, char **ret_value);
+/* Return all values from from the list `node' in a g_strsplit() array */
+char **config_node_get_list(CONFIG_NODE *node);
+/* Add all values in `array' to `node' */
+void config_node_add_list(CONFIG_REC *rec, CONFIG_NODE *node, char **array);
+
+char *config_node_get_str(CONFIG_NODE *parent, const char *key, const char *def);
+int config_node_get_int(CONFIG_NODE *parent, const char *key, int def);
+int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def);
+
+void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value);
+void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value);
+void config_node_set_bool(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value);
+
+/* Remove one node from block/list.
+ ..set_str() with value = NULL does the same. */
+void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node);
+/* Remove n'th node from a list */
+void config_node_list_remove(CONFIG_REC *rec, CONFIG_NODE *node, int index);
+
+/* Clear all data inside node, but leave the node */
+void config_node_clear(CONFIG_REC *rec, CONFIG_NODE *node);
+/* Clear the entire configuration */
+void config_nodes_remove_all(CONFIG_REC *rec);
+
+#endif
--- /dev/null
+#include "common.h"
+#include "iconfig.h"
+
+/* private */
+int config_error(CONFIG_REC *rec, const char *msg);
+
--- /dev/null
+/*
+ parse.c : irssi configuration - parse configuration file
+
+ Copyright (C) 1999 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"
+
+static int g_istr_equal(gconstpointer v, gconstpointer v2)
+{
+ return g_strcasecmp((const char *) v, (const char *) v2) == 0;
+}
+
+/* a char* hash function from ASU */
+static unsigned int g_istr_hash(gconstpointer v)
+{
+ const char *s = (const char *) v;
+ unsigned int h = 0, g;
+
+ while (*s != '\0') {
+ h = (h << 4) + toupper(*s);
+ if ((g = h & 0xf0000000UL)) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ s++;
+ }
+
+ return h /* % M */;
+}
+
+int config_error(CONFIG_REC *rec, const char *msg)
+{
+ g_free_and_null(rec->last_error);
+ rec->last_error = g_strdup(msg);
+ return -1;
+}
+
+static int node_add_comment(CONFIG_NODE *parent, const char *str)
+{
+ CONFIG_NODE *node;
+
+ g_return_val_if_fail(parent != NULL, -1);
+
+ if (!is_node_list(parent))
+ return -1;
+
+ node = g_new0(CONFIG_NODE, 1);
+ node->type = NODE_TYPE_COMMENT;
+ node->value = str == NULL ? NULL : g_strdup(str);
+
+ parent->value = g_slist_append(parent->value, node);
+ return 0;
+}
+
+/* same as g_scanner_get_next_token() except skips and reads the comments */
+static void config_parse_get_token(GScanner *scanner, CONFIG_NODE *node)
+{
+ int prev_empty = FALSE;
+
+ for (;;) {
+ g_scanner_get_next_token(scanner);
+
+ if (scanner->token == G_TOKEN_COMMENT_SINGLE)
+ node_add_comment(node, scanner->value.v_string);
+ else if (scanner->token == '\n') {
+ if (prev_empty) node_add_comment(node, NULL);
+ } 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;
+ }
+
+ prev_empty = TRUE;
+ }
+}
+
+/* same as g_scanner_peek_next_token() except skips and reads the comments */
+static void config_parse_peek_token(GScanner *scanner, CONFIG_NODE *node)
+{
+ int prev_empty = FALSE;
+
+ for (;;) {
+ g_scanner_peek_next_token(scanner);
+
+ if (scanner->next_token == G_TOKEN_COMMENT_SINGLE)
+ node_add_comment(node, scanner->next_value.v_string);
+ else if (scanner->next_token == '\n') {
+ if (prev_empty) node_add_comment(node, NULL);
+ } else
+ break;
+
+ prev_empty = TRUE;
+ g_scanner_get_next_token(scanner);
+ }
+}
+
+/* get optional token, optionally warn if it's missing */
+static void config_parse_warn_missing(CONFIG_REC *rec, CONFIG_NODE *node,
+ GTokenType expected_token, int print_warning)
+{
+ config_parse_peek_token(rec->scanner, node);
+ if (rec->scanner->next_token == expected_token) {
+ g_scanner_get_next_token(rec->scanner);
+ return;
+ }
+
+ if (print_warning)
+ g_scanner_warn(rec->scanner, "Warning: missing '%c'", expected_token);
+}
+
+static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, GTokenType expect);
+
+static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
+{
+ CONFIG_NODE *newnode;
+ GTokenType last_char;
+ int print_warning;
+ char *key;
+
+ g_return_val_if_fail(rec != NULL, G_TOKEN_ERROR);
+ g_return_val_if_fail(node != NULL, G_TOKEN_ERROR);
+
+ config_parse_get_token(rec->scanner, node);
+
+ last_char = (GTokenType) (node->type == NODE_TYPE_LIST ? ',' : ';');
+
+ /* key */
+ key = NULL;
+ if (node->type != NODE_TYPE_LIST &&
+ (rec->scanner->token == G_TOKEN_STRING)) {
+ key = g_strdup(rec->scanner->value.v_string);
+
+ config_parse_warn_missing(rec, node, '=', TRUE);
+ config_parse_get_token(rec->scanner, node);
+ }
+
+ switch (rec->scanner->token) {
+ case G_TOKEN_STRING:
+ /* value */
+ config_node_set_str(rec, node, key, rec->scanner->value.v_string);
+ g_free_not_null(key);
+
+ print_warning = TRUE;
+ if (node->type == NODE_TYPE_LIST) {
+ /* if it's last item it doesn't need comma */
+ config_parse_peek_token(rec->scanner, node);
+ if (rec->scanner->next_token == ')')
+ print_warning = FALSE;
+ }
+
+ config_parse_warn_missing(rec, node, last_char, print_warning);
+ break;
+
+ case '{':
+ /* block */
+ if (key == NULL && node->type != NODE_TYPE_LIST)
+ return G_TOKEN_ERROR;
+
+ newnode = config_node_section(node, key, NODE_TYPE_BLOCK);
+ config_parse_loop(rec, newnode, (GTokenType) '}');
+ g_free_not_null(key);
+
+ config_parse_get_token(rec->scanner, node);
+ if (rec->scanner->token != '}')
+ return (GTokenType) '}';
+
+ config_parse_warn_missing(rec, node, last_char, FALSE);
+ break;
+
+ case '(':
+ /* list */
+ if (key == NULL)
+ return G_TOKEN_ERROR;
+ newnode = config_node_section(node, key, NODE_TYPE_LIST);
+ config_parse_loop(rec, newnode, (GTokenType) ')');
+ g_free_not_null(key);
+
+ config_parse_get_token(rec->scanner, node);
+ if (rec->scanner->token != ')')
+ return (GTokenType) ')';
+
+ config_parse_warn_missing(rec, node, last_char, FALSE);
+ break;
+
+ default:
+ /* error */
+ g_free_not_null(key);
+ return G_TOKEN_STRING;
+ }
+
+ return G_TOKEN_NONE;
+}
+
+static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, GTokenType expect)
+{
+ GTokenType expected_token;
+
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(node != NULL);
+
+ for (;;) {
+ config_parse_peek_token(rec->scanner, node);
+ if (rec->scanner->next_token == expect ||
+ rec->scanner->next_token == G_TOKEN_EOF) break;
+
+ expected_token = config_parse_symbol(rec, node);
+ if (expected_token != G_TOKEN_NONE) {
+ if (expected_token == G_TOKEN_ERROR)
+ expected_token = G_TOKEN_NONE;
+ g_scanner_unexp_token(rec->scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE);
+ }
+ }
+}
+
+static void config_parse_error_func(GScanner *scanner, char *message, int is_error)
+{
+ CONFIG_REC *rec = scanner->user_data;
+ char *old;
+
+ old = rec->last_error;
+ rec->last_error = g_strdup_printf("%s%s:%d: %s%s\n",
+ old == NULL ? "" : old,
+ scanner->input_name, scanner->line,
+ is_error ? "error: " : "",
+ message);
+ g_free_not_null(old);
+}
+
+void config_parse_init(CONFIG_REC *rec, const char *name)
+{
+ GScanner *scanner;
+
+ g_free_and_null(rec->last_error);
+ config_nodes_remove_all(rec);
+
+ rec->scanner = scanner = g_scanner_new(NULL);
+ scanner->config->skip_comment_single = FALSE;
+ scanner->config->cset_skip_characters = " \t";
+ scanner->config->scan_binary = FALSE;
+ scanner->config->scan_octal = FALSE;
+ scanner->config->scan_float = FALSE;
+ scanner->config->scan_string_sq = TRUE;
+ scanner->config->scan_string_dq = TRUE;
+ scanner->config->scan_identifier_1char = TRUE;
+ scanner->config->identifier_2_string = TRUE;
+
+ scanner->user_data = rec;
+ scanner->input_name = name;
+ scanner->msg_handler = (GScannerMsgFunc) config_parse_error_func;
+}
+
+/* Parse configuration file */
+int config_parse(CONFIG_REC *rec)
+{
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(rec->fname != NULL, -1);
+
+ rec->handle = open(rec->fname, O_RDONLY);
+ if (rec->handle == -1)
+ return config_error(rec, g_strerror(errno));
+
+ config_parse_init(rec, rec->fname);
+ g_scanner_input_file(rec->scanner, rec->handle);
+ config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF);
+ g_scanner_destroy(rec->scanner);
+
+ close(rec->handle);
+ rec->handle = -1;
+
+ return rec->last_error == NULL ? 0 : -1;
+}
+
+/* Parse configuration found from `data'. `input_name' is specifies the
+ "configuration name" which is displayed in error messages. */
+int config_parse_data(CONFIG_REC *rec, const char *data, const char *input_name)
+{
+ config_parse_init(rec, input_name);
+ g_scanner_input_text(rec->scanner, data, strlen(data));
+ config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF);
+ g_scanner_destroy(rec->scanner);
+
+ return rec->last_error == NULL ? 0 : -1;
+}
+
+/* Open configuration. The file is created if it doesn't exist, unless
+ `create_mode' is -1. `fname' can be NULL if you just want to use
+ config_parse_data() */
+CONFIG_REC *config_open(const char *fname, int create_mode)
+{
+ CONFIG_REC *rec;
+ int f;
+
+ if (fname != NULL) {
+ f = open(fname, O_RDONLY | (create_mode != -1 ? O_CREAT : 0), create_mode);
+ if (f == -1) return NULL;
+ close(f);
+ }
+
+ rec = g_new0(CONFIG_REC, 1);
+ rec->fname = fname == NULL ? NULL : g_strdup(fname);
+ rec->handle = -1;
+ rec->create_mode = create_mode;
+ rec->mainnode = g_new0(CONFIG_NODE, 1);
+ rec->mainnode->type = NODE_TYPE_BLOCK;
+ rec->cache = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
+ rec->cache_nodes = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+
+ return rec;
+}
+
+/* Release all memory used by configuration */
+void config_close(CONFIG_REC *rec)
+{
+ g_return_if_fail(rec != NULL);
+
+ config_nodes_remove_all(rec);
+ g_free(rec->mainnode);
+
+ if (rec->handle != -1) close(rec->handle);
+ g_hash_table_foreach(rec->cache, (GHFunc) g_free, NULL);
+ g_hash_table_destroy(rec->cache);
+ g_hash_table_destroy(rec->cache_nodes);
+ g_free_not_null(rec->last_error);
+ g_free_not_null(rec->fname);
+ g_free(rec);
+}
+
+/* Change file name of config file */
+void config_change_file_name(CONFIG_REC *rec, const char *fname, int create_mode)
+{
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(fname != NULL);
+
+ g_free_not_null(rec->fname);
+ rec->fname = g_strdup(fname);
+
+ if (create_mode != -1)
+ rec->create_mode = create_mode;
+}
--- /dev/null
+/*
+ set.c : irssi configuration - change settings in memory
+
+ Copyright (C) 1999 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"
+
+static void cache_remove(CONFIG_REC *rec, CONFIG_NODE *node)
+{
+ char *path;
+
+ path = g_hash_table_lookup(rec->cache_nodes, node);
+ if (path != NULL) {
+ g_hash_table_remove(rec->cache, path);
+ g_hash_table_remove(rec->cache_nodes, node);
+ g_free(path);
+ }
+}
+
+void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node)
+{
+ g_return_if_fail(node != NULL);
+
+ if (parent == NULL)
+ parent = rec->mainnode;
+
+ rec->modifycounter++;
+ cache_remove(rec, node);
+ parent->value = g_slist_remove(parent->value, node);
+
+ switch (node->type) {
+ case NODE_TYPE_KEY:
+ case NODE_TYPE_VALUE:
+ case NODE_TYPE_COMMENT:
+ g_free_not_null(node->value);
+ break;
+ case NODE_TYPE_BLOCK:
+ case NODE_TYPE_LIST:
+ while (node->value != NULL)
+ config_node_remove(rec, node, ((GSList *) node->value)->data);
+ break;
+ }
+ g_free_not_null(node->key);
+ g_free(node);
+}
+
+/* Remove n'th node from a list */
+void config_node_list_remove(CONFIG_REC *rec, CONFIG_NODE *node, int index)
+{
+ CONFIG_NODE *child;
+
+ g_return_if_fail(node != NULL);
+ g_return_if_fail(is_node_list(node));
+
+ child = config_node_index(node, index);
+ if (child != NULL) config_node_remove(rec, node, child);
+}
+
+/* Clear all data inside node, but leave the node */
+void config_node_clear(CONFIG_REC *rec, CONFIG_NODE *node)
+{
+ g_return_if_fail(node != NULL);
+ g_return_if_fail(is_node_list(node));
+
+ while (node->value != NULL)
+ config_node_remove(rec, node, ((GSList *) node->value)->data);
+}
+
+void config_nodes_remove_all(CONFIG_REC *rec)
+{
+ g_return_if_fail(rec != NULL);
+
+ while (rec->mainnode->value != NULL)
+ config_node_remove(rec, rec->mainnode, ((GSList *) rec->mainnode->value)->data);
+}
+
+void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value)
+{
+ CONFIG_NODE *node;
+ int no_key;
+
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(parent != NULL);
+
+ no_key = key == NULL;
+ node = no_key ? NULL : config_node_find(parent, key);
+
+ if (value == NULL) {
+ /* remove the key */
+ if (node != NULL) config_node_remove(rec, parent, node);
+ return;
+ }
+
+ if (node != NULL) {
+ if (strcmp(node->value, value) == 0)
+ return;
+ g_free(node->value);
+ } else {
+ node = g_new0(CONFIG_NODE, 1);
+ parent->value = g_slist_append(parent->value, node);
+
+ node->type = no_key ? NODE_TYPE_VALUE : NODE_TYPE_KEY;
+ node->key = no_key ? NULL : g_strdup(key);
+ }
+
+ node->value = g_strdup(value);
+ rec->modifycounter++;
+}
+
+void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value)
+{
+ char str[MAX_INT_STRLEN];
+
+ g_snprintf(str, sizeof(str), "%d", value);
+ config_node_set_str(rec, parent, key, str);
+}
+
+void config_node_set_bool(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value)
+{
+ config_node_set_str(rec, parent, key, value ? "yes" : "no");
+}
+
+int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value)
+{
+ CONFIG_NODE *parent;
+
+ g_return_val_if_fail(rec != NULL, -1);
+
+ parent = config_node_traverse(rec, section, TRUE);
+ if (parent == NULL) return -1;
+
+ config_node_set_str(rec, parent, key, value);
+ return 0;
+}
+
+int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value)
+{
+ char str[MAX_INT_STRLEN];
+
+ g_snprintf(str, sizeof(str), "%d", value);
+ return config_set_str(rec, section, key, str);
+}
+
+int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int value)
+{
+ return config_set_str(rec, section, key, value ? "yes" : "no");
+}
+
+/* Add all values in `array' to `node' */
+void config_node_add_list(CONFIG_REC *rec, CONFIG_NODE *node, char **array)
+{
+ char **tmp;
+
+ for (tmp = array; *tmp != NULL; tmp++)
+ config_node_set_str(rec, node, NULL, *tmp);
+}
--- /dev/null
+/*
+ write.c : irssi configuration - write configuration file
+
+ Copyright (C) 1999 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"
+
+/* maximum length of lines in config file before splitting them to multiple lines */
+#define MAX_CHARS_IN_LINE 70
+
+#define CONFIG_INDENT_SIZE 2
+static const char *indent_block = " "; /* needs to be the same size as CONFIG_INDENT_SIZE! */
+
+/* write needed amount of indentation to the start of the line */
+static int config_write_indent(CONFIG_REC *rec)
+{
+ int n;
+
+ for (n = 0; n < rec->tmp_indent_level/CONFIG_INDENT_SIZE; n++) {
+ if (write(rec->handle, indent_block, CONFIG_INDENT_SIZE) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int config_write_str(CONFIG_REC *rec, const char *str)
+{
+ const char *strpos, *p;
+
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(str != NULL, -1);
+
+ strpos = str;
+ while (*strpos != '\0') {
+ /* fill the indentation */
+ if (rec->tmp_last_lf && rec->tmp_indent_level > 0 &&
+ *str != '\n') {
+ if (config_write_indent(rec) == -1)
+ return -1;
+ }
+
+ p = strchr(strpos, '\n');
+ if (p == NULL) {
+ if (write(rec->handle, strpos, strlen(strpos)) == -1)
+ return -1;
+ strpos = "";
+ rec->tmp_last_lf = FALSE;
+ } else {
+ if (write(rec->handle, strpos, (int) (p-strpos)+1) == -1)
+ return -1;
+ strpos = p+1;
+ rec->tmp_last_lf = TRUE;
+ }
+ }
+
+ return 0;
+}
+
+static int config_has_specials(const char *text)
+{
+ g_return_val_if_fail(text != NULL, FALSE);
+
+ while (*text != '\0') {
+ if (!isalnum((int) *text) && *text != '_')
+ return TRUE;
+ text++;
+ }
+
+ return FALSE;
+}
+
+static int get_octal(int decimal)
+{
+ int octal, pos;
+
+ octal = 0; pos = 0;
+ while (decimal > 0) {
+ octal += (decimal & 7)*(pos == 0 ? 1 : pos);
+ decimal /= 8;
+ pos += 10;
+ }
+
+ return octal;
+}
+
+static char *config_escape_string(const char *text)
+{
+ GString *str;
+ char *ret;
+
+ g_return_val_if_fail(text != NULL, NULL);
+
+ str = g_string_new("\"");
+ while (*text != '\0') {
+ if (*text == '\\' || *text == '"')
+ g_string_sprintfa(str, "\\%c", *text);
+ else if ((unsigned char) *text < 32)
+ g_string_sprintfa(str, "\\%03d", get_octal(*text));
+ else
+ g_string_append_c(str, *text);
+ text++;
+ }
+
+ g_string_append_c(str, '"');
+
+ ret = str->str;
+ g_string_free(str, FALSE);
+ return ret;
+}
+
+static int config_write_word(CONFIG_REC *rec, const char *word, int string)
+{
+ char *str;
+ int ret;
+
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(word != NULL, -1);
+
+ if (!string && !config_has_specials(word))
+ return config_write_str(rec, word);
+
+ str = config_escape_string(word);
+ ret = config_write_str(rec, str);
+ g_free(str);
+
+ return ret;
+}
+
+static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds);
+
+static int config_write_node(CONFIG_REC *rec, CONFIG_NODE *node, int line_feeds)
+{
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(node != NULL, -1);
+
+ switch (node->type) {
+ case NODE_TYPE_KEY:
+ if (config_write_word(rec, node->key, FALSE) == -1 ||
+ config_write_str(rec, " = ") == -1 ||
+ config_write_word(rec, node->value, TRUE) == -1)
+ return -1;
+ break;
+ case NODE_TYPE_VALUE:
+ if (config_write_word(rec, node->value, TRUE) == -1)
+ return -1;
+ break;
+ case NODE_TYPE_BLOCK:
+ /* key = { */
+ if (node->key != NULL) {
+ if (config_write_word(rec, node->key, FALSE) == -1 ||
+ config_write_str(rec, " = ") == -1)
+ return -1;
+ }
+ if (config_write_str(rec, line_feeds ? "{\n" : "{ ") == -1)
+ return -1;
+
+ /* ..block.. */
+ rec->tmp_indent_level += CONFIG_INDENT_SIZE;
+ if (config_write_block(rec, node, FALSE, line_feeds) == -1)
+ return -1;
+ rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
+
+ /* }; */
+ if (config_write_str(rec, "}") == -1)
+ return -1;
+ break;
+ case NODE_TYPE_LIST:
+ /* key = ( */
+ if (node->key != NULL) {
+ if (config_write_word(rec, node->key, FALSE) == -1 ||
+ config_write_str(rec, " = ") == -1)
+ return -1;
+ }
+ if (config_write_str(rec, line_feeds ? "(\n" : "( ") == -1)
+ return -1;
+
+ /* ..list.. */
+ rec->tmp_indent_level += CONFIG_INDENT_SIZE;
+ if (config_write_block(rec, node, TRUE, line_feeds) == -1)
+ return -1;
+ rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
+
+ /* ); */
+ if (config_write_str(rec, ")") == -1)
+ return -1;
+ break;
+ case NODE_TYPE_COMMENT:
+ if (node->value == NULL)
+ break;
+
+ if (config_write_str(rec, "#") == -1 ||
+ config_write_str(rec, node->value) == -1)
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node);
+
+static int config_node_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
+{
+ int len;
+
+ switch (node->type) {
+ case NODE_TYPE_KEY:
+ /* "key = value; " */
+ len = 5 + strlen(node->key) + strlen(node->value);
+ break;
+ case NODE_TYPE_VALUE:
+ /* "value, " */
+ len = 2 + strlen(node->value);
+ break;
+ case NODE_TYPE_BLOCK:
+ case NODE_TYPE_LIST:
+ /* "{ list }; " */
+ len = 6;
+ if (node->key != NULL) len += strlen(node->key);
+ len += config_block_get_length(rec, node);
+ break;
+ default:
+ /* comments always split the line */
+ len = 1000;
+ break;
+ }
+
+ return len;
+}
+
+/* return the number of characters `node' and it's subnodes take
+ if written to file */
+static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
+{
+ GSList *tmp;
+ int len;
+
+ len = 0;
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ CONFIG_NODE *subnode = tmp->data;
+
+ len += config_node_get_length(rec, subnode);
+ if (len > MAX_CHARS_IN_LINE) return len;
+ }
+
+ return len;
+}
+
+/* check if `node' and it's subnodes fit in one line in the config file */
+static int config_block_fit_one_line(CONFIG_REC *rec, CONFIG_NODE *node)
+{
+ g_return_val_if_fail(rec != NULL, 0);
+ g_return_val_if_fail(node != NULL, 0);
+
+ return rec->tmp_indent_level +
+ config_node_get_length(rec, node) <= MAX_CHARS_IN_LINE;
+}
+
+static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds)
+{
+ GSList *tmp;
+ int list_line_feeds, node_line_feeds;
+
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(node != NULL, -1);
+ g_return_val_if_fail(is_node_list(node), -1);
+
+ list_line_feeds = !config_block_fit_one_line(rec, node);
+
+ if (!line_feeds && list_line_feeds)
+ config_write_str(rec, "\n");
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ CONFIG_NODE *subnode = tmp->data;
+
+ node_line_feeds = !line_feeds ? FALSE : !config_block_fit_one_line(rec, subnode);
+ if (config_write_node(rec, subnode, node_line_feeds) == -1)
+ return -1;
+
+ if (subnode->type == NODE_TYPE_COMMENT)
+ config_write_str(rec, "\n");
+ else if (list) {
+ if (tmp->next != NULL)
+ config_write_str(rec, list_line_feeds ? ",\n" : ", ");
+ else
+ config_write_str(rec, list_line_feeds ? "\n" : " ");
+ } else {
+ config_write_str(rec, list_line_feeds ? ";\n" : "; ");
+ }
+ }
+
+ return 0;
+}
+
+/* Write configuration file. Write to `fname' if it's not NULL. */
+int config_write(CONFIG_REC *rec, const char *fname, int create_mode)
+{
+ int ret;
+
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(fname != NULL || rec->fname != NULL, -1);
+ g_return_val_if_fail(create_mode != -1 || rec->create_mode != -1, -1);
+
+ if (rec->handle != -1)
+ close(rec->handle);
+
+ rec->handle = open(fname != NULL ? fname : rec->fname,
+ O_WRONLY | O_TRUNC | O_CREAT,
+ create_mode != -1 ? create_mode : rec->create_mode);
+ if (rec->handle == -1)
+ return config_error(rec, g_strerror(errno));
+
+ rec->tmp_indent_level = 0;
+ rec->tmp_last_lf = TRUE;
+ ret = config_write_block(rec, rec->mainnode, FALSE, TRUE);
+ if (ret == -1) {
+ /* write error */
+ config_error(rec, errno == 0 ? "bug" : g_strerror(errno));
+ }
+
+ close(rec->handle);
+ rec->handle = -1;
+
+ return ret;
+}
--- /dev/null
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
--- /dev/null
+noinst_LIBRARIES = libpopt.a
+
+INCLUDES = \
+ $(GLIB_CFLAGS) \
+ -I$(top_srcdir)/src
+
+libpopt_a_SOURCES = \
+ findme.c popt.c poptconfig.c popthelp.c poptparse.c
+
+noinst_HEADERS = \
+ findme.h popt.h poptint.h
--- /dev/null
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "common.h"
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include <libc.h>
+#endif
+
+#include "findme.h"
+
+char * findProgramPath(char * argv0) {
+ char * path = getenv("PATH");
+ char * pathbuf;
+ char * start, * chptr;
+ char * buf;
+
+ /* If there is a / in the argv[0], it has to be an absolute
+ path */
+ if (strchr(argv0, '/'))
+ return g_strdup(argv0);
+
+ if (!path) return NULL;
+
+ start = pathbuf = g_strdup(path);
+ buf = g_malloc(strlen(path) + strlen(argv0) + 2);
+
+ chptr = NULL;
+ do {
+ if ((chptr = strchr(start, ':')))
+ *chptr = '\0';
+ sprintf(buf, "%s/%s", start, argv0);
+
+#ifndef WIN32
+ if (!access(buf, X_OK)) {
+ g_free(pathbuf);
+ return buf;
+ }
+#endif
+
+ if (chptr)
+ start = chptr + 1;
+ else
+ start = NULL;
+ } while (start && *start);
+
+ g_free(pathbuf);
+ free(buf);
+
+ return NULL;
+}
--- /dev/null
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+char * findProgramPath(char * argv0);
+
+#endif
--- /dev/null
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "common.h"
+
+#include "findme.h"
+#include "popt.h"
+#include "poptint.h"
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
+ if (con->execPath) free(con->execPath);
+ con->execPath = g_strdup(path);
+ con->execAbsolute = allowAbsolute;
+}
+
+static void invokeCallbacks(poptContext con, const struct poptOption * table,
+ int post) {
+ const struct poptOption * opt = table;
+ poptCallbackType cb;
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ invokeCallbacks(con, opt->arg, post);
+ } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
+ ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
+ ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
+ cb = (poptCallbackType)opt->arg;
+ cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
+ NULL, NULL, opt->descrip);
+ }
+ opt++;
+ }
+}
+
+poptContext poptGetContext(char * name, int argc, char ** argv,
+ const struct poptOption * options, int flags) {
+ poptContext con = malloc(sizeof(*con));
+
+ memset(con, 0, sizeof(*con));
+
+ con->os = con->optionStack;
+ con->os->argc = argc;
+ con->os->argv = argv;
+
+ if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+ con->os->next = 1; /* skip argv[0] */
+
+ con->leftovers = malloc(sizeof(char *) * (argc + 1));
+ con->options = options;
+ con->finalArgv = malloc(sizeof(*con->finalArgv) * (argc * 2));
+ con->finalArgvAlloced = argc * 2;
+ con->flags = flags;
+ con->execAbsolute = 1;
+
+ if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+ con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+ if (name)
+ con->appName = strcpy(malloc(strlen(name) + 1), name);
+
+ invokeCallbacks(con, con->options, 0);
+
+ return con;
+}
+
+void poptResetContext(poptContext con) {
+ int i;
+
+ con->os = con->optionStack;
+ con->os->currAlias = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->nextArg = NULL;
+ con->os->next = 1; /* skip argv[0] */
+
+ con->numLeftovers = 0;
+ con->nextLeftover = 0;
+ con->restLeftover = 0;
+ con->doExec = NULL;
+
+ for (i = 0; i < con->finalArgvCount; i++)
+ free(con->finalArgv[i]);
+
+ con->finalArgvCount = 0;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleExec(poptContext con, char * longName, char shortName) {
+ int i;
+
+ i = con->numExecs - 1;
+ if (longName) {
+ while (i >= 0 && (!con->execs[i].longName ||
+ strcmp(con->execs[i].longName, longName))) i--;
+ } else {
+ while (i >= 0 &&
+ con->execs[i].shortName != shortName) i--;
+ }
+
+ if (i < 0) return 0;
+
+ if (con->flags & POPT_CONTEXT_NO_EXEC)
+ return 1;
+
+ if (!con->doExec) {
+ con->doExec = con->execs + i;
+ return 1;
+ }
+
+ /* We already have an exec to do; remember this option for next
+ time 'round */
+ if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ i = con->finalArgvCount++;
+ con->finalArgv[i] = malloc((longName ? strlen(longName) : 0) + 3);
+ if (longName)
+ sprintf(con->finalArgv[i], "--%s", longName);
+ else
+ sprintf(con->finalArgv[i], "-%c", shortName);
+
+ return 1;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(poptContext con, char * longName, char shortName,
+ char * nextCharArg) {
+ int i;
+
+ if (con->os->currAlias && con->os->currAlias->longName && longName &&
+ !strcmp(con->os->currAlias->longName, longName))
+ return 0;
+ if (con->os->currAlias && shortName &&
+ shortName == con->os->currAlias->shortName)
+ return 0;
+
+ i = con->numAliases - 1;
+ if (longName) {
+ while (i >= 0 && (!con->aliases[i].longName ||
+ strcmp(con->aliases[i].longName, longName))) i--;
+ } else {
+ while (i >= 0 &&
+ con->aliases[i].shortName != shortName) i--;
+ }
+
+ if (i < 0) return 0;
+
+ if ((con->os - con->optionStack + 1)
+ == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ if (nextCharArg && *nextCharArg)
+ con->os->nextCharArg = nextCharArg;
+
+ con->os++;
+ con->os->next = 0;
+ con->os->stuffed = 0;
+ con->os->nextArg = con->os->nextCharArg = NULL;
+ con->os->currAlias = con->aliases + i;
+ con->os->argc = con->os->currAlias->argc;
+ con->os->argv = con->os->currAlias->argv;
+
+ return 1;
+}
+
+static void execCommand(poptContext con) {
+ char ** argv;
+ int pos = 0;
+ char * script = con->doExec->script;
+
+ argv = malloc(sizeof(*argv) *
+ (6 + con->numLeftovers + con->finalArgvCount));
+
+ if (!con->execAbsolute && strchr(script, '/')) return;
+
+ if (!strchr(script, '/') && con->execPath) {
+ argv[pos] = g_strdup_printf("%s/%s", con->execPath, script);
+ } else {
+ argv[pos] = script;
+ }
+ pos++;
+
+ argv[pos] = findProgramPath(con->os->argv[0]);
+ if (argv[pos]) pos++;
+ argv[pos++] = ";";
+
+ memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
+ pos += con->finalArgvCount;
+
+ if (con->numLeftovers) {
+ argv[pos++] = "--";
+ memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
+ pos += con->numLeftovers;
+ }
+
+ argv[pos++] = NULL;
+
+#ifdef __hpux
+ setresuid(getuid(), getuid(),-1);
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX sez' Timur Bakeyev <mc@bat.ru>
+ * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
+ */
+#if defined(HAVE_SETUID)
+ setuid(getuid());
+#elif defined (HAVE_SETREUID)
+ setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
+#else
+ ; /* Can't drop privileges */
+#endif
+#endif
+
+ execvp(argv[0], argv);
+}
+
+static const struct poptOption * findOption(const struct poptOption * table,
+ const char * longName,
+ char shortName,
+ poptCallbackType * callback,
+ void ** callbackData,
+ int singleDash) {
+ const struct poptOption * opt = table;
+ const struct poptOption * opt2;
+ const struct poptOption * cb = NULL;
+
+ /* This happens when a single - is given */
+ if (singleDash && !shortName && !*longName)
+ shortName = '-';
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ opt2 = findOption(opt->arg, longName, shortName, callback,
+ callbackData, singleDash);
+ if (opt2) {
+ if (*callback && !*callbackData)
+ *callbackData = opt->descrip;
+ return opt2;
+ }
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+ cb = opt;
+ } else if (longName && opt->longName &&
+ (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+ !strcmp(longName, opt->longName)) {
+ break;
+ } else if (shortName && shortName == opt->shortName) {
+ break;
+ }
+ opt++;
+ }
+
+ if (!opt->longName && !opt->shortName) return NULL;
+ *callbackData = NULL;
+ *callback = NULL;
+ if (cb) {
+ *callback = (poptCallbackType)cb->arg;
+ if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
+ *callbackData = cb->descrip;
+ }
+
+ return opt;
+}
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con) {
+ char * optString, * chptr, * localOptString;
+ char * longArg = NULL;
+ char * origOptString, *dup;
+ long aLong;
+ char * end;
+ const struct poptOption * opt = NULL;
+ int done = 0;
+ int i;
+ poptCallbackType cb;
+ void * cbData;
+ int singleDash;
+
+ dup = NULL;
+ while (!done) {
+ if (dup) {
+ g_free(dup);
+ dup = NULL;
+ }
+ while (!con->os->nextCharArg && con->os->next == con->os->argc
+ && con->os > con->optionStack)
+ con->os--;
+ if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+ invokeCallbacks(con, con->options, 1);
+ if (con->doExec) execCommand(con);
+ return -1;
+ }
+
+ if (!con->os->nextCharArg) {
+
+ origOptString = con->os->argv[con->os->next++];
+
+ if (con->restLeftover || *origOptString != '-') {
+ con->leftovers[con->numLeftovers++] = origOptString;
+ if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+ con->restLeftover = 1;
+ continue;
+ }
+
+ /* Make a copy we can hack at */
+ dup = localOptString = optString =
+ g_strdup(origOptString);
+
+ if (!optString[0]) {
+ g_free(dup);
+ return POPT_ERROR_BADOPT;
+ }
+
+ if (optString[1] == '-' && !optString[2]) {
+ con->restLeftover = 1;
+ continue;
+ } else {
+ optString++;
+ if (*optString == '-')
+ singleDash = 0, optString++;
+ else
+ singleDash = 1;
+
+ if (handleAlias(con, optString, '\0', NULL))
+ continue;
+ if (handleExec(con, optString, '\0'))
+ continue;
+
+ chptr = optString;
+ while (*chptr && *chptr != '=') chptr++;
+ if (*chptr == '=') {
+ longArg = origOptString + (chptr - localOptString) + 1;
+ *chptr = '\0';
+ }
+
+ opt = findOption(con->options, optString, '\0', &cb, &cbData,
+ singleDash);
+ if (!opt && !singleDash) {
+ g_free(dup);
+ return POPT_ERROR_BADOPT;
+ }
+ }
+
+ if (!opt)
+ con->os->nextCharArg = origOptString + 1;
+ }
+
+ if (con->os->nextCharArg) {
+ origOptString = con->os->nextCharArg;
+
+ con->os->nextCharArg = NULL;
+
+ if (handleAlias(con, NULL, *origOptString,
+ origOptString + 1)) {
+ origOptString++;
+ continue;
+ }
+ if (handleExec(con, NULL, *origOptString))
+ continue;
+
+ opt = findOption(con->options, NULL, *origOptString, &cb,
+ &cbData, 0);
+ if (!opt) {
+ g_free(dup);
+ return POPT_ERROR_BADOPT;
+ }
+ origOptString++;
+ if (*origOptString)
+ con->os->nextCharArg = origOptString;
+ }
+
+ if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
+ *((int *)opt->arg) = 1;
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+ if (opt->arg) *((int *) opt->arg) = opt->val;
+ } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+ if (longArg) {
+ con->os->nextArg = longArg;
+ } else if (con->os->nextCharArg) {
+ con->os->nextArg = con->os->nextCharArg;
+ con->os->nextCharArg = NULL;
+ } else {
+ while (con->os->next == con->os->argc &&
+ con->os > con->optionStack)
+ con->os--;
+ if (con->os->next == con->os->argc) {
+ g_free(dup);
+ return POPT_ERROR_NOARG;
+ }
+
+ con->os->nextArg = con->os->argv[con->os->next++];
+ }
+
+ if (opt->arg) {
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_STRING:
+ *((char **) opt->arg) = con->os->nextArg;
+ break;
+
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ aLong = strtol(con->os->nextArg, &end, 0);
+ if (!(end && *end == '\0')) {
+ g_free(dup);
+ return POPT_ERROR_BADNUMBER;
+ }
+
+ if (aLong == LONG_MIN || aLong == LONG_MAX) {
+ g_free(dup);
+ return POPT_ERROR_OVERFLOW;
+ }
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+ *((long *) opt->arg) = aLong;
+ } else {
+ if (aLong > INT_MAX || aLong < INT_MIN) {
+ g_free(dup);
+ return POPT_ERROR_OVERFLOW;
+ }
+ *((int *) opt->arg) =aLong;
+ }
+ break;
+
+ default:
+ fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
+ opt->argInfo & POPT_ARG_MASK);
+ exit(1);
+ }
+ }
+ }
+
+ if (cb)
+ cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
+ else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+ done = 1;
+
+ if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ i = con->finalArgvCount++;
+ con->finalArgv[i] =
+ malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
+ if (opt->longName)
+ sprintf(con->finalArgv[i], "--%s", opt->longName);
+ else
+ sprintf(con->finalArgv[i], "-%c", opt->shortName);
+
+ 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);
+ }
+
+ if (dup) g_free(dup);
+ return opt->val;
+}
+
+char * poptGetOptArg(poptContext con) {
+ char * ret = con->os->nextArg;
+ con->os->nextArg = NULL;
+ return ret;
+}
+
+char * poptGetArg(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+ return (con->leftovers[con->nextLeftover++]);
+}
+
+char * poptPeekArg(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+ return (con->leftovers[con->nextLeftover]);
+}
+
+char ** poptGetArgs(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+
+ /* some apps like [like RPM ;-) ] need this NULL terminated */
+ con->leftovers[con->numLeftovers] = NULL;
+
+ return (con->leftovers + con->nextLeftover);
+}
+
+void poptFreeContext(poptContext con) {
+ int i;
+
+ for (i = 0; i < con->numAliases; i++) {
+ if (con->aliases[i].longName) free(con->aliases[i].longName);
+ free(con->aliases[i].argv);
+ }
+
+ for (i = 0; i < con->numExecs; i++) {
+ if (con->execs[i].longName) free(con->execs[i].longName);
+ free(con->execs[i].script);
+ }
+
+ for (i = 0; i < con->finalArgvCount; i++)
+ free(con->finalArgv[i]);
+
+ free(con->leftovers);
+ free(con->finalArgv);
+ if (con->appName) free(con->appName);
+ if (con->aliases) free(con->aliases);
+ if (con->otherHelp) free(con->otherHelp);
+ if (con->execPath) free(con->execPath);
+ free(con);
+}
+
+int poptAddAlias(poptContext con, struct poptAlias newAlias, int flags) {
+ int aliasNum = con->numAliases++;
+ struct poptAlias * alias;
+
+ /* SunOS won't realloc(NULL, ...) */
+ if (!con->aliases)
+ con->aliases = malloc(sizeof(newAlias) * con->numAliases);
+ else
+ con->aliases = realloc(con->aliases,
+ sizeof(newAlias) * con->numAliases);
+ alias = con->aliases + aliasNum;
+
+ *alias = newAlias;
+ if (alias->longName)
+ alias->longName = strcpy(malloc(strlen(alias->longName) + 1),
+ alias->longName);
+ else
+ alias->longName = NULL;
+
+ return 0;
+}
+
+char * poptBadOption(poptContext con, int flags) {
+ struct optionStackEntry * os;
+
+ if (flags & POPT_BADOPTION_NOALIAS)
+ os = con->optionStack;
+ else
+ os = con->os;
+
+ return os->argv[os->next - 1];
+}
+
+#define POPT_ERROR_NOARG -10
+#define POPT_ERROR_BADOPT -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
+
+const char * poptStrerror(const int error) {
+ switch (error) {
+ case POPT_ERROR_NOARG:
+ return POPT_("missing argument");
+ case POPT_ERROR_BADOPT:
+ return POPT_("unknown option");
+ case POPT_ERROR_OPTSTOODEEP:
+ return POPT_("aliases nested too deeply");
+ case POPT_ERROR_BADQUOTE:
+ return POPT_("error in paramter quoting");
+ case POPT_ERROR_BADNUMBER:
+ return POPT_("invalid numeric value");
+ case POPT_ERROR_OVERFLOW:
+ return POPT_("number too large or too small");
+ case POPT_ERROR_ERRNO:
+ return strerror(errno);
+ default:
+ return POPT_("unknown error");
+ }
+}
+
+int poptStuffArgs(poptContext con, char ** argv) {
+ int i;
+
+ if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ for (i = 0; argv[i]; i++);
+
+ con->os++;
+ con->os->next = 0;
+ con->os->nextArg = con->os->nextCharArg = NULL;
+ con->os->currAlias = NULL;
+ con->os->argc = i;
+ con->os->argv = argv;
+ con->os->stuffed = 1;
+
+ return 0;
+}
+
+const char * poptGetInvocationName(poptContext con) {
+ return con->os->argv[0];
+}
--- /dev/null
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPT
+#define H_POPT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h> /* for FILE * */
+
+#define POPT_OPTION_DEPTH 10
+
+#define POPT_ARG_NONE 0
+#define POPT_ARG_STRING 1
+#define POPT_ARG_INT 2
+#define POPT_ARG_LONG 3
+#define POPT_ARG_INCLUDE_TABLE 4 /* arg points to table */
+#define POPT_ARG_CALLBACK 5 /* table-wide callback... must be
+ set first in table; arg points
+ to callback, descrip points to
+ callback data to pass */
+#define POPT_ARG_INTL_DOMAIN 6 /* set the translation domain
+ for this table and any
+ included tables; arg points
+ to the domain string */
+#define POPT_ARG_VAL 7 /* arg should take value val */
+#define POPT_ARG_MASK 0x0000FFFF
+#define POPT_ARGFLAG_ONEDASH 0x80000000 /* allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /* don't show in help/usage */
+#define POPT_CBFLAG_PRE 0x80000000 /* call the callback before parse */
+#define POPT_CBFLAG_POST 0x40000000 /* call the callback after parse */
+#define POPT_CBFLAG_INC_DATA 0x20000000 /* use data from the include line,
+ not the subtable */
+
+#define POPT_ERROR_NOARG -10
+#define POPT_ERROR_BADOPT -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
+#define POPT_ERROR_BADNUMBER -17
+#define POPT_ERROR_OVERFLOW -18
+
+/* poptBadOption() flags */
+#define POPT_BADOPTION_NOALIAS (1 << 0) /* don't go into an alias */
+
+/* poptGetContext() flags */
+#define POPT_CONTEXT_NO_EXEC (1 << 0) /* ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /* pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */
+
+struct poptOption {
+ const char * longName; /* may be NULL */
+ char shortName; /* may be '\0' */
+ int argInfo;
+ void * arg; /* depends on argInfo */
+ int val; /* 0 means don't return, just update flag */
+ char * descrip; /* description for autohelp -- may be NULL */
+ char * argDescrip; /* argument description for autohelp */
+};
+
+struct poptAlias {
+ char * longName; /* may be NULL */
+ char shortName; /* may be '\0' */
+ int argc;
+ char ** argv; /* must be free()able */
+};
+
+extern struct poptOption poptHelpOptions[];
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+ 0, "Help options", NULL },
+
+typedef struct poptContext_s * poptContext;
+#ifndef __cplusplus
+typedef struct poptOption * poptOption;
+#endif
+
+enum poptCallbackReason { POPT_CALLBACK_REASON_PRE,
+ POPT_CALLBACK_REASON_POST,
+ POPT_CALLBACK_REASON_OPTION };
+typedef void (*poptCallbackType)(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption * opt,
+ const char * arg, void * data);
+
+poptContext poptGetContext(char * name, int argc, char ** argv,
+ const struct poptOption * options, int flags);
+void poptResetContext(poptContext con);
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con);
+/* returns NULL if no argument is available */
+char * poptGetOptArg(poptContext con);
+/* returns NULL if no more options are available */
+char * poptGetArg(poptContext con);
+char * poptPeekArg(poptContext con);
+char ** poptGetArgs(poptContext con);
+/* returns the option which caused the most recent error */
+char * poptBadOption(poptContext con, int flags);
+void poptFreeContext(poptContext con);
+int poptStuffArgs(poptContext con, char ** argv);
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
+int poptReadConfigFile(poptContext con, char * fn);
+/* like above, but reads /etc/popt and $HOME/.popt along with environment
+ vars */
+int poptReadDefaultConfig(poptContext con, int useEnv);
+/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated
+ the same as " and both may include \ quotes */
+int poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr);
+const char * poptStrerror(const int error);
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
+void poptPrintHelp(poptContext con, FILE * f, int flags);
+void poptPrintUsage(poptContext con, FILE * f, int flags);
+void poptSetOtherOptionHelp(poptContext con, const char * text);
+const char * poptGetInvocationName(poptContext con);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "common.h"
+
+#include "popt.h"
+#include "poptint.h"
+
+static void configLine(poptContext con, char * line) {
+ int nameLength = strlen(con->appName);
+ char * opt;
+ struct poptAlias alias;
+ char * entryType;
+ char * longName = NULL;
+ char shortName = '\0';
+
+ if (strncmp(line, con->appName, nameLength)) return;
+ line += nameLength;
+ if (!*line || !isspace(*line)) return;
+ while (*line && isspace(*line)) line++;
+ entryType = line;
+
+ while (!*line || !isspace(*line)) line++;
+ *line++ = '\0';
+ while (*line && isspace(*line)) line++;
+ if (!*line) return;
+ opt = line;
+
+ while (!*line || !isspace(*line)) line++;
+ *line++ = '\0';
+ while (*line && isspace(*line)) line++;
+ if (!*line) return;
+
+ if (opt[0] == '-' && opt[1] == '-')
+ longName = opt + 2;
+ else if (opt[0] == '-' && !opt[2])
+ shortName = opt[1];
+
+ if (!strcmp(entryType, "alias")) {
+ if (poptParseArgvString(line, &alias.argc, &alias.argv)) return;
+ alias.longName = longName, alias.shortName = shortName;
+ poptAddAlias(con, alias, 0);
+ } else if (!strcmp(entryType, "exec")) {
+ con->execs = realloc(con->execs,
+ sizeof(*con->execs) * (con->numExecs + 1));
+ if (longName)
+ con->execs[con->numExecs].longName = g_strdup(longName);
+ else
+ con->execs[con->numExecs].longName = NULL;
+
+ con->execs[con->numExecs].shortName = shortName;
+ con->execs[con->numExecs].script = g_strdup(line);
+
+ con->numExecs++;
+ }
+}
+
+int poptReadConfigFile(poptContext con, char * fn) {
+ char * file, * chptr, * end;
+ char * buf, * dst;
+ int fd, rc;
+ int fileLength;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ return POPT_ERROR_ERRNO;
+ }
+
+ fileLength = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, 0);
+
+ file = malloc(fileLength + 1);
+ if (read(fd, file, fileLength) != fileLength) {
+ rc = errno;
+ close(fd);
+ errno = rc;
+ free(file);
+ return POPT_ERROR_ERRNO;
+ }
+ close(fd);
+
+ dst = buf = malloc(fileLength + 1);
+
+ chptr = file;
+ end = (file + fileLength);
+ while (chptr < end) {
+ switch (*chptr) {
+ case '\n':
+ *dst = '\0';
+ dst = buf;
+ while (*dst && isspace(*dst)) dst++;
+ if (*dst && *dst != '#') {
+ configLine(con, dst);
+ }
+ chptr++;
+ break;
+ case '\\':
+ *dst++ = *chptr++;
+ if (chptr < end) {
+ if (*chptr == '\n')
+ dst--, chptr++;
+ /* \ at the end of a line does not insert a \n */
+ else
+ *dst++ = *chptr++;
+ }
+ break;
+ default:
+ *dst++ = *chptr++;
+ }
+ }
+
+ free(buf);
+ free(file);
+ return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, int useEnv) {
+ char * fn, * home;
+ int rc;
+
+ if (!con->appName) return 0;
+
+ rc = poptReadConfigFile(con, "/etc/popt");
+ if (rc) return rc;
+#ifndef WIN32
+ if (getuid() != geteuid()) return 0;
+#endif
+
+ if ((home = getenv("HOME"))) {
+ fn = malloc(strlen(home) + 20);
+ strcpy(fn, home);
+ strcat(fn, "/.popt");
+ rc = poptReadConfigFile(con, fn);
+ free(fn);
+ if (rc) return rc;
+ }
+
+ return 0;
+}
+
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "common.h"
+
+#include "popt.h"
+#include "poptint.h"
+
+static void displayArgs(poptContext con, enum poptCallbackReason foo,
+ struct poptOption * key,
+ const char * arg, void * data) {
+ if (key->shortName== '?')
+ poptPrintHelp(con, stdout, 0);
+ else
+ poptPrintUsage(con, stdout, 0);
+ exit(0);
+}
+
+struct poptOption poptHelpOptions[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL },
+ { "help", '?', 0, NULL, '?', "Show this help message" },
+ { "usage", '\0', 0, NULL, 'u', "Display brief usage message" },
+ { NULL, '\0', 0, NULL, 0 }
+} ;
+
+
+static const char *
+getTableTranslationDomain(const struct poptOption *table)
+{
+ const struct poptOption *opt;
+
+ for(opt = table;
+ opt->longName || opt->shortName || opt->arg;
+ opt++) {
+ if(opt->argInfo == POPT_ARG_INTL_DOMAIN)
+ return opt->arg;
+ }
+
+ return NULL;
+}
+
+static const char * getArgDescrip(const struct poptOption * opt,
+ const char *translation_domain) {
+ if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+ if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+ if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+ if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+ return POPT_("ARG");
+}
+
+static void singleOptionHelp(FILE * f, int maxLeftCol,
+ const struct poptOption * opt,
+ const char *translation_domain) {
+ int indentLength = maxLeftCol + 5;
+ int lineLength = 79 - indentLength;
+ const char * help = D_(translation_domain, opt->descrip);
+ int helpLength;
+ const char * ch;
+ char format[10];
+ char * left = malloc(maxLeftCol + 1);
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ *left = '\0';
+ if (opt->longName && opt->shortName)
+ sprintf(left, "-%c, --%s", opt->shortName, opt->longName);
+ else if (opt->shortName)
+ sprintf(left, "-%c", opt->shortName);
+ else if (opt->longName)
+ sprintf(left, "--%s", opt->longName);
+ if (!*left) {
+ free(left);
+ return;
+ }
+ if (argDescrip) {
+ strcat(left, "=");
+ strcat(left, argDescrip);
+ }
+
+ if (help)
+ fprintf(f," %-*s ", maxLeftCol, left);
+ else {
+ fprintf(f," %s\n", left);
+ free(left);
+ return;
+ }
+
+ free(left);
+
+ helpLength = strlen(help);
+ while (helpLength > lineLength) {
+ ch = help + lineLength - 1;
+ while (ch > help && !isspace(*ch)) ch--;
+ if (ch == help) break; /* give up */
+ while (ch > (help + 1) && isspace(*ch)) ch--;
+ ch++;
+
+ sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
+ fprintf(f, format, help, " ");
+ help = ch;
+ while (isspace(*help) && *help) help++;
+ helpLength = strlen(help);
+ }
+
+ if (helpLength) fprintf(f, "%s\n", help);
+}
+
+static int maxArgWidth(const struct poptOption * opt,
+ const char * translation_domain) {
+ int max = 0;
+ int this;
+ const char * s;
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ this = maxArgWidth(opt->arg, translation_domain);
+ if (this > max) max = this;
+ } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ this = opt->shortName ? 2 : 0;
+ if (opt->longName) {
+ if (this) this += 2;
+ this += strlen(opt->longName) + 2;
+ }
+
+ s = getArgDescrip(opt, translation_domain);
+ if (s)
+ this += strlen(s) + 1;
+ if (this > max) max = this;
+ }
+
+ opt++;
+ }
+
+ return max;
+}
+
+static void singleTableHelp(FILE * f, const struct poptOption * table,
+ int left,
+ const char *translation_domain) {
+ const struct poptOption * opt;
+ const char *sub_transdom;
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ singleOptionHelp(f, left, opt, translation_domain);
+ opt++;
+ }
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ sub_transdom = getTableTranslationDomain(opt->arg);
+ if(!sub_transdom)
+ sub_transdom = translation_domain;
+
+ if (opt->descrip)
+ fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+ singleTableHelp(f, opt->arg, left, sub_transdom);
+ }
+ opt++;
+ }
+}
+
+static int showHelpIntro(poptContext con, FILE * f) {
+ int len = 6;
+ char * fn;
+
+ fprintf(f, POPT_("Usage:"));
+ if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+ fn = con->optionStack->argv[0];
+ if (strchr(fn, '/')) fn = strchr(fn, '/') + 1;
+ fprintf(f, " %s", fn);
+ len += strlen(fn) + 1;
+ }
+
+ return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * f, int flags) {
+ int leftColWidth;
+
+ showHelpIntro(con, f);
+ if (con->otherHelp)
+ fprintf(f, " %s\n", con->otherHelp);
+ else
+ fprintf(f, " %s\n", POPT_("[OPTION...]"));
+
+ leftColWidth = maxArgWidth(con->options, NULL);
+ singleTableHelp(f, con->options, leftColWidth, NULL);
+}
+
+static int singleOptionUsage(FILE * f, int cursor,
+ const struct poptOption * opt,
+ const char *translation_domain) {
+ int len = 3;
+ char shortStr[2];
+ const char * item = shortStr;
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ if (opt->shortName) {
+ if (!(opt->argInfo & POPT_ARG_MASK))
+ return cursor; /* we did these already */
+ len++;
+ *shortStr = opt->shortName;
+ shortStr[1] = '\0';
+ } else if (opt->longName) {
+ len += 1 + strlen(opt->longName);
+ item = opt->longName;
+ }
+
+ if (len == 3) return cursor;
+
+ if (argDescrip)
+ len += strlen(argDescrip) + 1;
+
+ if ((cursor + len) > 79) {
+ fprintf(f, "\n ");
+ cursor = 7;
+ }
+
+ fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item,
+ argDescrip ? (opt->shortName ? " " : "=") : "",
+ argDescrip ? argDescrip : "");
+
+ return cursor + len + 1;
+}
+
+static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table,
+ const char *translation_domain) {
+ const struct poptOption * opt;
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN)
+ translation_domain = (const char *)opt->arg;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ cursor = singleTableUsage(f, cursor, opt->arg,
+ translation_domain);
+ else if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ cursor = singleOptionUsage(f, cursor, opt, translation_domain);
+
+ opt++;
+ }
+
+ return cursor;
+}
+
+static int showShortOptions(const struct poptOption * opt, FILE * f,
+ char * str) {
+ char s[300]; /* this is larger then the ascii set, so
+ it should do just fine */
+
+ if (!str) {
+ str = s;
+ memset(str, 0, sizeof(s));
+ }
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+ str[strlen(str)] = opt->shortName;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ showShortOptions(opt->arg, f, str);
+
+ opt++;
+ }
+
+ if (s != str || !*s)
+ return 0;
+
+ fprintf(f, " [-%s]", s);
+ return strlen(s) + 4;
+}
+
+void poptPrintUsage(poptContext con, FILE * f, int flags) {
+ int cursor;
+
+ cursor = showHelpIntro(con, f);
+ cursor += showShortOptions(con->options, f, NULL);
+ singleTableUsage(f, cursor, con->options, NULL);
+
+ if (con->otherHelp) {
+ cursor += strlen(con->otherHelp) + 1;
+ if (cursor > 79) fprintf(f, "\n ");
+ fprintf(f, " %s", con->otherHelp);
+ }
+
+ fprintf(f, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text) {
+ if (con->otherHelp) free(con->otherHelp);
+ con->otherHelp = g_strdup(text);
+}
--- /dev/null
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+struct optionStackEntry {
+ int argc;
+ char ** argv;
+ int next;
+ char * nextArg;
+ char * nextCharArg;
+ struct poptAlias * currAlias;
+ int stuffed;
+};
+
+struct execEntry {
+ char * longName;
+ char shortName;
+ char * script;
+};
+
+struct poptContext_s {
+ struct optionStackEntry optionStack[POPT_OPTION_DEPTH], * os;
+ char ** leftovers;
+ int numLeftovers;
+ int nextLeftover;
+ const struct poptOption * options;
+ int restLeftover;
+ char * appName;
+ struct poptAlias * aliases;
+ int numAliases;
+ int flags;
+ struct execEntry * execs;
+ int numExecs;
+ char ** finalArgv;
+ int finalArgvCount;
+ int finalArgvAlloced;
+ struct execEntry * doExec;
+ char * execPath;
+ int execAbsolute;
+ char * otherHelp;
+};
+
+#ifdef HAVE_DGETTEXT
+#define D_(dom, str) dgettext(dom, str)
+#define POPT_(foo) D_("popt", foo)
+#else
+#define POPT_(foo) (foo)
+#define D_(dom, str) (str)
+#endif
+
+#endif
--- /dev/null
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "popt.h"
+
+static const int poptArgvArrayGrowDelta = 5;
+
+int poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr) {
+ char * buf, * bufStart, * dst;
+ const char * src;
+ char quote = '\0';
+ int argvAlloced = poptArgvArrayGrowDelta;
+ char ** argv = malloc(sizeof(*argv) * argvAlloced);
+ char ** argv2;
+ int argc = 0;
+ int i, buflen;
+
+ buflen = strlen(s) + 1;
+ bufStart = buf = malloc(buflen);
+ memset(buf, '\0', buflen);
+
+ src = s;
+ argv[argc] = buf;
+
+ while (*src) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ free(argv);
+ free(bufStart);
+ return POPT_ERROR_BADQUOTE;
+ }
+ if (*src != quote) *buf++ = '\\';
+ }
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*argv[argc]) {
+ buf++, argc++;
+ if (argc == argvAlloced) {
+ argvAlloced += poptArgvArrayGrowDelta;
+ argv = realloc(argv, sizeof(*argv) * argvAlloced);
+ }
+ argv[argc] = buf;
+ }
+ } else switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+ case '\\':
+ src++;
+ if (!*src) {
+ free(argv);
+ free(bufStart);
+ return POPT_ERROR_BADQUOTE;
+ }
+ /* fallthrough */
+ default:
+ *buf++ = *src;
+ }
+
+ src++;
+ }
+
+ if (strlen(argv[argc])) {
+ argc++, buf++;
+ }
+
+ dst = malloc(argc * sizeof(*argv) + (buf - bufStart));
+ argv2 = (void *) dst;
+ dst += argc * sizeof(*argv);
+ memcpy(argv2, argv, argc * sizeof(*argv));
+ memcpy(dst, bufStart, buf - bufStart);
+
+ for (i = 0; i < argc; i++) {
+ argv2[i] = dst + (argv[i] - bufStart);
+ }
+
+ free(argv);
+
+ *argvPtr = argv2;
+ *argcPtr = argc;
+
+ free(bufStart);
+ return 0;
+}
--- /dev/null
+SUBDIRS = core
+
+noinst_LIBRARIES = libsilc.a
+
+libsilc_a_SOURCES = silc.c
--- /dev/null
+/* this file is automatically generated by configure - don't change */
+void silc_core_init(void); void silc_core_deinit(void);
+void silc_init(void) { silc_core_init(); }
+void silc_deinit(void) { silc_core_deinit(); }
--- /dev/null
+#!/usr/bin/perl
+#
+# This script reads the syntaces of commands from irssi source tree.
+# Then it browses through all '.in' files in the current directory and
+# substitutes '@SYNTAX:foo@' tags with real syntaces found. This data
+# is written into the corresponding files without the '.in' extension.
+# For example: help.in -> ../help
+#
+# This path has to be changed. It should point to your irssi/src directory
+# Remember to include the asterisk ('*').
+$SRC_PATH='src';
+
+$FOO = `find src -name '*.c' -exec perl findsyntax.pl \{\} \\; | sed 's/.*SYNTAX: //' > irssi_syntax`;
+
+while (<docs/help/in/*.in>) {
+ next if (/Makefile/);
+
+ open (FILE, "$_");
+ @data = <FILE>;
+ close (FILE);
+ $count = 0;
+ foreach $DATARIVI (@data) {
+ if ($DATARIVI =~ /\@SYNTAX\:(.+)\@/) {
+ $etsittava = "\U$1 ";
+ $SYNTAX = `grep \'^$etsittava\' irssi_syntax`;
+ $SYNTAX =~ s/\*\///g;
+ $SYNTAX =~ s/ *$//; $SYNTAX =~ s/ *\n/\n/g;
+
+ # add %| after "COMMAND SUB " so parameters will indent correctly
+ $SYNTAX =~ s/^([A-Z ]+)/\1%|/;
+ $SYNTAX =~ s/(\n[A-Z ]+)/\1%|/g;
+ # no need for this if there's no parameters
+ $SYNTAX =~ s/%\|$//;
+ $DATARIVI = $SYNTAX;
+ } elsif ($DATARIVI =~ /^\S+/) {
+ if ($data[$count+1] =~ /^\S+/) {
+ chomp $DATARIVI;
+ $DATARIVI =~ s/ *$//g;
+ $DATARIVI .= " ";
+ }
+ } else {
+ $DATARIVI =~ s/^\t/ / while ($DATARIVI =~ /^\t/);
+ }
+ $count++;
+ }
+
+ # must always end with empty line
+ push @data, "\n" if ($data[@data-1] ne "\n");
+ push @data, "\n" if ($data[@data-2] !~ /\n$/);
+
+ $newfilename = $_; $newfilename =~ s/\.in$//;
+ $newfilename =~ s/\/in\//\//;
+ open (NEWFILE, ">$newfilename");
+ print NEWFILE @data;
+ close (NEWFILE);
+}
+unlink "irssi_syntax";