imported irssi. silc start
authorPekka Riikonen <priikone@silcnet.org>
Thu, 24 May 2001 12:09:25 +0000 (12:09 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 24 May 2001 12:09:25 +0000 (12:09 +0000)
198 files changed:
apps/irssi/Makefile.am [new file with mode: 0644]
apps/irssi/README [new file with mode: 0644]
apps/irssi/acconfig.h [new file with mode: 0644]
apps/irssi/autogen.sh [new file with mode: 0755]
apps/irssi/config [new file with mode: 0644]
apps/irssi/config.h.in [new file with mode: 0644]
apps/irssi/configure.in [new file with mode: 0644]
apps/irssi/curses.m4 [new file with mode: 0644]
apps/irssi/default-config.h [new file with mode: 0644]
apps/irssi/default.theme [new file with mode: 0644]
apps/irssi/docs/Makefile.am [new file with mode: 0644]
apps/irssi/docs/faq.txt [new file with mode: 0644]
apps/irssi/docs/formats.txt [new file with mode: 0644]
apps/irssi/docs/help/Makefile.am.gen [new file with mode: 0644]
apps/irssi/docs/help/in/.cvsignore [new file with mode: 0644]
apps/irssi/docs/help/in/Makefile.am.gen [new file with mode: 0644]
apps/irssi/docs/help/in/action.in [new file with mode: 0644]
apps/irssi/docs/help/in/admin.in [new file with mode: 0644]
apps/irssi/docs/help/in/alias.in [new file with mode: 0644]
apps/irssi/docs/help/in/away.in [new file with mode: 0644]
apps/irssi/docs/help/in/ban.in [new file with mode: 0644]
apps/irssi/docs/help/in/beep.in [new file with mode: 0644]
apps/irssi/docs/help/in/bind.in [new file with mode: 0644]
apps/irssi/docs/help/in/cat.in [new file with mode: 0644]
apps/irssi/docs/help/in/cd.in [new file with mode: 0644]
apps/irssi/docs/help/in/channel.in [new file with mode: 0644]
apps/irssi/docs/help/in/clear.in [new file with mode: 0644]
apps/irssi/docs/help/in/connect.in [new file with mode: 0644]
apps/irssi/docs/help/in/ctcp.in [new file with mode: 0644]
apps/irssi/docs/help/in/cycle.in [new file with mode: 0644]
apps/irssi/docs/help/in/date.in [new file with mode: 0644]
apps/irssi/docs/help/in/dcc.in [new file with mode: 0644]
apps/irssi/docs/help/in/dehilight.in [new file with mode: 0644]
apps/irssi/docs/help/in/deop.in [new file with mode: 0644]
apps/irssi/docs/help/in/devoice.in [new file with mode: 0644]
apps/irssi/docs/help/in/die.in [new file with mode: 0644]
apps/irssi/docs/help/in/disconnect.in [new file with mode: 0644]
apps/irssi/docs/help/in/echo.in [new file with mode: 0644]
apps/irssi/docs/help/in/eval.in [new file with mode: 0644]
apps/irssi/docs/help/in/exec.in [new file with mode: 0644]
apps/irssi/docs/help/in/format.in [new file with mode: 0644]
apps/irssi/docs/help/in/hash.in [new file with mode: 0644]
apps/irssi/docs/help/in/help.in [new file with mode: 0644]
apps/irssi/docs/help/in/hilight.in [new file with mode: 0644]
apps/irssi/docs/help/in/ignore.in [new file with mode: 0644]
apps/irssi/docs/help/in/info.in [new file with mode: 0644]
apps/irssi/docs/help/in/invite.in [new file with mode: 0644]
apps/irssi/docs/help/in/invitelist.in [new file with mode: 0644]
apps/irssi/docs/help/in/ircnet.in [new file with mode: 0644]
apps/irssi/docs/help/in/ison.in [new file with mode: 0644]
apps/irssi/docs/help/in/join.in [new file with mode: 0644]
apps/irssi/docs/help/in/kick.in [new file with mode: 0644]
apps/irssi/docs/help/in/kickban.in [new file with mode: 0644]
apps/irssi/docs/help/in/kill.in [new file with mode: 0644]
apps/irssi/docs/help/in/knockout.in [new file with mode: 0644]
apps/irssi/docs/help/in/lastlog.in [new file with mode: 0644]
apps/irssi/docs/help/in/layout.in [new file with mode: 0644]
apps/irssi/docs/help/in/levels.in [new file with mode: 0644]
apps/irssi/docs/help/in/links.in [new file with mode: 0644]
apps/irssi/docs/help/in/list.in [new file with mode: 0644]
apps/irssi/docs/help/in/load.in [new file with mode: 0644]
apps/irssi/docs/help/in/log.in [new file with mode: 0644]
apps/irssi/docs/help/in/lusers.in [new file with mode: 0644]
apps/irssi/docs/help/in/map.in [new file with mode: 0644]
apps/irssi/docs/help/in/me.in [new file with mode: 0644]
apps/irssi/docs/help/in/mircdcc.in [new file with mode: 0644]
apps/irssi/docs/help/in/mode.in [new file with mode: 0644]
apps/irssi/docs/help/in/motd.in [new file with mode: 0644]
apps/irssi/docs/help/in/msg.in [new file with mode: 0644]
apps/irssi/docs/help/in/names.in [new file with mode: 0644]
apps/irssi/docs/help/in/nctcp.in [new file with mode: 0644]
apps/irssi/docs/help/in/netsplit.in [new file with mode: 0644]
apps/irssi/docs/help/in/nick.in [new file with mode: 0644]
apps/irssi/docs/help/in/note.in [new file with mode: 0644]
apps/irssi/docs/help/in/notice.in [new file with mode: 0644]
apps/irssi/docs/help/in/notify.in [new file with mode: 0644]
apps/irssi/docs/help/in/op.in [new file with mode: 0644]
apps/irssi/docs/help/in/oper.in [new file with mode: 0644]
apps/irssi/docs/help/in/part.in [new file with mode: 0644]
apps/irssi/docs/help/in/perlflush.in [new file with mode: 0644]
apps/irssi/docs/help/in/ping.in [new file with mode: 0644]
apps/irssi/docs/help/in/query.in [new file with mode: 0644]
apps/irssi/docs/help/in/quit.in [new file with mode: 0644]
apps/irssi/docs/help/in/quote.in [new file with mode: 0644]
apps/irssi/docs/help/in/rawlog.in [new file with mode: 0644]
apps/irssi/docs/help/in/reconnect.in [new file with mode: 0644]
apps/irssi/docs/help/in/rehash.in [new file with mode: 0644]
apps/irssi/docs/help/in/reload.in [new file with mode: 0644]
apps/irssi/docs/help/in/restart.in [new file with mode: 0644]
apps/irssi/docs/help/in/rmreconns.in [new file with mode: 0644]
apps/irssi/docs/help/in/rmrejoins.in [new file with mode: 0644]
apps/irssi/docs/help/in/rping.in [new file with mode: 0644]
apps/irssi/docs/help/in/run.in [new file with mode: 0644]
apps/irssi/docs/help/in/save.in [new file with mode: 0644]
apps/irssi/docs/help/in/sconnect.in [new file with mode: 0644]
apps/irssi/docs/help/in/scrollback.in [new file with mode: 0644]
apps/irssi/docs/help/in/server.in [new file with mode: 0644]
apps/irssi/docs/help/in/servlist.in [new file with mode: 0644]
apps/irssi/docs/help/in/set.in [new file with mode: 0644]
apps/irssi/docs/help/in/silence.in [new file with mode: 0644]
apps/irssi/docs/help/in/squery.in [new file with mode: 0644]
apps/irssi/docs/help/in/squit.in [new file with mode: 0644]
apps/irssi/docs/help/in/stats.in [new file with mode: 0644]
apps/irssi/docs/help/in/time.in [new file with mode: 0644]
apps/irssi/docs/help/in/toggle.in [new file with mode: 0644]
apps/irssi/docs/help/in/topic.in [new file with mode: 0644]
apps/irssi/docs/help/in/trace.in [new file with mode: 0644]
apps/irssi/docs/help/in/ts.in [new file with mode: 0644]
apps/irssi/docs/help/in/unalias.in [new file with mode: 0644]
apps/irssi/docs/help/in/unban.in [new file with mode: 0644]
apps/irssi/docs/help/in/unignore.in [new file with mode: 0644]
apps/irssi/docs/help/in/unload.in [new file with mode: 0644]
apps/irssi/docs/help/in/unnotify.in [new file with mode: 0644]
apps/irssi/docs/help/in/unquery.in [new file with mode: 0644]
apps/irssi/docs/help/in/unsilence.in [new file with mode: 0644]
apps/irssi/docs/help/in/uping.in [new file with mode: 0644]
apps/irssi/docs/help/in/userhost.in [new file with mode: 0644]
apps/irssi/docs/help/in/ver.in [new file with mode: 0644]
apps/irssi/docs/help/in/version.in [new file with mode: 0644]
apps/irssi/docs/help/in/voice.in [new file with mode: 0644]
apps/irssi/docs/help/in/wait.in [new file with mode: 0644]
apps/irssi/docs/help/in/wall.in [new file with mode: 0644]
apps/irssi/docs/help/in/wallchops.in [new file with mode: 0644]
apps/irssi/docs/help/in/wallops.in [new file with mode: 0644]
apps/irssi/docs/help/in/who.in [new file with mode: 0644]
apps/irssi/docs/help/in/whois.in [new file with mode: 0644]
apps/irssi/docs/help/in/whowas.in [new file with mode: 0644]
apps/irssi/docs/help/in/window.in [new file with mode: 0644]
apps/irssi/docs/help/in/wjoin.in [new file with mode: 0644]
apps/irssi/docs/help/in/wquery.in [new file with mode: 0644]
apps/irssi/docs/manual.txt [new file with mode: 0644]
apps/irssi/docs/startup-HOWTO.html [new file with mode: 0644]
apps/irssi/file2header.sh [new file with mode: 0755]
apps/irssi/findsyntax.pl [new file with mode: 0755]
apps/irssi/irssi-config.in [new file with mode: 0644]
apps/irssi/irssi-version.h.in [new file with mode: 0644]
apps/irssi/irssi.spec.in [new file with mode: 0644]
apps/irssi/src/Makefile.am [new file with mode: 0644]
apps/irssi/src/common.h [new file with mode: 0644]
apps/irssi/src/fe-common/Makefile.am [new file with mode: 0644]
apps/irssi/src/fe-common/silc/Makefile.am [new file with mode: 0644]
apps/irssi/src/fe-common/silc/fe-channels.c [new file with mode: 0644]
apps/irssi/src/fe-common/silc/fe-common-silc.c [new file with mode: 0644]
apps/irssi/src/fe-common/silc/fe-common-silc.h [new file with mode: 0644]
apps/irssi/src/fe-common/silc/module-formats.c [new file with mode: 0644]
apps/irssi/src/fe-common/silc/module-formats.h [new file with mode: 0644]
apps/irssi/src/fe-common/silc/module.h [new file with mode: 0644]
apps/irssi/src/fe-common/silc/silc-modules.c [new file with mode: 0644]
apps/irssi/src/fe-text/Makefile.am [new file with mode: 0644]
apps/irssi/src/fe-text/gui-entry.c [new file with mode: 0644]
apps/irssi/src/fe-text/gui-entry.h [new file with mode: 0644]
apps/irssi/src/fe-text/gui-expandos.c [new file with mode: 0644]
apps/irssi/src/fe-text/gui-printtext.c [new file with mode: 0644]
apps/irssi/src/fe-text/gui-printtext.h [new file with mode: 0644]
apps/irssi/src/fe-text/gui-readline.c [new file with mode: 0644]
apps/irssi/src/fe-text/gui-readline.h [new file with mode: 0644]
apps/irssi/src/fe-text/gui-windows.c [new file with mode: 0644]
apps/irssi/src/fe-text/gui-windows.h [new file with mode: 0644]
apps/irssi/src/fe-text/lastlog.c [new file with mode: 0644]
apps/irssi/src/fe-text/mainwindow-activity.c [new file with mode: 0644]
apps/irssi/src/fe-text/mainwindows-save.c [new file with mode: 0644]
apps/irssi/src/fe-text/mainwindows.c [new file with mode: 0644]
apps/irssi/src/fe-text/mainwindows.h [new file with mode: 0644]
apps/irssi/src/fe-text/module-formats.c [new file with mode: 0644]
apps/irssi/src/fe-text/module-formats.h [new file with mode: 0644]
apps/irssi/src/fe-text/module.h [new file with mode: 0644]
apps/irssi/src/fe-text/screen.c [new file with mode: 0644]
apps/irssi/src/fe-text/screen.h [new file with mode: 0644]
apps/irssi/src/fe-text/silc.c [new file with mode: 0644]
apps/irssi/src/fe-text/statusbar-items.c [new file with mode: 0644]
apps/irssi/src/fe-text/statusbar.c [new file with mode: 0644]
apps/irssi/src/fe-text/statusbar.h [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-commands.c [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-view.c [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-view.h [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer.c [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer.h [new file with mode: 0644]
apps/irssi/src/lib-config/Makefile.am [new file with mode: 0644]
apps/irssi/src/lib-config/get.c [new file with mode: 0644]
apps/irssi/src/lib-config/iconfig.h [new file with mode: 0644]
apps/irssi/src/lib-config/module.h [new file with mode: 0644]
apps/irssi/src/lib-config/parse.c [new file with mode: 0644]
apps/irssi/src/lib-config/set.c [new file with mode: 0644]
apps/irssi/src/lib-config/write.c [new file with mode: 0644]
apps/irssi/src/lib-popt/.cvsignore [new file with mode: 0644]
apps/irssi/src/lib-popt/Makefile.am [new file with mode: 0644]
apps/irssi/src/lib-popt/findme.c [new file with mode: 0644]
apps/irssi/src/lib-popt/findme.h [new file with mode: 0644]
apps/irssi/src/lib-popt/popt.c [new file with mode: 0644]
apps/irssi/src/lib-popt/popt.h [new file with mode: 0644]
apps/irssi/src/lib-popt/poptconfig.c [new file with mode: 0644]
apps/irssi/src/lib-popt/popthelp.c [new file with mode: 0644]
apps/irssi/src/lib-popt/poptint.h [new file with mode: 0644]
apps/irssi/src/lib-popt/poptparse.c [new file with mode: 0644]
apps/irssi/src/silc/Makefile.am [new file with mode: 0644]
apps/irssi/src/silc/silc.c [new file with mode: 0644]
apps/irssi/stamp.h.in [new file with mode: 0644]
apps/irssi/syntax.pl [new file with mode: 0755]

diff --git a/apps/irssi/Makefile.am b/apps/irssi/Makefile.am
new file mode 100644 (file)
index 0000000..304c44d
--- /dev/null
@@ -0,0 +1,30 @@
+# 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
diff --git a/apps/irssi/README b/apps/irssi/README
new file mode 100644 (file)
index 0000000..7f8fdc7
--- /dev/null
@@ -0,0 +1,111 @@
+
+
+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
diff --git a/apps/irssi/acconfig.h b/apps/irssi/acconfig.h
new file mode 100644 (file)
index 0000000..7535059
--- /dev/null
@@ -0,0 +1,38 @@
+/* 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
diff --git a/apps/irssi/autogen.sh b/apps/irssi/autogen.sh
new file mode 100755 (executable)
index 0000000..daf8a99
--- /dev/null
@@ -0,0 +1,105 @@
+#!/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
diff --git a/apps/irssi/config b/apps/irssi/config
new file mode 100644 (file)
index 0000000..876b402
--- /dev/null
@@ -0,0 +1,65 @@
+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";
+};
diff --git a/apps/irssi/config.h.in b/apps/irssi/config.h.in
new file mode 100644 (file)
index 0000000..14b0915
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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
+
diff --git a/apps/irssi/configure.in b/apps/irssi/configure.in
new file mode 100644 (file)
index 0000000..87063b4
--- /dev/null
@@ -0,0 +1,784 @@
+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"
+
diff --git a/apps/irssi/curses.m4 b/apps/irssi/curses.m4
new file mode 100644 (file)
index 0000000..7865b1c
--- /dev/null
@@ -0,0 +1,295 @@
+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
+])
+
diff --git a/apps/irssi/default-config.h b/apps/irssi/default-config.h
new file mode 100644 (file)
index 0000000..635030b
--- /dev/null
@@ -0,0 +1,67 @@
+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"
+;
diff --git a/apps/irssi/default.theme b/apps/irssi/default.theme
new file mode 100644 (file)
index 0000000..501a161
--- /dev/null
@@ -0,0 +1,260 @@
+# 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-";
+};
diff --git a/apps/irssi/docs/Makefile.am b/apps/irssi/docs/Makefile.am
new file mode 100644 (file)
index 0000000..8049961
--- /dev/null
@@ -0,0 +1,12 @@
+docdir = $(prefix)/doc/irssi
+
+doc_DATA = \
+       formats.txt \
+       manual.txt \
+       faq.txt \
+       startup-HOWTO.html \
+       startup-HOWTO.txt
+
+EXTRA_DIST = $(doc_DATA)
+
+SUBDIRS = help
diff --git a/apps/irssi/docs/faq.txt b/apps/irssi/docs/faq.txt
new file mode 100644 (file)
index 0000000..6f603e8
--- /dev/null
@@ -0,0 +1,36 @@
+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).
diff --git a/apps/irssi/docs/formats.txt b/apps/irssi/docs/formats.txt
new file mode 100644 (file)
index 0000000..6dabc43
--- /dev/null
@@ -0,0 +1,64 @@
+
+ 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
diff --git a/apps/irssi/docs/help/Makefile.am.gen b/apps/irssi/docs/help/Makefile.am.gen
new file mode 100644 (file)
index 0000000..abf824b
--- /dev/null
@@ -0,0 +1,12 @@
+# 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
diff --git a/apps/irssi/docs/help/in/.cvsignore b/apps/irssi/docs/help/in/.cvsignore
new file mode 100644 (file)
index 0000000..4717c0c
--- /dev/null
@@ -0,0 +1,3 @@
+Makefile.am
+Makefile.in
+Makefile
diff --git a/apps/irssi/docs/help/in/Makefile.am.gen b/apps/irssi/docs/help/in/Makefile.am.gen
new file mode 100644 (file)
index 0000000..9b0f2aa
--- /dev/null
@@ -0,0 +1,5 @@
+# Makefile.am is autogenerated by autogen.sh from Makefile.am.gen
+
+EXTRA_DIST = \
+       Makefile.am.gen \
+@HELPFILES@
diff --git a/apps/irssi/docs/help/in/action.in b/apps/irssi/docs/help/in/action.in
new file mode 100644 (file)
index 0000000..896e8d1
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/admin.in b/apps/irssi/docs/help/in/admin.in
new file mode 100644 (file)
index 0000000..b5d9525
--- /dev/null
@@ -0,0 +1,8 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/alias.in b/apps/irssi/docs/help/in/alias.in
new file mode 100644 (file)
index 0000000..97fe395
--- /dev/null
@@ -0,0 +1,22 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/away.in b/apps/irssi/docs/help/in/away.in
new file mode 100644 (file)
index 0000000..8283d63
--- /dev/null
@@ -0,0 +1,28 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/ban.in b/apps/irssi/docs/help/in/ban.in
new file mode 100644 (file)
index 0000000..4f8a541
--- /dev/null
@@ -0,0 +1,24 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/beep.in b/apps/irssi/docs/help/in/beep.in
new file mode 100644 (file)
index 0000000..12df1ae
--- /dev/null
@@ -0,0 +1,6 @@
+
+@SYNTAX:beep@
+
+Outputs the bell-character, usually causing
+your terminal beep.
+
diff --git a/apps/irssi/docs/help/in/bind.in b/apps/irssi/docs/help/in/bind.in
new file mode 100644 (file)
index 0000000..0d0d2ab
--- /dev/null
@@ -0,0 +1,100 @@
+
+@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
diff --git a/apps/irssi/docs/help/in/cat.in b/apps/irssi/docs/help/in/cat.in
new file mode 100644 (file)
index 0000000..ea324aa
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:cat@
+
+Outputs the contents of the specified file. Equivalent to
+UNIX 'cat' command.
+
+See also: CD
+
diff --git a/apps/irssi/docs/help/in/cd.in b/apps/irssi/docs/help/in/cd.in
new file mode 100644 (file)
index 0000000..dd62f71
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:cd@
+
+Changes the current working directory. Equivalent to UNIX
+'cd' command.
+
+See also: DCC GET
+
diff --git a/apps/irssi/docs/help/in/channel.in b/apps/irssi/docs/help/in/channel.in
new file mode 100644 (file)
index 0000000..7484ac1
--- /dev/null
@@ -0,0 +1,35 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/clear.in b/apps/irssi/docs/help/in/clear.in
new file mode 100644 (file)
index 0000000..b3db54e
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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). 
+
+
diff --git a/apps/irssi/docs/help/in/connect.in b/apps/irssi/docs/help/in/connect.in
new file mode 100644 (file)
index 0000000..f4671ab
--- /dev/null
@@ -0,0 +1,12 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/ctcp.in b/apps/irssi/docs/help/in/ctcp.in
new file mode 100644 (file)
index 0000000..77c78b7
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:ctcp@
+
+Sends a CTCP-message. For example CTCP ACTION, or CTCP VERSION.
+
+See also: ME, ACTION
+
diff --git a/apps/irssi/docs/help/in/cycle.in b/apps/irssi/docs/help/in/cycle.in
new file mode 100644 (file)
index 0000000..7e8a8fb
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:cycle@
+
+Cycles (leaves and joins) the current channel or the specified 
+channel.
+
+See also: JOIN, LEAVE, PART
+
diff --git a/apps/irssi/docs/help/in/date.in b/apps/irssi/docs/help/in/date.in
new file mode 100644 (file)
index 0000000..5b2ca4b
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/dcc.in b/apps/irssi/docs/help/in/dcc.in
new file mode 100644 (file)
index 0000000..eb9caa3
--- /dev/null
@@ -0,0 +1,25 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/dehilight.in b/apps/irssi/docs/help/in/dehilight.in
new file mode 100644 (file)
index 0000000..34f856a
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/deop.in b/apps/irssi/docs/help/in/deop.in
new file mode 100644 (file)
index 0000000..e9893e6
--- /dev/null
@@ -0,0 +1,10 @@
+
+@SYNTAX:deop@
+
+Takes off the channel operator privileges from the
+specified nick(s). 
+
+Wildcards in the nick are allowed.
+
+See also: OP
+
diff --git a/apps/irssi/docs/help/in/devoice.in b/apps/irssi/docs/help/in/devoice.in
new file mode 100644 (file)
index 0000000..ce09019
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/die.in b/apps/irssi/docs/help/in/die.in
new file mode 100644 (file)
index 0000000..4ef6da0
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:die@
+
+IRC-operator command. Makes IRC-server to die.
+
+See also: OPER
+
diff --git a/apps/irssi/docs/help/in/disconnect.in b/apps/irssi/docs/help/in/disconnect.in
new file mode 100644 (file)
index 0000000..cfd221d
--- /dev/null
@@ -0,0 +1,9 @@
+
+@SYNTAX:disconnect@
+
+Disconnects from the specified IRC-server.
+The server tags can be seen with:
+/SERVER LIST
+
+See also: CONNECT, SERVER
+
diff --git a/apps/irssi/docs/help/in/echo.in b/apps/irssi/docs/help/in/echo.in
new file mode 100644 (file)
index 0000000..0a70e35
--- /dev/null
@@ -0,0 +1,5 @@
+
+@SYNTAX:echo@
+
+Prints text into the current window. Useful for scripts.
+
diff --git a/apps/irssi/docs/help/in/eval.in b/apps/irssi/docs/help/in/eval.in
new file mode 100644 (file)
index 0000000..8ea0652
--- /dev/null
@@ -0,0 +1,6 @@
+
+@SYNTAX:eval@
+
+Evaluates the given commands and executes them. Internal variables
+are expanded. See the special_vars.txt file in the docs-directory.
+
diff --git a/apps/irssi/docs/help/in/exec.in b/apps/irssi/docs/help/in/exec.in
new file mode 100644 (file)
index 0000000..c33ec59
--- /dev/null
@@ -0,0 +1,37 @@
+
+@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.
diff --git a/apps/irssi/docs/help/in/format.in b/apps/irssi/docs/help/in/format.in
new file mode 100644 (file)
index 0000000..ed576c9
--- /dev/null
@@ -0,0 +1,9 @@
+
+@SYNTAX:format@
+
+   -reset
+   -delete
+
+Allows you to view/change irssi's messages.
+Use this command with care.
+
diff --git a/apps/irssi/docs/help/in/hash.in b/apps/irssi/docs/help/in/hash.in
new file mode 100644 (file)
index 0000000..62a889f
--- /dev/null
@@ -0,0 +1,5 @@
+
+@SYNTAX:hash@
+
+Not available.
+
diff --git a/apps/irssi/docs/help/in/help.in b/apps/irssi/docs/help/in/help.in
new file mode 100644 (file)
index 0000000..318b820
--- /dev/null
@@ -0,0 +1,13 @@
+
+@SYNTAX:help@
+
+Shows help on commands. Try:
+  /HELP command
+
+Also try, for example:
+  /SET beep
+or
+  /SET auto
+
+See also: 
+
diff --git a/apps/irssi/docs/help/in/hilight.in b/apps/irssi/docs/help/in/hilight.in
new file mode 100644 (file)
index 0000000..a38d10c
--- /dev/null
@@ -0,0 +1,27 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/ignore.in b/apps/irssi/docs/help/in/ignore.in
new file mode 100644 (file)
index 0000000..34a49de
--- /dev/null
@@ -0,0 +1,26 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/info.in b/apps/irssi/docs/help/in/info.in
new file mode 100644 (file)
index 0000000..e9aad17
--- /dev/null
@@ -0,0 +1,6 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/invite.in b/apps/irssi/docs/help/in/invite.in
new file mode 100644 (file)
index 0000000..1e7f69b
--- /dev/null
@@ -0,0 +1,10 @@
+@SYNTAX:invite@
+
+Invites the specified nick to the current or specified channel.
+
+Example:
+   /INVITE buddy #mychannel
+
+See also: MODE
+
diff --git a/apps/irssi/docs/help/in/invitelist.in b/apps/irssi/docs/help/in/invitelist.in
new file mode 100644 (file)
index 0000000..ed6bc06
--- /dev/null
@@ -0,0 +1,9 @@
+
+@SYNTAX:invitelist@
+
+Shows the +I modes of the current channel. +I mode
+allows free joins of clients with certain userhost mask
+even if the channel is invite only.
+
+See also: INVITE, MODE
+
diff --git a/apps/irssi/docs/help/in/ircnet.in b/apps/irssi/docs/help/in/ircnet.in
new file mode 100644 (file)
index 0000000..51a123f
--- /dev/null
@@ -0,0 +1,21 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/ison.in b/apps/irssi/docs/help/in/ison.in
new file mode 100644 (file)
index 0000000..b97ff5d
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:ison@
+
+Tells whether specified nicks are online.
+
+See also: WHOIS, WHOWAS, NOTIFY
+
diff --git a/apps/irssi/docs/help/in/join.in b/apps/irssi/docs/help/in/join.in
new file mode 100644 (file)
index 0000000..bdd4b15
--- /dev/null
@@ -0,0 +1,13 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/kick.in b/apps/irssi/docs/help/in/kick.in
new file mode 100644 (file)
index 0000000..304c4d3
--- /dev/null
@@ -0,0 +1,16 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/kickban.in b/apps/irssi/docs/help/in/kickban.in
new file mode 100644 (file)
index 0000000..aff0cfb
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/kill.in b/apps/irssi/docs/help/in/kill.in
new file mode 100644 (file)
index 0000000..338871d
--- /dev/null
@@ -0,0 +1,18 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/knockout.in b/apps/irssi/docs/help/in/knockout.in
new file mode 100644 (file)
index 0000000..1d18b53
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/lastlog.in b/apps/irssi/docs/help/in/lastlog.in
new file mode 100644 (file)
index 0000000..0c508c4
--- /dev/null
@@ -0,0 +1,22 @@
+
+@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:
+
diff --git a/apps/irssi/docs/help/in/layout.in b/apps/irssi/docs/help/in/layout.in
new file mode 100644 (file)
index 0000000..d5e8143
--- /dev/null
@@ -0,0 +1,13 @@
+
+@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.
diff --git a/apps/irssi/docs/help/in/levels.in b/apps/irssi/docs/help/in/levels.in
new file mode 100644 (file)
index 0000000..937ec1d
--- /dev/null
@@ -0,0 +1,35 @@
+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
+
diff --git a/apps/irssi/docs/help/in/links.in b/apps/irssi/docs/help/in/links.in
new file mode 100644 (file)
index 0000000..726fcb6
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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:
+
diff --git a/apps/irssi/docs/help/in/list.in b/apps/irssi/docs/help/in/list.in
new file mode 100644 (file)
index 0000000..32a3cdb
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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:
+
diff --git a/apps/irssi/docs/help/in/load.in b/apps/irssi/docs/help/in/load.in
new file mode 100644 (file)
index 0000000..134c762
--- /dev/null
@@ -0,0 +1,14 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/log.in b/apps/irssi/docs/help/in/log.in
new file mode 100644 (file)
index 0000000..6a61ef9
--- /dev/null
@@ -0,0 +1,40 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/lusers.in b/apps/irssi/docs/help/in/lusers.in
new file mode 100644 (file)
index 0000000..abcd55c
--- /dev/null
@@ -0,0 +1,5 @@
+
+@SYNTAX:lusers@
+
+Shows user statistics of the current IRC network.
+
diff --git a/apps/irssi/docs/help/in/map.in b/apps/irssi/docs/help/in/map.in
new file mode 100644 (file)
index 0000000..824dce3
--- /dev/null
@@ -0,0 +1,5 @@
+
+@SYNTAX:map@
+
+Not available in IRC.
+
diff --git a/apps/irssi/docs/help/in/me.in b/apps/irssi/docs/help/in/me.in
new file mode 100644 (file)
index 0000000..b918ec7
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:me@
+
+Sends a CTCP ACTION to the current channel or query.
+For example: /me sits back.
+
+See also: ACTION, CTCP
+
diff --git a/apps/irssi/docs/help/in/mircdcc.in b/apps/irssi/docs/help/in/mircdcc.in
new file mode 100644 (file)
index 0000000..d3402e0
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/mode.in b/apps/irssi/docs/help/in/mode.in
new file mode 100644 (file)
index 0000000..5bef27c
--- /dev/null
@@ -0,0 +1,97 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/motd.in b/apps/irssi/docs/help/in/motd.in
new file mode 100644 (file)
index 0000000..ba35440
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:motd@
+
+Shows the motd of the current server. This contains
+usually some useful info on the server, administrator and
+the rules.
+
diff --git a/apps/irssi/docs/help/in/msg.in b/apps/irssi/docs/help/in/msg.in
new file mode 100644 (file)
index 0000000..f58ba41
--- /dev/null
@@ -0,0 +1,15 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/names.in b/apps/irssi/docs/help/in/names.in
new file mode 100644 (file)
index 0000000..2d515c1
--- /dev/null
@@ -0,0 +1,21 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/nctcp.in b/apps/irssi/docs/help/in/nctcp.in
new file mode 100644 (file)
index 0000000..58b8054
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:nctcp@
+
+Sends a CTCP reply notice to the nick/channel.
+
+See also: CTCP, ACTION, MSG, NOTICE
+
diff --git a/apps/irssi/docs/help/in/netsplit.in b/apps/irssi/docs/help/in/netsplit.in
new file mode 100644 (file)
index 0000000..2e244ca
--- /dev/null
@@ -0,0 +1,6 @@
+
+@SYNTAX:netsplit@
+
+Irssi keeps track of people who were lost in net splits. With this
+command you can get a list of them.
+
diff --git a/apps/irssi/docs/help/in/nick.in b/apps/irssi/docs/help/in/nick.in
new file mode 100644 (file)
index 0000000..57313b7
--- /dev/null
@@ -0,0 +1,6 @@
+
+@SYNTAX:nick@
+
+Changes your nick. This should be hardly rarely 
+used or needed.
+
diff --git a/apps/irssi/docs/help/in/note.in b/apps/irssi/docs/help/in/note.in
new file mode 100644 (file)
index 0000000..d77bb23
--- /dev/null
@@ -0,0 +1,28 @@
+
+@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. 
+
diff --git a/apps/irssi/docs/help/in/notice.in b/apps/irssi/docs/help/in/notice.in
new file mode 100644 (file)
index 0000000..6b4da9b
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/notify.in b/apps/irssi/docs/help/in/notify.in
new file mode 100644 (file)
index 0000000..5f278e0
--- /dev/null
@@ -0,0 +1,15 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/op.in b/apps/irssi/docs/help/in/op.in
new file mode 100644 (file)
index 0000000..ce46cb6
--- /dev/null
@@ -0,0 +1,8 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/oper.in b/apps/irssi/docs/help/in/oper.in
new file mode 100644 (file)
index 0000000..6cb6518
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/part.in b/apps/irssi/docs/help/in/part.in
new file mode 100644 (file)
index 0000000..ff3b12f
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:part@
+
+Parts from the current or specified channel. Depending
+on your settings, closes the corresponding window, too.
+
+See also: LEAVE, JOIN
+
diff --git a/apps/irssi/docs/help/in/perlflush.in b/apps/irssi/docs/help/in/perlflush.in
new file mode 100644 (file)
index 0000000..27fc509
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:perlflush@
+
+Stops and removes all Perl scripts which have been run.
+Also undefines all the commands defined by Perl scripts.
+
+See also: RUN
+
diff --git a/apps/irssi/docs/help/in/ping.in b/apps/irssi/docs/help/in/ping.in
new file mode 100644 (file)
index 0000000..3518b0c
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/query.in b/apps/irssi/docs/help/in/query.in
new file mode 100644 (file)
index 0000000..818bbe6
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/quit.in b/apps/irssi/docs/help/in/quit.in
new file mode 100644 (file)
index 0000000..f753621
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/quote.in b/apps/irssi/docs/help/in/quote.in
new file mode 100644 (file)
index 0000000..eceb409
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:quote@
+
+Sends server raw data without parsing.
+
+Example:
+   /QUOTE PRIVMSG cras :Hey, this works!
+
diff --git a/apps/irssi/docs/help/in/rawlog.in b/apps/irssi/docs/help/in/rawlog.in
new file mode 100644 (file)
index 0000000..b863e23
--- /dev/null
@@ -0,0 +1,15 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/reconnect.in b/apps/irssi/docs/help/in/reconnect.in
new file mode 100644 (file)
index 0000000..81a7fc4
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/rehash.in b/apps/irssi/docs/help/in/rehash.in
new file mode 100644 (file)
index 0000000..2fb5579
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/reload.in b/apps/irssi/docs/help/in/reload.in
new file mode 100644 (file)
index 0000000..e7b2a5b
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:reload@
+
+Reloads the irssi's configuration file.
+
+See also: SAVE
+
diff --git a/apps/irssi/docs/help/in/restart.in b/apps/irssi/docs/help/in/restart.in
new file mode 100644 (file)
index 0000000..14cd940
--- /dev/null
@@ -0,0 +1,12 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/rmreconns.in b/apps/irssi/docs/help/in/rmreconns.in
new file mode 100644 (file)
index 0000000..e6e4e92
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:rmreconns@
+
+Removes the pending reconnections from the reconnect list.
+
+See also: CONNECT, RECONNECT, SERVER
+
diff --git a/apps/irssi/docs/help/in/rmrejoins.in b/apps/irssi/docs/help/in/rmrejoins.in
new file mode 100644 (file)
index 0000000..a485e93
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/rping.in b/apps/irssi/docs/help/in/rping.in
new file mode 100644 (file)
index 0000000..e062317
--- /dev/null
@@ -0,0 +1,12 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/run.in b/apps/irssi/docs/help/in/run.in
new file mode 100644 (file)
index 0000000..982808c
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:run@
+
+Runs a perl-script. For more information, see the
+perl.txt in the docs-directory of irssi.
+
+See also: PERLFLUSH
diff --git a/apps/irssi/docs/help/in/save.in b/apps/irssi/docs/help/in/save.in
new file mode 100644 (file)
index 0000000..a6d5ea5
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:save@
+
+Saves the current Irssi configuration into the configuration
+file.
+
+See also: RELOAD
+
diff --git a/apps/irssi/docs/help/in/sconnect.in b/apps/irssi/docs/help/in/sconnect.in
new file mode 100644 (file)
index 0000000..0f19d34
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:sconnect@
+
+IRC Operator command. Makes an IRC server to connect
+to another server.
+
+See also: OPER, SQUIT, RESTART
+
diff --git a/apps/irssi/docs/help/in/scrollback.in b/apps/irssi/docs/help/in/scrollback.in
new file mode 100644 (file)
index 0000000..7da9b82
--- /dev/null
@@ -0,0 +1,12 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/server.in b/apps/irssi/docs/help/in/server.in
new file mode 100644 (file)
index 0000000..f75462c
--- /dev/null
@@ -0,0 +1,30 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/servlist.in b/apps/irssi/docs/help/in/servlist.in
new file mode 100644 (file)
index 0000000..cee9612
--- /dev/null
@@ -0,0 +1,19 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/set.in b/apps/irssi/docs/help/in/set.in
new file mode 100644 (file)
index 0000000..e355aab
--- /dev/null
@@ -0,0 +1,23 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/silence.in b/apps/irssi/docs/help/in/silence.in
new file mode 100644 (file)
index 0000000..4e37d7b
--- /dev/null
@@ -0,0 +1,22 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/squery.in b/apps/irssi/docs/help/in/squery.in
new file mode 100644 (file)
index 0000000..a0bb4f1
--- /dev/null
@@ -0,0 +1,10 @@
+
+@SYNTAX:squery@
+
+   <service>   - Service name
+   <commands>  - Commands to pass to the service.
+
+/SQUERY sends a query to the specified service.
+
+See also: SERVLIST
+
diff --git a/apps/irssi/docs/help/in/squit.in b/apps/irssi/docs/help/in/squit.in
new file mode 100644 (file)
index 0000000..64b8616
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:squit@
+
+IRC Operator command. Makes server to quit IRC network.
+
+See also: OPER, DIE, RESTART
+
diff --git a/apps/irssi/docs/help/in/stats.in b/apps/irssi/docs/help/in/stats.in
new file mode 100644 (file)
index 0000000..3afc6eb
--- /dev/null
@@ -0,0 +1,20 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/time.in b/apps/irssi/docs/help/in/time.in
new file mode 100644 (file)
index 0000000..7877fe7
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/toggle.in b/apps/irssi/docs/help/in/toggle.in
new file mode 100644 (file)
index 0000000..1991306
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/topic.in b/apps/irssi/docs/help/in/topic.in
new file mode 100644 (file)
index 0000000..ae2564f
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:topic@
+
+   -delete  - Deletes the topic.
+
+Shows or/and changes the topic of the current or specified 
+channel.
+
diff --git a/apps/irssi/docs/help/in/trace.in b/apps/irssi/docs/help/in/trace.in
new file mode 100644 (file)
index 0000000..27df384
--- /dev/null
@@ -0,0 +1,8 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/ts.in b/apps/irssi/docs/help/in/ts.in
new file mode 100644 (file)
index 0000000..0f51f96
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:ts@
+
+Shows topics of all channels you're on.
+
+See also: CHANNEL, TOPIC
+
diff --git a/apps/irssi/docs/help/in/unalias.in b/apps/irssi/docs/help/in/unalias.in
new file mode 100644 (file)
index 0000000..d3b2e74
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:unalias@
+
+Removes an alias.
+
+See also: ALIAS
+
diff --git a/apps/irssi/docs/help/in/unban.in b/apps/irssi/docs/help/in/unban.in
new file mode 100644 (file)
index 0000000..a22963a
--- /dev/null
@@ -0,0 +1,11 @@
+
+@SYNTAX:unban@
+
+Removes the specified ban(s) from the channel.
+
+Examples:
+   /UNBAN *!*@*.fi
+   /UNBAN larry!*@* *!me@*.mydomain.net
+
+See also: BAN, KNOCKOUT
+
diff --git a/apps/irssi/docs/help/in/unignore.in b/apps/irssi/docs/help/in/unignore.in
new file mode 100644 (file)
index 0000000..6b5c555
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:unignore@
+
+Unignores the specified userhost mask.
+
+See also: IGNORE
+
diff --git a/apps/irssi/docs/help/in/unload.in b/apps/irssi/docs/help/in/unload.in
new file mode 100644 (file)
index 0000000..4bfdeb9
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:unload@
+
+Unload a running plugin. List of running plugins can be shown with
+/LOAD.
+
+See also: LOAD
+
diff --git a/apps/irssi/docs/help/in/unnotify.in b/apps/irssi/docs/help/in/unnotify.in
new file mode 100644 (file)
index 0000000..5d74ce8
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:unnotify@
+
+Removes an entry from the notify list.
+
+See also: NOTIFY
+
diff --git a/apps/irssi/docs/help/in/unquery.in b/apps/irssi/docs/help/in/unquery.in
new file mode 100644 (file)
index 0000000..0d488c6
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:unquery@
+
+Removes a query window of specified nick.
+
+See also: QUERY, SET QUERY
+
diff --git a/apps/irssi/docs/help/in/unsilence.in b/apps/irssi/docs/help/in/unsilence.in
new file mode 100644 (file)
index 0000000..110ff55
--- /dev/null
@@ -0,0 +1,9 @@
+
+@SYNTAX:unsilence@
+
+Works only in the Undernet and Open Projects (ircu).
+
+Removes a pattern from your silence list.
+
+See also: SILENCE
+
diff --git a/apps/irssi/docs/help/in/uping.in b/apps/irssi/docs/help/in/uping.in
new file mode 100644 (file)
index 0000000..0fc003b
--- /dev/null
@@ -0,0 +1,12 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/userhost.in b/apps/irssi/docs/help/in/userhost.in
new file mode 100644 (file)
index 0000000..a1039f9
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:userhost@
+
+Shows the userhost info of the specified nick.
+
+See also: WHOIS
+
diff --git a/apps/irssi/docs/help/in/ver.in b/apps/irssi/docs/help/in/ver.in
new file mode 100644 (file)
index 0000000..975fbf7
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/version.in b/apps/irssi/docs/help/in/version.in
new file mode 100644 (file)
index 0000000..a7dd5c6
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:version@
+
+Shows the version info of the current or specified
+IRC server.
+
+See also: ADMIN, STATS
+
diff --git a/apps/irssi/docs/help/in/voice.in b/apps/irssi/docs/help/in/voice.in
new file mode 100644 (file)
index 0000000..ee6f243
--- /dev/null
@@ -0,0 +1,8 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/wait.in b/apps/irssi/docs/help/in/wait.in
new file mode 100644 (file)
index 0000000..4f5dc2f
--- /dev/null
@@ -0,0 +1,10 @@
+
+@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.
+
diff --git a/apps/irssi/docs/help/in/wall.in b/apps/irssi/docs/help/in/wall.in
new file mode 100644 (file)
index 0000000..212c3a8
--- /dev/null
@@ -0,0 +1,11 @@
+
+@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
diff --git a/apps/irssi/docs/help/in/wallchops.in b/apps/irssi/docs/help/in/wallchops.in
new file mode 100644 (file)
index 0000000..034b1f6
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/wallops.in b/apps/irssi/docs/help/in/wallops.in
new file mode 100644 (file)
index 0000000..1690db2
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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
diff --git a/apps/irssi/docs/help/in/who.in b/apps/irssi/docs/help/in/who.in
new file mode 100644 (file)
index 0000000..014231e
--- /dev/null
@@ -0,0 +1,23 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/whois.in b/apps/irssi/docs/help/in/whois.in
new file mode 100644 (file)
index 0000000..55f3e33
--- /dev/null
@@ -0,0 +1,15 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/whowas.in b/apps/irssi/docs/help/in/whowas.in
new file mode 100644 (file)
index 0000000..2c7c68b
--- /dev/null
@@ -0,0 +1,19 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/window.in b/apps/irssi/docs/help/in/window.in
new file mode 100644 (file)
index 0000000..7af71dc
--- /dev/null
@@ -0,0 +1,40 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/wjoin.in b/apps/irssi/docs/help/in/wjoin.in
new file mode 100644 (file)
index 0000000..8386e17
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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
+
diff --git a/apps/irssi/docs/help/in/wquery.in b/apps/irssi/docs/help/in/wquery.in
new file mode 100644 (file)
index 0000000..ae4799a
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:wquery@
+
+Starts a query in the current window without
+opening a new window.
+
+See also: QUERY, WINDOW, SET AUTOCREATE
+
diff --git a/apps/irssi/docs/manual.txt b/apps/irssi/docs/manual.txt
new file mode 100644 (file)
index 0000000..7c25c7b
--- /dev/null
@@ -0,0 +1,1030 @@
+
+ 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.
diff --git a/apps/irssi/docs/startup-HOWTO.html b/apps/irssi/docs/startup-HOWTO.html
new file mode 100644 (file)
index 0000000..e0af349
--- /dev/null
@@ -0,0 +1,647 @@
+ <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 &amp; 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 &lt;number&gt;          - 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 [&lt;number&gt;|&lt;name&gt;] - Make the split window hidden window
+     /WINDOW SHOW &lt;number&gt;|&lt;name&gt;   - Make the hidden window a split window
+
+     /WINDOW SHRINK [&lt;lines&gt;]       - Shrink the split window
+     /WINDOW GROW [&lt;lines&gt;]         - 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 &lt;number&gt;|&lt;name&gt; - 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 &amp; 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/&lt;servertag&gt;/&lt;target&gt;.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 &quot;&lt;nick&gt; text&quot;
+  and other channels as &quot;&lt;nick:channel&gt; text&quot;. 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
+  &lt;@nick&gt;, voices &lt;+nick&gt; and others &lt; nick&gt;</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>
diff --git a/apps/irssi/file2header.sh b/apps/irssi/file2header.sh
new file mode 100755 (executable)
index 0000000..f25ae13
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+echo "const char *$2 ="
+cat $1|sed 's/\\/\\\\/g'|sed 's/"/\\"/g'|sed 's/^/\"/'|sed 's/$/\\n\"/'
+echo ";"
diff --git a/apps/irssi/findsyntax.pl b/apps/irssi/findsyntax.pl
new file mode 100755 (executable)
index 0000000..e24d50f
--- /dev/null
@@ -0,0 +1,18 @@
+#!/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;
+       }
+}
diff --git a/apps/irssi/irssi-config.in b/apps/irssi/irssi-config.in
new file mode 100644 (file)
index 0000000..0a41042
--- /dev/null
@@ -0,0 +1,6 @@
+PROG_LIBS="@PROG_LIBS@"
+PERL_LDFLAGS="@PERL_LDFLAGS@"
+COMMON_LIBS="@COMMON_LIBS@"
+
+CHAT_MODULES="@CHAT_MODULES@"
+silc_MODULES="@silc_MODULES@"
diff --git a/apps/irssi/irssi-version.h.in b/apps/irssi/irssi-version.h.in
new file mode 100644 (file)
index 0000000..668993f
--- /dev/null
@@ -0,0 +1,3 @@
+/* automatically created by autogen.sh */
+#define IRSSI_VERSION "@VERSION@"
+#define IRSSI_VERSION_DATE "20010524"
diff --git a/apps/irssi/irssi.spec.in b/apps/irssi/irssi.spec.in
new file mode 100644 (file)
index 0000000..a3e9b9c
--- /dev/null
@@ -0,0 +1,216 @@
+# $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>)
diff --git a/apps/irssi/src/Makefile.am b/apps/irssi/src/Makefile.am
new file mode 100644 (file)
index 0000000..d8fc189
--- /dev/null
@@ -0,0 +1,8 @@
+if BUILD_TEXTUI
+TEXTUI=fe-text
+endif
+
+noinst_HEADERS = \
+       common.h
+
+SUBDIRS = lib-popt lib-config core silc fe-common $(TEXTUI)
diff --git a/apps/irssi/src/common.h b/apps/irssi/src/common.h
new file mode 100644 (file)
index 0000000..c79c5d9
--- /dev/null
@@ -0,0 +1,89 @@
+#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
diff --git a/apps/irssi/src/fe-common/Makefile.am b/apps/irssi/src/fe-common/Makefile.am
new file mode 100644 (file)
index 0000000..de95911
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = core silc
diff --git a/apps/irssi/src/fe-common/silc/Makefile.am b/apps/irssi/src/fe-common/silc/Makefile.am
new file mode 100644 (file)
index 0000000..74efdc4
--- /dev/null
@@ -0,0 +1,33 @@
+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
diff --git a/apps/irssi/src/fe-common/silc/fe-channels.c b/apps/irssi/src/fe-common/silc/fe-channels.c
new file mode 100644 (file)
index 0000000..47696cf
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ 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)
+{
+}
diff --git a/apps/irssi/src/fe-common/silc/fe-common-silc.c b/apps/irssi/src/fe-common/silc/fe-common-silc.c
new file mode 100644 (file)
index 0000000..c779cae
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ 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)
+{
+}
diff --git a/apps/irssi/src/fe-common/silc/fe-common-silc.h b/apps/irssi/src/fe-common/silc/fe-common-silc.h
new file mode 100644 (file)
index 0000000..4a9a648
--- /dev/null
@@ -0,0 +1,8 @@
+#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
diff --git a/apps/irssi/src/fe-common/silc/module-formats.c b/apps/irssi/src/fe-common/silc/module-formats.c
new file mode 100644 (file)
index 0000000..2b30bff
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ 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 }
+};
diff --git a/apps/irssi/src/fe-common/silc/module-formats.h b/apps/irssi/src/fe-common/silc/module-formats.h
new file mode 100644 (file)
index 0000000..aa4c87c
--- /dev/null
@@ -0,0 +1,181 @@
+#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[];
diff --git a/apps/irssi/src/fe-common/silc/module.h b/apps/irssi/src/fe-common/silc/module.h
new file mode 100644 (file)
index 0000000..f6f6791
--- /dev/null
@@ -0,0 +1,9 @@
+#include "common.h"
+
+#define MODULE_NAME "fe-common/silc"
+
+#undef PACKAGE
+#undef VERSION
+#include "silcincludes.h"
+#include "clientlibincludes.h"
+#include "silc-core.h"
diff --git a/apps/irssi/src/fe-common/silc/silc-modules.c b/apps/irssi/src/fe-common/silc/silc-modules.c
new file mode 100644 (file)
index 0000000..16081e1
--- /dev/null
@@ -0,0 +1,3 @@
+/* this file is automatically generated by configure - don't change */
+void fe_silc_modules_init(void) {  }
+void fe_silc_modules_deinit(void) {  }
diff --git a/apps/irssi/src/fe-text/Makefile.am b/apps/irssi/src/fe-text/Makefile.am
new file mode 100644 (file)
index 0000000..ef0f561
--- /dev/null
@@ -0,0 +1,53 @@
+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
diff --git a/apps/irssi/src/fe-text/gui-entry.c b/apps/irssi/src/fe-text/gui-entry.c
new file mode 100644 (file)
index 0000000..7544539
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/gui-entry.h b/apps/irssi/src/fe-text/gui-entry.h
new file mode 100644 (file)
index 0000000..ff8f3ef
--- /dev/null
@@ -0,0 +1,31 @@
+#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
diff --git a/apps/irssi/src/fe-text/gui-expandos.c b/apps/irssi/src/fe-text/gui-expandos.c
new file mode 100644 (file)
index 0000000..c95ac62
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/gui-printtext.c b/apps/irssi/src/fe-text/gui-printtext.c
new file mode 100644 (file)
index 0000000..5faae0f
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/gui-printtext.h b/apps/irssi/src/fe-text/gui-printtext.h
new file mode 100644 (file)
index 0000000..3b2098b
--- /dev/null
@@ -0,0 +1,13 @@
+#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
diff --git a/apps/irssi/src/fe-text/gui-readline.c b/apps/irssi/src/fe-text/gui-readline.c
new file mode 100644 (file)
index 0000000..bc3429d
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/gui-readline.h b/apps/irssi/src/fe-text/gui-readline.h
new file mode 100644 (file)
index 0000000..6464921
--- /dev/null
@@ -0,0 +1,12 @@
+#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
diff --git a/apps/irssi/src/fe-text/gui-windows.c b/apps/irssi/src/fe-text/gui-windows.c
new file mode 100644 (file)
index 0000000..9a199cd
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/gui-windows.h b/apps/irssi/src/fe-text/gui-windows.h
new file mode 100644 (file)
index 0000000..fb94b46
--- /dev/null
@@ -0,0 +1,36 @@
+#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
diff --git a/apps/irssi/src/fe-text/lastlog.c b/apps/irssi/src/fe-text/lastlog.c
new file mode 100644 (file)
index 0000000..75e68da
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/mainwindow-activity.c b/apps/irssi/src/fe-text/mainwindow-activity.c
new file mode 100644 (file)
index 0000000..4301d98
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/mainwindows-save.c b/apps/irssi/src/fe-text/mainwindows-save.c
new file mode 100644 (file)
index 0000000..113037b
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/mainwindows.c b/apps/irssi/src/fe-text/mainwindows.c
new file mode 100644 (file)
index 0000000..d727464
--- /dev/null
@@ -0,0 +1,909 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/mainwindows.h b/apps/irssi/src/fe-text/mainwindows.h
new file mode 100644 (file)
index 0000000..491449c
--- /dev/null
@@ -0,0 +1,42 @@
+#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
diff --git a/apps/irssi/src/fe-text/module-formats.c b/apps/irssi/src/fe-text/module-formats.c
new file mode 100644 (file)
index 0000000..be3c0ea
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ 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 }
+};
diff --git a/apps/irssi/src/fe-text/module-formats.h b/apps/irssi/src/fe-text/module-formats.h
new file mode 100644 (file)
index 0000000..fe33c8e
--- /dev/null
@@ -0,0 +1,20 @@
+#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[];
diff --git a/apps/irssi/src/fe-text/module.h b/apps/irssi/src/fe-text/module.h
new file mode 100644 (file)
index 0000000..ba5888a
--- /dev/null
@@ -0,0 +1,6 @@
+#include "common.h"
+
+#define MODULE_NAME "fe-text"
+
+extern int quitting;
+void irssi_redraw(void);
diff --git a/apps/irssi/src/fe-text/screen.c b/apps/irssi/src/fe-text/screen.c
new file mode 100644 (file)
index 0000000..5378b5c
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ 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();
+       }
+}
diff --git a/apps/irssi/src/fe-text/screen.h b/apps/irssi/src/fe-text/screen.h
new file mode 100644 (file)
index 0000000..c1449bf
--- /dev/null
@@ -0,0 +1,46 @@
+#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
diff --git a/apps/irssi/src/fe-text/silc.c b/apps/irssi/src/fe-text/silc.c
new file mode 100644 (file)
index 0000000..9112b56
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ 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;
+}
diff --git a/apps/irssi/src/fe-text/statusbar-items.c b/apps/irssi/src/fe-text/statusbar-items.c
new file mode 100644 (file)
index 0000000..137e741
--- /dev/null
@@ -0,0 +1,765 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/statusbar.c b/apps/irssi/src/fe-text/statusbar.c
new file mode 100644 (file)
index 0000000..8e99c4f
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/statusbar.h b/apps/irssi/src/fe-text/statusbar.h
new file mode 100644 (file)
index 0000000..32d35b8
--- /dev/null
@@ -0,0 +1,63 @@
+#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
diff --git a/apps/irssi/src/fe-text/textbuffer-commands.c b/apps/irssi/src/fe-text/textbuffer-commands.c
new file mode 100644 (file)
index 0000000..92ec70b
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/textbuffer-view.c b/apps/irssi/src/fe-text/textbuffer-view.c
new file mode 100644 (file)
index 0000000..f3a7408
--- /dev/null
@@ -0,0 +1,1118 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/textbuffer-view.h b/apps/irssi/src/fe-text/textbuffer-view.h
new file mode 100644 (file)
index 0000000..21ed28c
--- /dev/null
@@ -0,0 +1,131 @@
+#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
diff --git a/apps/irssi/src/fe-text/textbuffer.c b/apps/irssi/src/fe-text/textbuffer.c
new file mode 100644 (file)
index 0000000..f97f46c
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/fe-text/textbuffer.h b/apps/irssi/src/fe-text/textbuffer.h
new file mode 100644 (file)
index 0000000..21f70e2
--- /dev/null
@@ -0,0 +1,90 @@
+#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
diff --git a/apps/irssi/src/lib-config/Makefile.am b/apps/irssi/src/lib-config/Makefile.am
new file mode 100644 (file)
index 0000000..3907ac3
--- /dev/null
@@ -0,0 +1,15 @@
+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
diff --git a/apps/irssi/src/lib-config/get.c b/apps/irssi/src/lib-config/get.c
new file mode 100644 (file)
index 0000000..98e2a30
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ 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;
+}
diff --git a/apps/irssi/src/lib-config/iconfig.h b/apps/irssi/src/lib-config/iconfig.h
new file mode 100644 (file)
index 0000000..ea2e6c3
--- /dev/null
@@ -0,0 +1,144 @@
+#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
diff --git a/apps/irssi/src/lib-config/module.h b/apps/irssi/src/lib-config/module.h
new file mode 100644 (file)
index 0000000..22e0e7c
--- /dev/null
@@ -0,0 +1,6 @@
+#include "common.h"
+#include "iconfig.h"
+
+/* private */
+int config_error(CONFIG_REC *rec, const char *msg);
+
diff --git a/apps/irssi/src/lib-config/parse.c b/apps/irssi/src/lib-config/parse.c
new file mode 100644 (file)
index 0000000..883a920
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ 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;
+}
diff --git a/apps/irssi/src/lib-config/set.c b/apps/irssi/src/lib-config/set.c
new file mode 100644 (file)
index 0000000..fdb358f
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ 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);
+}
diff --git a/apps/irssi/src/lib-config/write.c b/apps/irssi/src/lib-config/write.c
new file mode 100644 (file)
index 0000000..446735c
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ 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;
+}
diff --git a/apps/irssi/src/lib-popt/.cvsignore b/apps/irssi/src/lib-popt/.cvsignore
new file mode 100644 (file)
index 0000000..577aa17
--- /dev/null
@@ -0,0 +1,7 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
diff --git a/apps/irssi/src/lib-popt/Makefile.am b/apps/irssi/src/lib-popt/Makefile.am
new file mode 100644 (file)
index 0000000..928e5c0
--- /dev/null
@@ -0,0 +1,11 @@
+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
diff --git a/apps/irssi/src/lib-popt/findme.c b/apps/irssi/src/lib-popt/findme.c
new file mode 100644 (file)
index 0000000..fae8f03
--- /dev/null
@@ -0,0 +1,54 @@
+/* (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;
+}
diff --git a/apps/irssi/src/lib-popt/findme.h b/apps/irssi/src/lib-popt/findme.h
new file mode 100644 (file)
index 0000000..fdd01d5
--- /dev/null
@@ -0,0 +1,10 @@
+/* (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
diff --git a/apps/irssi/src/lib-popt/popt.c b/apps/irssi/src/lib-popt/popt.c
new file mode 100644 (file)
index 0000000..c09a0b3
--- /dev/null
@@ -0,0 +1,593 @@
+/* (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];
+}
diff --git a/apps/irssi/src/lib-popt/popt.h b/apps/irssi/src/lib-popt/popt.h
new file mode 100644 (file)
index 0000000..2fea9e8
--- /dev/null
@@ -0,0 +1,123 @@
+/* (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
diff --git a/apps/irssi/src/lib-popt/poptconfig.c b/apps/irssi/src/lib-popt/poptconfig.c
new file mode 100644 (file)
index 0000000..d064297
--- /dev/null
@@ -0,0 +1,144 @@
+/* (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;
+}
+
diff --git a/apps/irssi/src/lib-popt/popthelp.c b/apps/irssi/src/lib-popt/popthelp.c
new file mode 100644 (file)
index 0000000..c1876d7
--- /dev/null
@@ -0,0 +1,301 @@
+/* -*- 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);
+}
diff --git a/apps/irssi/src/lib-popt/poptint.h b/apps/irssi/src/lib-popt/poptint.h
new file mode 100644 (file)
index 0000000..a134671
--- /dev/null
@@ -0,0 +1,54 @@
+/* (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
diff --git a/apps/irssi/src/lib-popt/poptparse.c b/apps/irssi/src/lib-popt/poptparse.c
new file mode 100644 (file)
index 0000000..eb2a672
--- /dev/null
@@ -0,0 +1,98 @@
+/* (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;
+}
diff --git a/apps/irssi/src/silc/Makefile.am b/apps/irssi/src/silc/Makefile.am
new file mode 100644 (file)
index 0000000..05b9a00
--- /dev/null
@@ -0,0 +1,5 @@
+SUBDIRS = core
+
+noinst_LIBRARIES = libsilc.a
+
+libsilc_a_SOURCES = silc.c
diff --git a/apps/irssi/src/silc/silc.c b/apps/irssi/src/silc/silc.c
new file mode 100644 (file)
index 0000000..de2bde0
--- /dev/null
@@ -0,0 +1,4 @@
+/* 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(); }
diff --git a/apps/irssi/stamp.h.in b/apps/irssi/stamp.h.in
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/irssi/syntax.pl b/apps/irssi/syntax.pl
new file mode 100755 (executable)
index 0000000..f0ec779
--- /dev/null
@@ -0,0 +1,57 @@
+#!/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";